mirror of
https://github.com/grocy/grocy.git
synced 2026-04-13 07:46:15 +02:00
Implement async loading of products, add TODOs
This commit is contained in:
parent
7532626123
commit
94b6bf8eef
|
|
@ -6,7 +6,7 @@ use LessQL\Result;
|
|||
|
||||
class BaseApiController extends BaseController
|
||||
{
|
||||
const PATTERN_FIELD = '[A-Za-z_][A-Za-z0-9_]+';
|
||||
const PATTERN_FIELD = '[A-Za-z_][A-Za-z0-9_\.]+';
|
||||
|
||||
const PATTERN_OPERATOR = '!?(=|~|<|>|(>=)|(<=)|(§))';
|
||||
|
||||
|
|
@ -16,8 +16,7 @@ class BaseApiController extends BaseController
|
|||
|
||||
protected function ApiResponse(\Psr\Http\Message\ResponseInterface $response, $data, $cache = false)
|
||||
{
|
||||
if ($cache)
|
||||
{
|
||||
if ($cache) {
|
||||
$response = $response->withHeader('Cache-Control', 'max-age=2592000');
|
||||
}
|
||||
|
||||
|
|
@ -39,38 +38,93 @@ class BaseApiController extends BaseController
|
|||
|
||||
public function FilteredApiResponse(\Psr\Http\Message\ResponseInterface $response, Result $data, array $query)
|
||||
{
|
||||
$data = $this->queryData($data, $query);
|
||||
// get total row count
|
||||
$response = $response->withHeader('x-rowcount-total', $data->count());
|
||||
|
||||
// apply filter, get filtered row count
|
||||
$data = $this->applyQuery($data, $query);
|
||||
$response = $response->withHeader('x-rowcount-filtered', $data->count());
|
||||
|
||||
// apply limit/order
|
||||
$data = $this->applyOrder($data, $query);
|
||||
$data = $this->applyLimit($data, $query);
|
||||
return $this->ApiResponse($response, $data);
|
||||
}
|
||||
|
||||
protected function queryData(Result $data, array $query)
|
||||
{
|
||||
if (isset($query['query']))
|
||||
protected function applyQuery(Result $data, array $query): Result
|
||||
{
|
||||
if (isset($query['query'])) {
|
||||
$data = $this->filter($data, $query['query']);
|
||||
}
|
||||
|
||||
if (isset($query['limit']))
|
||||
if (isset($query['search'])) {
|
||||
// get list of fields from table
|
||||
$stmt = $this->getDatabase()->prepare('SELECT `sql` FROM sqlite_master WHERE `name` = ? LIMIT 1');
|
||||
$stmt->execute([$data->getTable()]);
|
||||
$sql = $stmt->fetchColumn();
|
||||
$sql = substr($sql, strpos($sql, '(') + 1);
|
||||
$sql = substr($sql, 0, strrpos($sql, ')'));
|
||||
$sql = trim($sql);
|
||||
while (preg_match('/\(.*?\)/', $sql) === 1) {
|
||||
$sql = preg_replace('/\([^\(\)]*\)/', '', $sql);
|
||||
}
|
||||
$fields = array_map(function ($field) {
|
||||
preg_match('/\s*([^\s]+)/', $field, $matches);
|
||||
return $matches[1];
|
||||
}, explode(',', $sql));
|
||||
|
||||
$join_info = match ($data->getTable()) {
|
||||
'products' => [
|
||||
'product_group_id' => ['field' => 'name', 'table' => 'product_groups'],
|
||||
'location_id' => ['field' => 'name', 'table' => 'locations'],
|
||||
'shopping_location_id' => ['field' => 'name', 'table' => 'shopping_locations'],
|
||||
'qu_id_purchase' => ['field' => 'name', 'table' => 'quantity_units'],
|
||||
'qu_id_stock' => ['field' => 'name', 'table' => 'quantity_units'],
|
||||
'parent_product_id' => ['field' => 'name', 'table' => 'products'],
|
||||
],
|
||||
default => [],
|
||||
};
|
||||
|
||||
// create search query that matches any field
|
||||
$fields_query = implode(' OR ', array_map(function ($field) use ($join_info) {
|
||||
$field_escaped = '`' . str_replace('`', '``', $field) . '`';
|
||||
if (array_key_exists($field, $join_info)) {
|
||||
$table_escaped = '`' . str_replace('`', '``', $join_info[$field]['table']) . '`';
|
||||
return $field_escaped . ' IN(SELECT id FROM ' . $table_escaped . ' WHERE `' . str_replace('`', '``', $join_info[$field]['field']) . '` LIKE ? ESCAPE \'\\\')';
|
||||
}
|
||||
return $field_escaped . ' LIKE ? ESCAPE \'\\\'';
|
||||
}, $fields));
|
||||
|
||||
// apply search query
|
||||
$data = $data->where($fields_query, array_fill(0, count($fields), '%' . str_replace(['\\', '%', '_', '*'], ['\\\\', '\\%', '\\_', '%'], $query['search']) . '%'));
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function applyLimit(Result $data, array $query): Result
|
||||
{
|
||||
if (isset($query['limit'])) {
|
||||
$data = $data->limit(intval($query['limit']), intval($query['offset'] ?? 0));
|
||||
}
|
||||
|
||||
if (isset($query['order']))
|
||||
{
|
||||
$parts = explode(':', $query['order']);
|
||||
|
||||
if (count($parts) == 1)
|
||||
{
|
||||
$data = $data->orderBy($parts[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($parts[1] != 'asc' && $parts[1] != 'desc')
|
||||
{
|
||||
throw new \Exception('Invalid sort order ' . $parts[1]);
|
||||
return $data;
|
||||
}
|
||||
|
||||
$data = $data->orderBy($parts[0], $parts[1]);
|
||||
protected function applyOrder(Result $data, array $query): Result
|
||||
{
|
||||
if (isset($query['order'])) {
|
||||
$parts = explode(',', $query['order']);
|
||||
foreach ($parts as $part) {
|
||||
$col_dir = explode(':', $part, 2);
|
||||
if (count($col_dir) == 1) {
|
||||
$data = $data->orderBy($col_dir[0]);
|
||||
} else {
|
||||
if ($col_dir[1] != 'asc' && $col_dir[1] != 'desc' && $col_dir[1] != 'collate nocase') {
|
||||
throw new \Exception('Invalid sort order ' . $col_dir[1]);
|
||||
}
|
||||
$data = $data->orderBy($col_dir[0], $col_dir[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -79,8 +133,7 @@ class BaseApiController extends BaseController
|
|||
|
||||
protected function filter(Result $data, array $query): Result
|
||||
{
|
||||
foreach ($query as $q)
|
||||
{
|
||||
foreach ($query as $q) {
|
||||
$matches = [];
|
||||
preg_match(
|
||||
'/(?P<field>' . self::PATTERN_FIELD . ')'
|
||||
|
|
@ -90,47 +143,63 @@ class BaseApiController extends BaseController
|
|||
$matches
|
||||
);
|
||||
|
||||
if (!array_key_exists('field', $matches) || !array_key_exists('op', $matches) || !array_key_exists('value', $matches))
|
||||
{
|
||||
if (!array_key_exists('field', $matches) || !array_key_exists('op', $matches) || !array_key_exists('value', $matches) || !in_array($matches['op'], ['=', '!=', '~', '!~', '<', '>', '>=', '<=', '§'], true)) {
|
||||
throw new \Exception('Invalid query');
|
||||
}
|
||||
list('field' => $field, 'op' => $op, 'value' => $value) = $matches;
|
||||
|
||||
$sqlOrNull = '';
|
||||
if (strtolower($matches['value']) == 'null')
|
||||
{
|
||||
$sqlOrNull = ' OR ' . $matches['field'] . ' IS NULL';
|
||||
$params = match ($op) {
|
||||
'=' => [$value],
|
||||
'!=' => [$value],
|
||||
'~' => ['%' . $value . '%'],
|
||||
'!~' => ['%' . $value . '%'],
|
||||
'<' => [$value],
|
||||
'>' => [$value],
|
||||
'>=' => [$value],
|
||||
'<=' => [$value],
|
||||
'§' => [$value],
|
||||
default => [],
|
||||
};
|
||||
|
||||
$where_prefix = '';
|
||||
$where_suffix = '';
|
||||
if (strpos($field, '.') !== false) {
|
||||
list($join, $field) = explode('.', $field, 2);
|
||||
$join_info = match ($data->getTable()) {
|
||||
'products' => [
|
||||
'product_group' => ['id_field' => 'product_group_id', 'table' => 'product_groups'],
|
||||
'location' => ['id_field' => 'location_id', 'table' => 'locations'],
|
||||
'shopping_location' => ['id_field' => 'shopping_location_id', 'table' => 'shopping_locations'],
|
||||
'qu_purchase' => ['id_field' => 'qu_id_purchase', 'table' => 'quantity_units'],
|
||||
'qu_stock' => ['id_field' => 'qu_id_stock', 'table' => 'quantity_units'],
|
||||
'parent_product' => ['id_field' => 'parent_product_id', 'table' => 'products'],
|
||||
],
|
||||
default => [],
|
||||
};
|
||||
if (!array_key_exists($join, $join_info)) {
|
||||
throw new \Exception('Invalid query');
|
||||
}
|
||||
$field_escaped = '`' . str_replace('`', '``', $join_info[$join]['id_field']) . '`';
|
||||
$table_escaped = '`' . str_replace('`', '``', $join_info[$join]['table']) . '`';
|
||||
$where_prefix = $field_escaped . ' IN(SELECT id FROM ' . $table_escaped . ' WHERE ';
|
||||
$where_suffix = ')';
|
||||
}
|
||||
|
||||
switch ($matches['op']) {
|
||||
case '=':
|
||||
$data = $data->where($matches['field'] . ' = ?' . $sqlOrNull, $matches['value']);
|
||||
break;
|
||||
case '!=':
|
||||
$data = $data->where($matches['field'] . ' != ?' . $sqlOrNull, $matches['value']);
|
||||
break;
|
||||
case '~':
|
||||
$data = $data->where($matches['field'] . ' LIKE ?', '%' . $matches['value'] . '%');
|
||||
break;
|
||||
case '!~':
|
||||
$data = $data->where($matches['field'] . ' NOT LIKE ?', '%' . $matches['value'] . '%');
|
||||
break;
|
||||
case '<':
|
||||
$data = $data->where($matches['field'] . ' < ?', $matches['value']);
|
||||
break;
|
||||
case '>':
|
||||
$data = $data->where($matches['field'] . ' > ?', $matches['value']);
|
||||
break;
|
||||
case '>=':
|
||||
$data = $data->where($matches['field'] . ' >= ?', $matches['value']);
|
||||
break;
|
||||
case '<=':
|
||||
$data = $data->where($matches['field'] . ' <= ?', $matches['value']);
|
||||
break;
|
||||
case '§':
|
||||
$data = $data->where($matches['field'] . ' REGEXP ?', $matches['value']);
|
||||
break;
|
||||
$field_escaped = '`' . str_replace('`', '``', $field) . '`';
|
||||
$where = match ($op) {
|
||||
'=' => $field_escaped . ' = ?' . (strtolower($value) === 'null' ? ' OR ' . $field_escaped . ' IS NULL' : ''),
|
||||
'!=' => $field_escaped . ' != ?' . (strtolower($value) === 'null' ? ' OR ' . $field_escaped . ' IS NULL' : ''),
|
||||
'~' => $field_escaped . ' LIKE ?',
|
||||
'!~' => $field_escaped . ' NOT LIKE ?',
|
||||
'<' => $field_escaped . ' < ?',
|
||||
'>' => $field_escaped . ' > ?',
|
||||
'>=' => $field_escaped . ' >= ?',
|
||||
'<=' => $field_escaped . ' <= ?',
|
||||
'§' => $field_escaped . ' REGEXP ?',
|
||||
default => '',
|
||||
};
|
||||
|
||||
}
|
||||
$data = $data->where($where_prefix . $where . $where_suffix, $params);
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
|
@ -138,8 +207,7 @@ class BaseApiController extends BaseController
|
|||
|
||||
protected function getOpenApispec()
|
||||
{
|
||||
if ($this->OpenApiSpec == null)
|
||||
{
|
||||
if ($this->OpenApiSpec == null) {
|
||||
$this->OpenApiSpec = json_decode(file_get_contents(__DIR__ . '/../grocy.openapi.json'));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,19 +13,15 @@ class ChoresController extends BaseController
|
|||
$usersService = $this->getUsersService();
|
||||
$users = $usersService->GetUsersAsDto();
|
||||
|
||||
if ($args['choreId'] == 'new')
|
||||
{
|
||||
if ($args['choreId'] == 'new') {
|
||||
return $this->renderPage($response, 'choreform', [
|
||||
'periodTypes' => GetClassConstants('\Grocy\Services\ChoresService', 'CHORE_PERIOD_TYPE_'),
|
||||
'mode' => 'create',
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('chores'),
|
||||
'assignmentTypes' => GetClassConstants('\Grocy\Services\ChoresService', 'CHORE_ASSIGNMENT_TYPE_'),
|
||||
'users' => $users,
|
||||
'products' => $this->getDatabase()->products()->orderBy('name', 'COLLATE NOCASE')
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->renderPage($response, 'choreform', [
|
||||
'chore' => $this->getDatabase()->chores($args['choreId']),
|
||||
'periodTypes' => GetClassConstants('\Grocy\Services\ChoresService', 'CHORE_PERIOD_TYPE_'),
|
||||
|
|
@ -33,19 +29,15 @@ class ChoresController extends BaseController
|
|||
'userfields' => $this->getUserfieldsService()->GetFields('chores'),
|
||||
'assignmentTypes' => GetClassConstants('\Grocy\Services\ChoresService', 'CHORE_ASSIGNMENT_TYPE_'),
|
||||
'users' => $users,
|
||||
'products' => $this->getDatabase()->products()->orderBy('name', 'COLLATE NOCASE')
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function ChoresList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if (isset($request->getQueryParams()['include_disabled']))
|
||||
{
|
||||
if (isset($request->getQueryParams()['include_disabled'])) {
|
||||
$chores = $this->getDatabase()->chores()->orderBy('name', 'COLLATE NOCASE');
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$chores = $this->getDatabase()->chores()->where('active = 1')->orderBy('name', 'COLLATE NOCASE');
|
||||
}
|
||||
|
||||
|
|
@ -63,19 +55,15 @@ class ChoresController extends BaseController
|
|||
|
||||
public function Journal(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if (isset($request->getQueryParams()['months']) && filter_var($request->getQueryParams()['months'], FILTER_VALIDATE_INT) !== false)
|
||||
{
|
||||
if (isset($request->getQueryParams()['months']) && filter_var($request->getQueryParams()['months'], FILTER_VALIDATE_INT) !== false) {
|
||||
$months = $request->getQueryParams()['months'];
|
||||
$where = "tracked_time > DATE(DATE('now', 'localtime'), '-$months months')";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Default 1 year
|
||||
$where = "tracked_time > DATE(DATE('now', 'localtime'), '-12 months')";
|
||||
}
|
||||
|
||||
if (isset($request->getQueryParams()['chore']) && filter_var($request->getQueryParams()['chore'], FILTER_VALIDATE_INT) !== false)
|
||||
{
|
||||
if (isset($request->getQueryParams()['chore']) && filter_var($request->getQueryParams()['chore'], FILTER_VALIDATE_INT) !== false) {
|
||||
$choreId = $request->getQueryParams()['chore'];
|
||||
$where .= " AND chore_id = $choreId";
|
||||
}
|
||||
|
|
@ -96,20 +84,13 @@ class ChoresController extends BaseController
|
|||
|
||||
$chores = $this->getDatabase()->chores()->orderBy('name', 'COLLATE NOCASE');
|
||||
$currentChores = $this->getChoresService()->GetCurrent();
|
||||
foreach ($currentChores as $currentChore)
|
||||
{
|
||||
if (FindObjectInArrayByPropertyValue($chores, 'id', $currentChore->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY)
|
||||
{
|
||||
if ($currentChore->next_estimated_execution_time < date('Y-m-d H:i:s'))
|
||||
{
|
||||
foreach ($currentChores as $currentChore) {
|
||||
if (FindObjectInArrayByPropertyValue($chores, 'id', $currentChore->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY) {
|
||||
if ($currentChore->next_estimated_execution_time < date('Y-m-d H:i:s')) {
|
||||
$currentChore->due_type = 'overdue';
|
||||
}
|
||||
elseif ($currentChore->next_estimated_execution_time <= date('Y-m-d 23:59:59'))
|
||||
{
|
||||
} elseif ($currentChore->next_estimated_execution_time <= date('Y-m-d 23:59:59')) {
|
||||
$currentChore->due_type = 'duetoday';
|
||||
}
|
||||
elseif ($nextXDays > 0 && $currentChore->next_estimated_execution_time <= date('Y-m-d H:i:s', strtotime('+' . $nextXDays . ' days')))
|
||||
{
|
||||
} elseif ($nextXDays > 0 && $currentChore->next_estimated_execution_time <= date('Y-m-d H:i:s', strtotime('+' . $nextXDays . ' days'))) {
|
||||
$currentChore->due_type = 'duesoon';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
namespace Grocy\Controllers;
|
||||
|
||||
use Grocy\Controllers\Users\User;
|
||||
use Slim\Exception\HttpBadRequestException;
|
||||
use LessQL\Row;
|
||||
|
||||
class GenericEntityApiController extends BaseApiController
|
||||
{
|
||||
|
|
@ -11,37 +11,28 @@ class GenericEntityApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_MASTER_DATA_EDIT);
|
||||
|
||||
if ($this->IsValidExposedEntity($args['entity']) && !$this->IsEntityWithNoEdit($args['entity']))
|
||||
{
|
||||
if ($this->IsEntityWithEditRequiresAdmin($args['entity']))
|
||||
{
|
||||
if ($this->IsValidExposedEntity($args['entity']) && !$this->IsEntityWithNoEdit($args['entity'])) {
|
||||
if ($this->IsEntityWithEditRequiresAdmin($args['entity'])) {
|
||||
User::checkPermission($request, User::PERMISSION_ADMIN);
|
||||
}
|
||||
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
if ($requestBody === null)
|
||||
{
|
||||
try {
|
||||
if ($requestBody === null) {
|
||||
throw new \Exception('Request body could not be parsed (probably invalid JSON format or missing/wrong Content-Type header)');
|
||||
}
|
||||
|
||||
$newRow = $this->getDatabase()->{$args['entity']}()->createRow($requestBody);
|
||||
$newRow->save();
|
||||
$success = $newRow->isClean();
|
||||
|
||||
return $this->ApiResponse($response, [
|
||||
'created_object_id' => $this->getDatabase()->lastInsertId()
|
||||
]);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
} catch (\Exception $ex) {
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->GenericErrorResponse($response, 'Entity does not exist or is not exposed');
|
||||
}
|
||||
}
|
||||
|
|
@ -50,26 +41,20 @@ class GenericEntityApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_MASTER_DATA_EDIT);
|
||||
|
||||
if ($this->IsValidExposedEntity($args['entity']) && !$this->IsEntityWithNoDelete($args['entity']))
|
||||
{
|
||||
if ($this->IsEntityWithEditRequiresAdmin($args['entity']))
|
||||
{
|
||||
if ($this->IsValidExposedEntity($args['entity']) && !$this->IsEntityWithNoDelete($args['entity'])) {
|
||||
if ($this->IsEntityWithEditRequiresAdmin($args['entity'])) {
|
||||
User::checkPermission($request, User::PERMISSION_ADMIN);
|
||||
}
|
||||
|
||||
$row = $this->getDatabase()->{$args['entity']}($args['objectId']);
|
||||
if ($row == null)
|
||||
{
|
||||
if ($row == null) {
|
||||
return $this->GenericErrorResponse($response, 'Object not found', 400);
|
||||
}
|
||||
|
||||
$row->delete();
|
||||
$success = $row->isClean();
|
||||
|
||||
return $this->EmptyApiResponse($response);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->GenericErrorResponse($response, 'Invalid entity');
|
||||
}
|
||||
}
|
||||
|
|
@ -78,102 +63,78 @@ class GenericEntityApiController extends BaseApiController
|
|||
{
|
||||
User::checkPermission($request, User::PERMISSION_MASTER_DATA_EDIT);
|
||||
|
||||
if ($this->IsValidExposedEntity($args['entity']) && !$this->IsEntityWithNoEdit($args['entity']))
|
||||
{
|
||||
if ($this->IsEntityWithEditRequiresAdmin($args['entity']))
|
||||
{
|
||||
if ($this->IsValidExposedEntity($args['entity']) && !$this->IsEntityWithNoEdit($args['entity'])) {
|
||||
if ($this->IsEntityWithEditRequiresAdmin($args['entity'])) {
|
||||
User::checkPermission($request, User::PERMISSION_ADMIN);
|
||||
}
|
||||
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
if ($requestBody === null)
|
||||
{
|
||||
try {
|
||||
if ($requestBody === null) {
|
||||
throw new \Exception('Request body could not be parsed (probably invalid JSON format or missing/wrong Content-Type header)');
|
||||
}
|
||||
|
||||
$row = $this->getDatabase()->{$args['entity']}($args['objectId']);
|
||||
if ($row == null)
|
||||
{
|
||||
if ($row == null) {
|
||||
return $this->GenericErrorResponse($response, 'Object not found', 400);
|
||||
}
|
||||
|
||||
$row->update($requestBody);
|
||||
$success = $row->isClean();
|
||||
|
||||
return $this->EmptyApiResponse($response);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
} catch (\Exception $ex) {
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->GenericErrorResponse($response, 'Entity does not exist or is not exposed');
|
||||
}
|
||||
}
|
||||
|
||||
public function GetObject(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if ($this->IsValidExposedEntity($args['entity']) && !$this->IsEntityWithNoListing($args['entity']))
|
||||
{
|
||||
$userfields = $this->getUserfieldsService()->GetValues($args['entity'], $args['objectId']);
|
||||
if (count($userfields) === 0)
|
||||
{
|
||||
$userfields = null;
|
||||
}
|
||||
|
||||
if ($this->IsValidExposedEntity($args['entity']) && !$this->IsEntityWithNoListing($args['entity'])) {
|
||||
$object = $this->getDatabase()->{$args['entity']}($args['objectId']);
|
||||
if ($object == null)
|
||||
{
|
||||
if ($object == null) {
|
||||
return $this->GenericErrorResponse($response, 'Object not found', 404);
|
||||
}
|
||||
|
||||
$object['userfields'] = $userfields;
|
||||
$this->addUserfieldsAndJoinsToRow($object, $args);
|
||||
|
||||
return $this->ApiResponse($response, $object);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->GenericErrorResponse($response, 'Entity does not exist or is not exposed');
|
||||
}
|
||||
}
|
||||
|
||||
public function GetObjects(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if (!$this->IsValidExposedEntity($args['entity']) || $this->IsEntityWithNoListing($args['entity']))
|
||||
{
|
||||
if (!$this->IsValidExposedEntity($args['entity']) || $this->IsEntityWithNoListing($args['entity'])) {
|
||||
return $this->GenericErrorResponse($response, 'Entity does not exist or is not exposed');
|
||||
}
|
||||
|
||||
$objects = $this->queryData($this->getDatabase()->{$args['entity']}(), $request->getQueryParams());
|
||||
$userfields = $this->getUserfieldsService()->GetFields($args['entity']);
|
||||
$query = $request->getQueryParams();
|
||||
|
||||
if (count($userfields) > 0)
|
||||
{
|
||||
$allUserfieldValues = $this->getUserfieldsService()->GetAllValues($args['entity']);
|
||||
// get result and total row count
|
||||
$objects = $this->getDatabase()->{$args['entity']}();
|
||||
$response = $response->withHeader('x-rowcount-total', $objects->count());
|
||||
|
||||
foreach ($objects as $object)
|
||||
{
|
||||
$userfieldKeyValuePairs = null;
|
||||
foreach ($userfields as $userfield)
|
||||
{
|
||||
$value = FindObjectInArrayByPropertyValue(FindAllObjectsInArrayByPropertyValue($allUserfieldValues, 'object_id', $object->id), 'name', $userfield->name);
|
||||
if ($value)
|
||||
{
|
||||
$userfieldKeyValuePairs[$userfield->name] = $value->value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$userfieldKeyValuePairs[$userfield->name] = null;
|
||||
}
|
||||
// apply filter, get filtered row count
|
||||
$objects = $this->applyQuery($objects, $query);
|
||||
$response = $response->withHeader('x-rowcount-filtered', $objects->count());
|
||||
|
||||
// apply limit/order
|
||||
$objects = $this->applyLimit($objects, $query);
|
||||
$objects = $this->applyOrder($objects, $query);
|
||||
|
||||
// add entity-specific queries
|
||||
if ($args['entity'] === 'products' && isset($query['only_in_stock'])) {
|
||||
$objects = $objects->where('id IN (SELECT product_id from stock_current WHERE amount_aggregated > 0)');
|
||||
}
|
||||
|
||||
$object->userfields = $userfieldKeyValuePairs;
|
||||
}
|
||||
// add userfields and joins to objects
|
||||
foreach ($objects as $object) {
|
||||
$this->addUserfieldsAndJoinsToRow($object, $args);
|
||||
}
|
||||
|
||||
return $this->ApiResponse($response, $objects);
|
||||
|
|
@ -181,12 +142,9 @@ class GenericEntityApiController extends BaseApiController
|
|||
|
||||
public function GetUserfields(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
return $this->ApiResponse($response, $this->getUserfieldsService()->GetValues($args['entity'], $args['objectId']));
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
} catch (\Exception $ex) {
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
|
@ -197,22 +155,40 @@ class GenericEntityApiController extends BaseApiController
|
|||
|
||||
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
|
||||
|
||||
try
|
||||
{
|
||||
if ($requestBody === null)
|
||||
{
|
||||
try {
|
||||
if ($requestBody === null) {
|
||||
throw new \Exception('Request body could not be parsed (probably invalid JSON format or missing/wrong Content-Type header)');
|
||||
}
|
||||
|
||||
$this->getUserfieldsService()->SetValues($args['entity'], $args['objectId'], $requestBody);
|
||||
return $this->EmptyApiResponse($response);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
} catch (\Exception $ex) {
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function addUserfieldsAndJoinsToRow(Row $object, array $args)
|
||||
{
|
||||
// add userfields
|
||||
$userfields = $this->getUserfieldsService()->GetValues($args['entity'], $object->id);
|
||||
if (count($userfields) === 0) {
|
||||
$userfields = null;
|
||||
}
|
||||
$object->userfields = $userfields;
|
||||
|
||||
// add entity-specific joins
|
||||
if ($args['entity'] === 'products') {
|
||||
$object->product_group = $object->product_group_id !== null ? $this->getDatabase()->product_groups($object->product_group_id) : null;
|
||||
$object->location = $object->location_id !== null ? $this->getDatabase()->locations($object->location_id) : null;
|
||||
$object->shopping_location = $object->shopping_location_id !== null ? $this->getDatabase()->shopping_locations($object->shopping_location_id) : null;
|
||||
$object->qu_purchase = $object->qu_id_purchase !== null ? $this->getDatabase()->quantity_units($object->qu_id_purchase) : null;
|
||||
$object->qu_stock = $object->qu_id_stock !== null ? $this->getDatabase()->quantity_units($object->qu_id_stock) : null;
|
||||
$object->parent_product = $object->parent_product_id !== null ? $this->getDatabase()->products($object->parent_product_id) : null;
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
private function IsEntityWithEditRequiresAdmin($entity)
|
||||
{
|
||||
return in_array($entity, $this->getOpenApiSpec()->components->schemas->ExposedEntityEditRequiresAdmin->enum);
|
||||
|
|
|
|||
|
|
@ -12,14 +12,12 @@ class RecipesController extends BaseController
|
|||
public function MealPlan(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
$start = date('Y-m-d');
|
||||
if (isset($request->getQueryParams()['start']) && IsIsoDate($request->getQueryParams()['start']))
|
||||
{
|
||||
if (isset($request->getQueryParams()['start']) && IsIsoDate($request->getQueryParams()['start'])) {
|
||||
$start = $request->getQueryParams()['start'];
|
||||
}
|
||||
|
||||
$days = 6;
|
||||
if (isset($request->getQueryParams()['days']) && filter_var($request->getQueryParams()['days'], FILTER_VALIDATE_INT) !== false)
|
||||
{
|
||||
if (isset($request->getQueryParams()['days']) && filter_var($request->getQueryParams()['days'], FILTER_VALIDATE_INT) !== false) {
|
||||
$days = $request->getQueryParams()['days'];
|
||||
}
|
||||
|
||||
|
|
@ -27,19 +25,16 @@ class RecipesController extends BaseController
|
|||
|
||||
$recipes = $this->getDatabase()->recipes()->where('type', RecipesService::RECIPE_TYPE_NORMAL)->fetchAll();
|
||||
$events = [];
|
||||
foreach ($this->getDatabase()->meal_plan()->where($mealPlanWhereTimespan) as $mealPlanEntry)
|
||||
{
|
||||
foreach ($this->getDatabase()->meal_plan()->where($mealPlanWhereTimespan) as $mealPlanEntry) {
|
||||
$recipe = FindObjectInArrayByPropertyValue($recipes, 'id', $mealPlanEntry['recipe_id']);
|
||||
$title = '';
|
||||
|
||||
if ($recipe !== null)
|
||||
{
|
||||
if ($recipe !== null) {
|
||||
$title = $recipe->name;
|
||||
}
|
||||
|
||||
$productDetails = null;
|
||||
if ($mealPlanEntry['product_id'] !== null)
|
||||
{
|
||||
if ($mealPlanEntry['product_id'] !== null) {
|
||||
$productDetails = $this->getStockService()->GetProductDetails($mealPlanEntry['product_id']);
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +55,6 @@ class RecipesController extends BaseController
|
|||
'recipes' => $recipes,
|
||||
'internalRecipes' => $this->getDatabase()->recipes()->where("id IN (SELECT recipe_id FROM meal_plan_internal_recipe_relation WHERE $mealPlanWhereTimespan)")->fetchAll(),
|
||||
'recipesResolved' => $this->getRecipesService()->GetRecipesResolved("recipe_id IN (SELECT recipe_id FROM meal_plan_internal_recipe_relation WHERE $mealPlanWhereTimespan)"),
|
||||
'products' => $this->getDatabase()->products()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityUnits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityUnitConversionsResolved' => $this->getDatabase()->quantity_unit_conversions_resolved(),
|
||||
'mealplanSections' => $this->getDatabase()->meal_plan_sections()->orderBy('sort_number'),
|
||||
|
|
@ -74,14 +68,10 @@ class RecipesController extends BaseController
|
|||
$recipesResolved = $this->getRecipesService()->GetRecipesResolved('recipe_id > 0');
|
||||
|
||||
$selectedRecipe = null;
|
||||
if (isset($request->getQueryParams()['recipe']))
|
||||
{
|
||||
if (isset($request->getQueryParams()['recipe'])) {
|
||||
$selectedRecipe = $this->getDatabase()->recipes($request->getQueryParams()['recipe']);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($recipes as $recipe)
|
||||
{
|
||||
} else {
|
||||
foreach ($recipes as $recipe) {
|
||||
$selectedRecipe = $recipe;
|
||||
break;
|
||||
}
|
||||
|
|
@ -89,8 +79,7 @@ class RecipesController extends BaseController
|
|||
|
||||
$totalCosts = null;
|
||||
$totalCalories = null;
|
||||
if ($selectedRecipe)
|
||||
{
|
||||
if ($selectedRecipe) {
|
||||
$totalCosts = FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $selectedRecipe->id)->costs;
|
||||
$totalCalories = FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $selectedRecipe->id)->calories;
|
||||
}
|
||||
|
|
@ -109,27 +98,22 @@ class RecipesController extends BaseController
|
|||
'selectedRecipeTotalCalories' => $totalCalories
|
||||
];
|
||||
|
||||
if ($selectedRecipe)
|
||||
{
|
||||
if ($selectedRecipe) {
|
||||
$selectedRecipeSubRecipes = $this->getDatabase()->recipes()->where('id IN (SELECT includes_recipe_id FROM recipes_nestings_resolved WHERE recipe_id = :1 AND includes_recipe_id != :1)', $selectedRecipe->id)->orderBy('name', 'COLLATE NOCASE')->fetchAll();
|
||||
|
||||
$includedRecipeIdsAbsolute = [];
|
||||
$includedRecipeIdsAbsolute[] = $selectedRecipe->id;
|
||||
foreach ($selectedRecipeSubRecipes as $subRecipe)
|
||||
{
|
||||
foreach ($selectedRecipeSubRecipes as $subRecipe) {
|
||||
$includedRecipeIdsAbsolute[] = $subRecipe->id;
|
||||
}
|
||||
|
||||
// TODO: Why not directly use recipes_pos_resolved for all recipe positions here (parent and child)?
|
||||
// This view already correctly recolves child recipe amounts...
|
||||
$allRecipePositions = [];
|
||||
foreach ($includedRecipeIdsAbsolute as $id)
|
||||
{
|
||||
foreach ($includedRecipeIdsAbsolute as $id) {
|
||||
$allRecipePositions[$id] = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id = :1 AND is_nested_recipe_pos = 0', $id)->orderBy('ingredient_group', 'ASC', 'product_group', 'ASC');
|
||||
foreach ($allRecipePositions[$id] as $pos)
|
||||
{
|
||||
if ($id != $selectedRecipe->id)
|
||||
{
|
||||
foreach ($allRecipePositions[$id] as $pos) {
|
||||
if ($id != $selectedRecipe->id) {
|
||||
$pos2 = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id = :1 AND recipe_pos_id = :2 AND is_nested_recipe_pos = 1', $selectedRecipe->id, $pos->recipe_pos_id)->fetch();
|
||||
$pos->recipe_amount = $pos2->recipe_amount;
|
||||
$pos->missing_amount = $pos2->missing_amount;
|
||||
|
|
@ -153,6 +137,7 @@ class RecipesController extends BaseController
|
|||
'recipe' => $this->getDatabase()->recipes($recipeId),
|
||||
'recipePositions' => $this->getDatabase()->recipes_pos()->where('recipe_id', $recipeId),
|
||||
'mode' => $recipeId == 'new' ? 'create' : 'edit',
|
||||
// TODO: remove 'products' after converting DataTable
|
||||
'products' => $this->getDatabase()->products()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityunits' => $this->getDatabase()->quantity_units(),
|
||||
'recipes' => $this->getDatabase()->recipes()->where('type', RecipesService::RECIPE_TYPE_NORMAL)->orderBy('name', 'COLLATE NOCASE'),
|
||||
|
|
@ -164,24 +149,21 @@ class RecipesController extends BaseController
|
|||
|
||||
public function RecipePosEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if ($args['recipePosId'] == 'new')
|
||||
{
|
||||
if ($args['recipePosId'] == 'new') {
|
||||
return $this->renderPage($response, 'recipeposform', [
|
||||
'mode' => 'create',
|
||||
'recipe' => $this->getDatabase()->recipes($args['recipeId']),
|
||||
'recipePos' => new \stdClass(),
|
||||
'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||
'recipePosId' => $args['recipePosId'],
|
||||
'quantityUnits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityUnitConversionsResolved' => $this->getDatabase()->quantity_unit_conversions_resolved()
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->renderPage($response, 'recipeposform', [
|
||||
'mode' => 'edit',
|
||||
'recipe' => $this->getDatabase()->recipes($args['recipeId']),
|
||||
'recipePos' => $this->getDatabase()->recipes_pos($args['recipePosId']),
|
||||
'products' => $this->getDatabase()->products()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'recipePosId' => $args['recipePosId'],
|
||||
'quantityUnits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityUnitConversionsResolved' => $this->getDatabase()->quantity_unit_conversions_resolved()
|
||||
]);
|
||||
|
|
@ -195,14 +177,11 @@ class RecipesController extends BaseController
|
|||
|
||||
public function MealPlanSectionEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if ($args['sectionId'] == 'new')
|
||||
{
|
||||
if ($args['sectionId'] == 'new') {
|
||||
return $this->renderPage($response, 'mealplansectionform', [
|
||||
'mode' => 'create'
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->renderPage($response, 'mealplansectionform', [
|
||||
'mealplanSection' => $this->getDatabase()->meal_plan_sections($args['sectionId']),
|
||||
'mode' => 'edit'
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ class StockController extends BaseController
|
|||
public function Consume(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
return $this->renderPage($response, 'consume', [
|
||||
'products' => $this->getDatabase()->products()->where('active = 1')->where('id IN (SELECT product_id from stock_current WHERE amount_aggregated > 0)')->orderBy('name'),
|
||||
'barcodes' => $this->getDatabase()->product_barcodes_comma_separated(),
|
||||
'recipes' => $this->getDatabase()->recipes()->where('type', RecipesService::RECIPE_TYPE_NORMAL)->orderBy('name', 'COLLATE NOCASE'),
|
||||
'locations' => $this->getDatabase()->locations()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityUnits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'),
|
||||
|
|
@ -24,8 +22,6 @@ class StockController extends BaseController
|
|||
public function Inventory(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
return $this->renderPage($response, 'inventory', [
|
||||
'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||
'barcodes' => $this->getDatabase()->product_barcodes_comma_separated(),
|
||||
'shoppinglocations' => $this->getDatabase()->shopping_locations()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'locations' => $this->getDatabase()->locations()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityUnits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'),
|
||||
|
|
@ -35,19 +31,15 @@ class StockController extends BaseController
|
|||
|
||||
public function Journal(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if (isset($request->getQueryParams()['months']) && filter_var($request->getQueryParams()['months'], FILTER_VALIDATE_INT) !== false)
|
||||
{
|
||||
if (isset($request->getQueryParams()['months']) && filter_var($request->getQueryParams()['months'], FILTER_VALIDATE_INT) !== false) {
|
||||
$months = $request->getQueryParams()['months'];
|
||||
$where = "row_created_timestamp > DATE(DATE('now', 'localtime'), '-$months months')";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Default 6 months
|
||||
$where = "row_created_timestamp > DATE(DATE('now', 'localtime'), '-6 months')";
|
||||
}
|
||||
|
||||
if (isset($request->getQueryParams()['product']) && filter_var($request->getQueryParams()['product'], FILTER_VALIDATE_INT) !== false)
|
||||
{
|
||||
if (isset($request->getQueryParams()['product']) && filter_var($request->getQueryParams()['product'], FILTER_VALIDATE_INT) !== false) {
|
||||
$productId = $request->getQueryParams()['product'];
|
||||
$where .= " AND product_id = $productId";
|
||||
}
|
||||
|
|
@ -56,7 +48,6 @@ class StockController extends BaseController
|
|||
|
||||
return $this->renderPage($response, 'stockjournal', [
|
||||
'stockLog' => $this->getDatabase()->uihelper_stock_journal()->where($where)->orderBy('row_created_timestamp', 'DESC'),
|
||||
'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||
'locations' => $this->getDatabase()->locations()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'users' => $usersService->GetUsersAsDto(),
|
||||
'transactionTypes' => GetClassConstants('\Grocy\Services\StockService', 'TRANSACTION_TYPE_')
|
||||
|
|
@ -75,15 +66,12 @@ class StockController extends BaseController
|
|||
|
||||
public function LocationEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if ($args['locationId'] == 'new')
|
||||
{
|
||||
if ($args['locationId'] == 'new') {
|
||||
return $this->renderPage($response, 'locationform', [
|
||||
'mode' => 'create',
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('locations')
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->renderPage($response, 'locationform', [
|
||||
'location' => $this->getDatabase()->locations($args['locationId']),
|
||||
'mode' => 'edit',
|
||||
|
|
@ -121,13 +109,11 @@ class StockController extends BaseController
|
|||
{
|
||||
$product = null;
|
||||
|
||||
if (isset($request->getQueryParams()['product']))
|
||||
{
|
||||
if (isset($request->getQueryParams()['product'])) {
|
||||
$product = $this->getDatabase()->products($request->getQueryParams()['product']);
|
||||
}
|
||||
|
||||
if ($args['productBarcodeId'] == 'new')
|
||||
{
|
||||
if ($args['productBarcodeId'] == 'new') {
|
||||
return $this->renderPage($response, 'productbarcodeform', [
|
||||
'mode' => 'create',
|
||||
'barcodes' => $this->getDatabase()->product_barcodes()->orderBy('barcode'),
|
||||
|
|
@ -137,9 +123,7 @@ class StockController extends BaseController
|
|||
'quantityUnitConversionsResolved' => $this->getDatabase()->quantity_unit_conversions_resolved(),
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('product_barcodes')
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->renderPage($response, 'productbarcodeform', [
|
||||
'mode' => 'edit',
|
||||
'barcode' => $this->getDatabase()->product_barcodes($args['productBarcodeId']),
|
||||
|
|
@ -154,8 +138,7 @@ class StockController extends BaseController
|
|||
|
||||
public function ProductEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if ($args['productId'] == 'new')
|
||||
{
|
||||
if ($args['productId'] == 'new') {
|
||||
return $this->renderPage($response, 'productform', [
|
||||
'locations' => $this->getDatabase()->locations()->orderBy('name'),
|
||||
'barcodes' => $this->getDatabase()->product_barcodes()->orderBy('barcode'),
|
||||
|
|
@ -163,13 +146,10 @@ class StockController extends BaseController
|
|||
'shoppinglocations' => $this->getDatabase()->shopping_locations()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'productgroups' => $this->getDatabase()->product_groups()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('products'),
|
||||
'products' => $this->getDatabase()->products()->where('parent_product_id IS NULL and active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||
'isSubProductOfOthers' => false,
|
||||
'mode' => 'create'
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$product = $this->getDatabase()->products($args['productId']);
|
||||
|
||||
return $this->renderPage($response, 'productform', [
|
||||
|
|
@ -180,7 +160,6 @@ class StockController extends BaseController
|
|||
'shoppinglocations' => $this->getDatabase()->shopping_locations()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'productgroups' => $this->getDatabase()->product_groups()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('products'),
|
||||
'products' => $this->getDatabase()->products()->where('id != :1 AND parent_product_id IS NULL and active = 1', $product->id)->orderBy('name', 'COLLATE NOCASE'),
|
||||
'isSubProductOfOthers' => $this->getDatabase()->products()->where('parent_product_id = :1', $product->id)->count() !== 0,
|
||||
'mode' => 'edit',
|
||||
'quConversions' => $this->getDatabase()->quantity_unit_conversions(),
|
||||
|
|
@ -198,15 +177,12 @@ class StockController extends BaseController
|
|||
|
||||
public function ProductGroupEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if ($args['productGroupId'] == 'new')
|
||||
{
|
||||
if ($args['productGroupId'] == 'new') {
|
||||
return $this->renderPage($response, 'productgroupform', [
|
||||
'mode' => 'create',
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('product_groups')
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->renderPage($response, 'productgroupform', [
|
||||
'group' => $this->getDatabase()->product_groups($args['productGroupId']),
|
||||
'mode' => 'edit',
|
||||
|
|
@ -227,21 +203,21 @@ class StockController extends BaseController
|
|||
|
||||
public function ProductsList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
$products = $this->getDatabase()->products();
|
||||
if (!isset($request->getQueryParams()['include_disabled']))
|
||||
{
|
||||
$products = $products->where('active = 1');
|
||||
}
|
||||
// $products = $this->getDatabase()->products();
|
||||
// if (!isset($request->getQueryParams()['include_disabled']))
|
||||
// {
|
||||
// $products = $products->where('active = 1');
|
||||
// }
|
||||
|
||||
if (isset($request->getQueryParams()['only_in_stock']))
|
||||
{
|
||||
$products = $products->where('id IN (SELECT product_id from stock_current WHERE amount_aggregated > 0)');
|
||||
}
|
||||
// if (isset($request->getQueryParams()['only_in_stock']))
|
||||
// {
|
||||
// $products = $products->where('id IN (SELECT product_id from stock_current WHERE amount_aggregated > 0)');
|
||||
// }
|
||||
|
||||
$products = $products->orderBy('name', 'COLLATE NOCASE');
|
||||
// $products = $products->orderBy('name', 'COLLATE NOCASE');
|
||||
|
||||
return $this->renderPage($response, 'products', [
|
||||
'products' => $products,
|
||||
// 'products' => $products,
|
||||
'locations' => $this->getDatabase()->locations()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityunits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'productGroups' => $this->getDatabase()->product_groups()->orderBy('name', 'COLLATE NOCASE'),
|
||||
|
|
@ -254,8 +230,6 @@ class StockController extends BaseController
|
|||
public function Purchase(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
return $this->renderPage($response, 'purchase', [
|
||||
'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||
'barcodes' => $this->getDatabase()->product_barcodes_comma_separated(),
|
||||
'shoppinglocations' => $this->getDatabase()->shopping_locations()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'locations' => $this->getDatabase()->locations()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityUnits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'),
|
||||
|
|
@ -267,20 +241,17 @@ class StockController extends BaseController
|
|||
{
|
||||
$product = null;
|
||||
|
||||
if (isset($request->getQueryParams()['product']))
|
||||
{
|
||||
if (isset($request->getQueryParams()['product'])) {
|
||||
$product = $this->getDatabase()->products($request->getQueryParams()['product']);
|
||||
}
|
||||
|
||||
$defaultQuUnit = null;
|
||||
|
||||
if (isset($request->getQueryParams()['qu-unit']))
|
||||
{
|
||||
if (isset($request->getQueryParams()['qu-unit'])) {
|
||||
$defaultQuUnit = $this->getDatabase()->quantity_units($request->getQueryParams()['qu-unit']);
|
||||
}
|
||||
|
||||
if ($args['quConversionId'] == 'new')
|
||||
{
|
||||
if ($args['quConversionId'] == 'new') {
|
||||
return $this->renderPage($response, 'quantityunitconversionform', [
|
||||
'mode' => 'create',
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('quantity_unit_conversions'),
|
||||
|
|
@ -288,9 +259,7 @@ class StockController extends BaseController
|
|||
'product' => $product,
|
||||
'defaultQuUnit' => $defaultQuUnit
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->renderPage($response, 'quantityunitconversionform', [
|
||||
'quConversion' => $this->getDatabase()->quantity_unit_conversions($args['quConversionId']),
|
||||
'mode' => 'edit',
|
||||
|
|
@ -304,17 +273,14 @@ class StockController extends BaseController
|
|||
|
||||
public function QuantityUnitEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if ($args['quantityunitId'] == 'new')
|
||||
{
|
||||
if ($args['quantityunitId'] == 'new') {
|
||||
return $this->renderPage($response, 'quantityunitform', [
|
||||
'mode' => 'create',
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('quantity_units'),
|
||||
'pluralCount' => $this->getLocalizationService()->GetPluralCount(),
|
||||
'pluralRule' => $this->getLocalizationService()->GetPluralDefinition()
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$quantityUnit = $this->getDatabase()->quantity_units($args['quantityunitId']);
|
||||
|
||||
return $this->renderPage($response, 'quantityunitform', [
|
||||
|
|
@ -349,8 +315,7 @@ class StockController extends BaseController
|
|||
{
|
||||
$listId = 1;
|
||||
|
||||
if (isset($request->getQueryParams()['list']))
|
||||
{
|
||||
if (isset($request->getQueryParams()['list'])) {
|
||||
$listId = $request->getQueryParams()['list'];
|
||||
}
|
||||
|
||||
|
|
@ -371,15 +336,12 @@ class StockController extends BaseController
|
|||
|
||||
public function ShoppingListEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if ($args['listId'] == 'new')
|
||||
{
|
||||
if ($args['listId'] == 'new') {
|
||||
return $this->renderPage($response, 'shoppinglistform', [
|
||||
'mode' => 'create',
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('shopping_lists')
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->renderPage($response, 'shoppinglistform', [
|
||||
'shoppingList' => $this->getDatabase()->shopping_lists($args['listId']),
|
||||
'mode' => 'edit',
|
||||
|
|
@ -390,8 +352,7 @@ class StockController extends BaseController
|
|||
|
||||
public function ShoppingListItemEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if ($args['itemId'] == 'new')
|
||||
{
|
||||
if ($args['itemId'] == 'new') {
|
||||
return $this->renderPage($response, 'shoppinglistitemform', [
|
||||
'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||
'shoppingLists' => $this->getDatabase()->shopping_lists()->orderBy('name', 'COLLATE NOCASE'),
|
||||
|
|
@ -400,12 +361,9 @@ class StockController extends BaseController
|
|||
'quantityUnitConversionsResolved' => $this->getDatabase()->quantity_unit_conversions_resolved(),
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('shopping_list')
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->renderPage($response, 'shoppinglistitemform', [
|
||||
'listItem' => $this->getDatabase()->shopping_list($args['itemId']),
|
||||
'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||
'shoppingLists' => $this->getDatabase()->shopping_lists()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'mode' => 'edit',
|
||||
'quantityUnits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'),
|
||||
|
|
@ -422,15 +380,12 @@ class StockController extends BaseController
|
|||
|
||||
public function ShoppingLocationEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
if ($args['shoppingLocationId'] == 'new')
|
||||
{
|
||||
if ($args['shoppingLocationId'] == 'new') {
|
||||
return $this->renderPage($response, 'shoppinglocationform', [
|
||||
'mode' => 'create',
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('shopping_locations')
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->renderPage($response, 'shoppinglocationform', [
|
||||
'shoppinglocation' => $this->getDatabase()->shopping_locations($args['shoppingLocationId']),
|
||||
'mode' => 'edit',
|
||||
|
|
@ -489,6 +444,7 @@ class StockController extends BaseController
|
|||
$nextXDays = $usersService->GetUserSettings(GROCY_USER_ID)['stock_due_soon_days'];
|
||||
|
||||
return $this->renderPage($response, 'stockentries', [
|
||||
// TODO: remove 'products' after converting DataTable
|
||||
'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityunits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'locations' => $this->getDatabase()->locations()->orderBy('name', 'COLLATE NOCASE'),
|
||||
|
|
@ -504,8 +460,6 @@ class StockController extends BaseController
|
|||
public function Transfer(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
return $this->renderPage($response, 'transfer', [
|
||||
'products' => $this->getDatabase()->products()->where('active = 1')->where('id IN (SELECT product_id from stock_current WHERE amount_aggregated > 0)')->orderBy('name', 'COLLATE NOCASE'),
|
||||
'barcodes' => $this->getDatabase()->product_barcodes_comma_separated(),
|
||||
'locations' => $this->getDatabase()->locations()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityUnits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'),
|
||||
'quantityUnitConversionsResolved' => $this->getDatabase()->quantity_unit_conversions_resolved()
|
||||
|
|
@ -515,23 +469,19 @@ class StockController extends BaseController
|
|||
public function JournalSummary(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
$entries = $this->getDatabase()->uihelper_stock_journal_summary();
|
||||
if (isset($request->getQueryParams()['product_id']))
|
||||
{
|
||||
if (isset($request->getQueryParams()['product_id'])) {
|
||||
$entries = $entries->where('product_id', $request->getQueryParams()['product_id']);
|
||||
}
|
||||
if (isset($request->getQueryParams()['user_id']))
|
||||
{
|
||||
if (isset($request->getQueryParams()['user_id'])) {
|
||||
$entries = $entries->where('user_id', $request->getQueryParams()['user_id']);
|
||||
}
|
||||
if (isset($request->getQueryParams()['transaction_type']))
|
||||
{
|
||||
if (isset($request->getQueryParams()['transaction_type'])) {
|
||||
$entries = $entries->where('transaction_type', $request->getQueryParams()['transaction_type']);
|
||||
}
|
||||
|
||||
$usersService = $this->getUsersService();
|
||||
return $this->renderPage($response, 'stockjournalsummary', [
|
||||
'entries' => $entries,
|
||||
'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||
'users' => $usersService->GetUsersAsDto(),
|
||||
'transactionTypes' => GetClassConstants('\Grocy\Services\StockService', 'TRANSACTION_TYPE_')
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -2308,3 +2308,18 @@ msgstr ""
|
|||
|
||||
msgid "Average execution frequency"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pakke"
|
||||
msgstr ""
|
||||
|
||||
msgid "Del"
|
||||
msgstr ""
|
||||
|
||||
msgid "Boks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bunt"
|
||||
msgstr ""
|
||||
|
||||
msgid "Flaske"
|
||||
msgstr ""
|
||||
|
|
|
|||
20
package.json
20
package.json
|
|
@ -12,15 +12,15 @@
|
|||
"bootstrap-select": "^1.13.18",
|
||||
"bwip-js": "^3.0.1",
|
||||
"chart.js": "^2.8.0",
|
||||
"datatables.net": "^1.10.22",
|
||||
"datatables.net-bs4": "^1.10.22",
|
||||
"datatables.net-colreorder": "^1.5.2",
|
||||
"datatables.net-colreorder-bs4": "^1.5.2",
|
||||
"datatables.net-plugins": "^1.10.20",
|
||||
"datatables.net-rowgroup": "^1.1.2",
|
||||
"datatables.net-rowgroup-bs4": "^1.1.2",
|
||||
"datatables.net-select": "^1.3.1",
|
||||
"datatables.net-select-bs4": "^1.3.1",
|
||||
"datatables.net": "^1.11.5",
|
||||
"datatables.net-bs4": "^1.11.5",
|
||||
"datatables.net-colreorder": "^1.5.5",
|
||||
"datatables.net-colreorder-bs4": "^1.5.5",
|
||||
"datatables.net-plugins": "^1.11.5",
|
||||
"datatables.net-rowgroup": "^1.1.4",
|
||||
"datatables.net-rowgroup-bs4": "^1.1.4",
|
||||
"datatables.net-select": "^1.3.4",
|
||||
"datatables.net-select-bs4": "^1.3.4",
|
||||
"fullcalendar": "^3.10.1",
|
||||
"gettext-translator": "2.1.0",
|
||||
"jquery": "3.5.1",
|
||||
|
|
@ -28,6 +28,8 @@
|
|||
"jquery-serializejson": "^2.9.0",
|
||||
"moment": "^2.27.0",
|
||||
"nosleep.js": "^0.12.0",
|
||||
"select2": "^4.0.13",
|
||||
"select2-theme-bootstrap4": "^1.0.1",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"startbootstrap-sb-admin": "4.0.0",
|
||||
"summernote": "^0.8.18",
|
||||
|
|
|
|||
|
|
@ -94,10 +94,10 @@ body.fullscreen-card {
|
|||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.form-check-input.is-valid ~ .form-check-label,
|
||||
.was-validated .form-check-input:valid ~ .form-check-label,
|
||||
.custom-control-input.is-valid ~ .custom-control-label,
|
||||
.was-validated .custom-control-input:valid ~ .custom-control-label {
|
||||
.form-check-input.is-valid~.form-check-label,
|
||||
.was-validated .form-check-input:valid~.form-check-label,
|
||||
.custom-control-input.is-valid~.custom-control-label,
|
||||
.was-validated .custom-control-input:valid~.custom-control-label {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
|
@ -192,20 +192,20 @@ form.has-sticky-form-footer .form-group:nth-last-child(2) {
|
|||
border-color: #d6d6d6 !important;
|
||||
}
|
||||
|
||||
.navbar-sidenav > li,
|
||||
.sidenav-second-level > li {
|
||||
.navbar-sidenav>li,
|
||||
.sidenav-second-level>li {
|
||||
transition: all 0.3s !important;
|
||||
}
|
||||
|
||||
.navbar-sidenav > li:hover,
|
||||
.sidenav-second-level > li:hover,
|
||||
.navbar-sidenav>li:hover,
|
||||
.sidenav-second-level>li:hover,
|
||||
.navbar-nav .dropdown-item:hover {
|
||||
box-shadow: inset 5px 0 0 #337ab7 !important;
|
||||
background-color: #d6d6d6 !important;
|
||||
}
|
||||
|
||||
.navbar-sidenav > li > a:focus,
|
||||
.sidenav-second-level > li > a:focus,
|
||||
.navbar-sidenav>li>a:focus,
|
||||
.sidenav-second-level>li>a:focus,
|
||||
.navbar-nav .dropdown-item:focus {
|
||||
box-shadow: inset 5px 0 0 #ab2230 !important;
|
||||
background-color: #d6d6d6 !important;
|
||||
|
|
@ -228,7 +228,8 @@ form.has-sticky-form-footer .form-group:nth-last-child(2) {
|
|||
cursor: wait;
|
||||
}
|
||||
|
||||
.expandable-text .collapse, .module .collapsing {
|
||||
.expandable-text .collapse,
|
||||
.module .collapsing {
|
||||
height: 2.4rem;
|
||||
}
|
||||
|
||||
|
|
@ -255,7 +256,8 @@ form.has-sticky-form-footer .form-group:nth-last-child(2) {
|
|||
.table-inline-menu.dropdown-menu {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
width: 96vw; /* Set width of popup menu to screen size */
|
||||
width: 96vw;
|
||||
/* Set width of popup menu to screen size */
|
||||
}
|
||||
|
||||
a:not([href]) {
|
||||
|
|
@ -326,12 +328,39 @@ a:not([href]) {
|
|||
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;
|
||||
}
|
||||
|
||||
.select2-hidden-accessible.custom-select.is-invalid+.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection,
|
||||
.was-validated .select2-hidden-accessible.custom-select:invalid+.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection {
|
||||
border-color: #dc3545;
|
||||
}
|
||||
|
||||
.select2-hidden-accessible.custom-select.is-invalid+.select2-container--focus.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection,
|
||||
.was-validated .select2-hidden-accessible.custom-select:invalid+.select2-container--focus.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection,
|
||||
.select2-hidden-accessible.custom-select.is-invalid+.select2-container--open.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection,
|
||||
.was-validated .select2-hidden-accessible.custom-select:invalid+.select2-container--open.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection {
|
||||
border-color: #dc3545;
|
||||
box-shadow: 0 0 0 0.2rem rgb(220 53 69 / 25%);
|
||||
}
|
||||
|
||||
.select2-hidden-accessible.custom-select.is-valid+.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection,
|
||||
.was-validated .select2-hidden-accessible.custom-select:valid+.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection {
|
||||
border-color: #28a745;
|
||||
}
|
||||
|
||||
.select2-hidden-accessible.custom-select.is-valid+.select2-container--focus.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection,
|
||||
.was-validated .select2-hidden-accessible.custom-select:valid+.select2-container--focus.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection,
|
||||
.select2-hidden-accessible.custom-select.is-valid+.select2-container--open.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection,
|
||||
.was-validated .select2-hidden-accessible.custom-select:valid+.select2-container--open.select2-container--bootstrap:not(:last-child)>.selection>.select2-selection {
|
||||
border-color: #28a745;
|
||||
box-shadow: 0 0 0 0.2rem rgb(40 167 69 / 25%);
|
||||
}
|
||||
|
||||
/* There is a little too much padding on form inputs */
|
||||
.form-control {
|
||||
padding-right: 0.75rem !important;
|
||||
}
|
||||
|
||||
.btn-group-xs > .btn, .btn-xs {
|
||||
.btn-group-xs>.btn,
|
||||
.btn-xs {
|
||||
padding: 0.25rem 0.4rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 0.5;
|
||||
|
|
@ -346,7 +375,7 @@ a:not([href]) {
|
|||
font-size: 125%;
|
||||
}
|
||||
|
||||
.input-group > .form-control:focus {
|
||||
.input-group>.form-control:focus {
|
||||
z-index: inherit;
|
||||
}
|
||||
|
||||
|
|
@ -384,7 +413,7 @@ tr.dtrg-group {
|
|||
}
|
||||
|
||||
/* Third party component customizations - toastr */
|
||||
#toast-container > div {
|
||||
#toast-container>div {
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100);
|
||||
}
|
||||
|
|
@ -397,7 +426,7 @@ tr.dtrg-group {
|
|||
background-color: #dc3545;
|
||||
}
|
||||
|
||||
#toast-container > div {
|
||||
#toast-container>div {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
|
@ -408,19 +437,19 @@ tr.dtrg-group {
|
|||
}
|
||||
|
||||
/* Third party component customizations - SB Admin 2 */
|
||||
#mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link:after,
|
||||
#mainNav .navbar-collapse .navbar-nav>.nav-item.dropdown>.nav-link:after,
|
||||
#mainNav .navbar-collapse .navbar-sidenav .nav-link-collapse:after {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
#mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link.py-0:after,
|
||||
#mainNav .navbar-collapse .navbar-nav>.nav-item.dropdown>.nav-link.py-0:after,
|
||||
#mainNav .navbar-collapse .navbar-sidenav .nav-link-collapse.py-0:after {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
#mainNav .navbar-collapse .navbar-sidenav > .nav-item > .nav-link,
|
||||
#mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a {
|
||||
#mainNav .navbar-collapse .navbar-sidenav>.nav-item>.nav-link,
|
||||
#mainNav .navbar-collapse .navbar-sidenav>.nav-item .sidenav-second-level>li>a {
|
||||
padding: 0.75em;
|
||||
}
|
||||
|
||||
|
|
@ -460,7 +489,7 @@ html {
|
|||
margin-left: -0.15em !important;
|
||||
}
|
||||
|
||||
#mainNav .navbar-collapse .navbar-sidenav > .nav-item > .nav-link {
|
||||
#mainNav .navbar-collapse .navbar-sidenav>.nav-item>.nav-link {
|
||||
padding-right: 1.25em !important;
|
||||
}
|
||||
|
||||
|
|
@ -603,7 +632,7 @@ canvas.drawingBuffer {
|
|||
margin-bottom: -.65rem;
|
||||
}
|
||||
|
||||
.grocy-tabs.tab-content > .active {
|
||||
.grocy-tabs.tab-content>.active {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
|
@ -615,7 +644,7 @@ canvas.drawingBuffer {
|
|||
}
|
||||
|
||||
@media print {
|
||||
.grocy-tabs.print.tab-content > .tab-pane {
|
||||
.grocy-tabs.print.tab-content>.tab-pane {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
opacity: 1 !important;
|
||||
|
|
@ -625,7 +654,7 @@ canvas.drawingBuffer {
|
|||
height: auto !important;
|
||||
}
|
||||
|
||||
.grocy-tabs.break > .tab-pane {
|
||||
.grocy-tabs.break>.tab-pane {
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
|
|
@ -656,3 +685,9 @@ canvas.drawingBuffer {
|
|||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Fix Select2 in .input-group */
|
||||
.input-group>.select2-hidden-accessible+.select2-container--bootstrap>.selection>.select2-selection,
|
||||
.input-group>.select2-hidden-accessible+.select2-container--bootstrap>.selection>.select2-selection.form-control {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,72 +1,60 @@
|
|||
$('#save-chore-button').on('click', function(e)
|
||||
{
|
||||
$('#save-chore-button').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if ($(".combobox-menu-visible").length)
|
||||
{
|
||||
if ($(".combobox-menu-visible").length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var jsonData = $('#chore-form').serializeJSON();
|
||||
jsonData.start_date = Grocy.Components.DateTimePicker.GetValue();
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) {
|
||||
jsonData.assignment_config = $("#assignment_config").val().join(",");
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.BeginUiBusy("chore-form");
|
||||
|
||||
if (Grocy.EditMode === 'create')
|
||||
{
|
||||
if (Grocy.EditMode === 'create') {
|
||||
Grocy.Api.Post('objects/chores', jsonData,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
Grocy.EditObjectId = result.created_object_id;
|
||||
Grocy.Components.UserfieldsForm.Save(function()
|
||||
{
|
||||
Grocy.Api.Post('chores/executions/calculate-next-assignments', { "chore_id": Grocy.EditObjectId },
|
||||
function(result)
|
||||
{
|
||||
Grocy.Components.UserfieldsForm.Save(function() {
|
||||
Grocy.Api.Post('chores/executions/calculate-next-assignments', {
|
||||
"chore_id": Grocy.EditObjectId
|
||||
},
|
||||
function(result) {
|
||||
window.location.href = U('/chores');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy();
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("chore-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.Api.Put('objects/chores/' + Grocy.EditObjectId, jsonData,
|
||||
function(result)
|
||||
{
|
||||
Grocy.Components.UserfieldsForm.Save(function()
|
||||
{
|
||||
Grocy.Api.Post('chores/executions/calculate-next-assignments', { "chore_id": Grocy.EditObjectId },
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
Grocy.Components.UserfieldsForm.Save(function() {
|
||||
Grocy.Api.Post('chores/executions/calculate-next-assignments', {
|
||||
"chore_id": Grocy.EditObjectId
|
||||
},
|
||||
function(result) {
|
||||
window.location.href = U('/chores');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy();
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("chore-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
|
|
@ -74,13 +62,11 @@
|
|||
}
|
||||
});
|
||||
|
||||
$('#chore-form input').keyup(function(event)
|
||||
{
|
||||
$('#chore-form input').on('keyup', function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('chore-form');
|
||||
});
|
||||
|
||||
$('#chore-form input').keydown(function(event)
|
||||
{
|
||||
$('#chore-form input').on('keydown', function(event) {
|
||||
if (event.keyCode === 13) //Enter
|
||||
{
|
||||
event.preventDefault();
|
||||
|
|
@ -88,58 +74,48 @@ $('#chore-form input').keydown(function(event)
|
|||
if (document.getElementById('chore-form').checkValidity() === false) //There is at least one validation error
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#save-chore-button').click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var checkboxValues = $("#period_config").val().split(",");
|
||||
for (var i = 0; i < checkboxValues.length; i++)
|
||||
{
|
||||
if (!checkboxValues[i].isEmpty())
|
||||
{
|
||||
for (var i = 0; i < checkboxValues.length; i++) {
|
||||
if (!checkboxValues[i].isEmpty()) {
|
||||
$("#" + checkboxValues[i]).prop('checked', true);
|
||||
}
|
||||
}
|
||||
|
||||
Grocy.Components.UserfieldsForm.Load();
|
||||
$('#name').focus();
|
||||
$('#name').trigger('focus');
|
||||
Grocy.FrontendHelpers.ValidateForm('chore-form');
|
||||
|
||||
if (Grocy.EditMode == "edit")
|
||||
{
|
||||
if (Grocy.EditMode == "edit") {
|
||||
Grocy.Api.Get('objects/chores_log?limit=1&query[]=chore_id=' + Grocy.EditObjectId,
|
||||
function(journalEntries)
|
||||
{
|
||||
if (journalEntries.length > 0)
|
||||
{
|
||||
function(journalEntries) {
|
||||
if (journalEntries.length > 0) {
|
||||
$(".datetimepicker-input").attr("disabled", "");
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
setTimeout(function() {
|
||||
$(".input-group-chore-period-type").trigger("change");
|
||||
$(".input-group-chore-assignment-type").trigger("change");
|
||||
|
||||
// Click twice to trigger on-click but not change the actual checked state
|
||||
$("#consume_product_on_execution").click();
|
||||
$("#consume_product_on_execution").click();
|
||||
$("#consume_product_on_execution").trigger('click');
|
||||
$("#consume_product_on_execution").trigger('click');
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
Grocy.Components.ProductPicker.Validate();
|
||||
}, 100);
|
||||
|
||||
$('.input-group-chore-period-type').on('change keyup', function(e)
|
||||
{
|
||||
$('.input-group-chore-period-type').on('change keyup', function(e) {
|
||||
var periodType = $('#period_type').val();
|
||||
var periodDays = $('#period_days').val();
|
||||
var periodInterval = $('#period_interval').val();
|
||||
|
|
@ -148,68 +124,49 @@ $('.input-group-chore-period-type').on('change keyup', function(e)
|
|||
$(".period-type-" + periodType).removeClass("d-none");
|
||||
$("#period_config").val("");
|
||||
|
||||
if (periodType === 'manually')
|
||||
{
|
||||
if (periodType === 'manually') {
|
||||
$('#chore-schedule-info').text(__t('This means the next execution of this chore is not scheduled'));
|
||||
}
|
||||
else if (periodType === 'hourly')
|
||||
{
|
||||
} else if (periodType === 'hourly') {
|
||||
$('#chore-schedule-info').text(__n(periodInterval, "This means the next execution of this chore is scheduled %s hour after the last execution", "This means the next execution of this chore is scheduled %s hours after the last execution"));
|
||||
}
|
||||
else if (periodType === 'daily')
|
||||
{
|
||||
} else if (periodType === 'daily') {
|
||||
$('#chore-schedule-info').text(__n(periodInterval, "This means the next execution of this chore is scheduled at the same time (based on the start date) every day", "This means the next execution of this chore is scheduled at the same time (based on the start date) every %s days"));
|
||||
}
|
||||
else if (periodType === 'weekly')
|
||||
{
|
||||
} else if (periodType === 'weekly') {
|
||||
$('#chore-schedule-info').text(__n(periodInterval, "This means the next execution of this chore is scheduled every week on the selected weekdays", "This means the next execution of this chore is scheduled every %s weeks on the selected weekdays"));
|
||||
$("#period_config").val($(".period-type-weekly input:checkbox:checked").map(function() { return this.value; }).get().join(","));
|
||||
}
|
||||
else if (periodType === 'monthly')
|
||||
{
|
||||
$("#period_config").val($(".period-type-weekly input:checkbox:checked").map(function() {
|
||||
return this.value;
|
||||
}).get().join(","));
|
||||
} else if (periodType === 'monthly') {
|
||||
$('#chore-schedule-info').text(__n(periodInterval, "This means the next execution of this chore is scheduled on the selected day every month", "This means the next execution of this chore is scheduled on the selected day every %s months"));
|
||||
$("label[for='period_days']").text(__t("Day of month"));
|
||||
$("#period_days").attr("min", "1");
|
||||
$("#period_days").attr("max", "31");
|
||||
}
|
||||
else if (periodType === 'yearly')
|
||||
{
|
||||
} else if (periodType === 'yearly') {
|
||||
$('#chore-schedule-info').text(__n(periodInterval, 'This means the next execution of this chore is scheduled every year on the same day (based on the start date)', 'This means the next execution of this chore is scheduled every %s years on the same day (based on the start date)'));
|
||||
}
|
||||
else if (periodType === 'adaptive')
|
||||
{
|
||||
} else if (periodType === 'adaptive') {
|
||||
$('#chore-schedule-info').text(__t('This means the next execution of this chore is scheduled dynamically based on the past average execution frequency'));
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.ValidateForm('chore-form');
|
||||
});
|
||||
|
||||
$('.input-group-chore-assignment-type').on('change', function(e)
|
||||
{
|
||||
$('.input-group-chore-assignment-type').on('change', function(e) {
|
||||
var assignmentType = $('#assignment_type').val();
|
||||
|
||||
$('#chore-period-assignment-info').text("");
|
||||
$("#assignment_config").removeAttr("required");
|
||||
$("#assignment_config").attr("disabled", "");
|
||||
|
||||
if (assignmentType === 'no-assignment')
|
||||
{
|
||||
if (assignmentType === 'no-assignment') {
|
||||
$('#chore-assignment-type-info').text(__t('This means the next execution of this chore will not be assigned to anyone'));
|
||||
}
|
||||
else if (assignmentType === 'who-least-did-first')
|
||||
{
|
||||
} else if (assignmentType === 'who-least-did-first') {
|
||||
$('#chore-assignment-type-info').text(__t('This means the next execution of this chore will be assigned to the one who executed it least'));
|
||||
$("#assignment_config").attr("required", "");
|
||||
$("#assignment_config").removeAttr("disabled");
|
||||
}
|
||||
else if (assignmentType === 'random')
|
||||
{
|
||||
} else if (assignmentType === 'random') {
|
||||
$('#chore-assignment-type-info').text(__t('This means the next execution of this chore will be assigned randomly'));
|
||||
$("#assignment_config").attr("required", "");
|
||||
$("#assignment_config").removeAttr("disabled");
|
||||
}
|
||||
else if (assignmentType === 'in-alphabetical-order')
|
||||
{
|
||||
} else if (assignmentType === 'in-alphabetical-order') {
|
||||
$('#chore-assignment-type-info').text(__t('This means the next execution of this chore will be assigned to the next one in alphabetical order'));
|
||||
$("#assignment_config").attr("required", "");
|
||||
$("#assignment_config").removeAttr("disabled");
|
||||
|
|
@ -218,15 +175,11 @@ $('.input-group-chore-assignment-type').on('change', function(e)
|
|||
Grocy.FrontendHelpers.ValidateForm('chore-form');
|
||||
});
|
||||
|
||||
$("#consume_product_on_execution").on("click", function()
|
||||
{
|
||||
if (this.checked)
|
||||
{
|
||||
$("#consume_product_on_execution").on("click", function() {
|
||||
if (this.checked) {
|
||||
Grocy.Components.ProductPicker.Enable();
|
||||
$("#product_amount").removeAttr("disabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.Components.ProductPicker.Disable();
|
||||
$("#product_amount").attr("disabled", "");
|
||||
}
|
||||
|
|
@ -234,35 +187,28 @@ $("#consume_product_on_execution").on("click", function()
|
|||
Grocy.FrontendHelpers.ValidateForm("chore-form");
|
||||
});
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
Grocy.Components.ProductPicker.OnChange(function(e) {
|
||||
var productId = $(e.target).val();
|
||||
|
||||
if (productId)
|
||||
{
|
||||
if (productId) {
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
$('#amount_qu_unit').text(productDetails.quantity_unit_stock.name);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '.chore-grocycode-label-print', function(e)
|
||||
{
|
||||
$(document).on('click', '.chore-grocycode-label-print', function(e) {
|
||||
e.preventDefault();
|
||||
document.activeElement.blur();
|
||||
|
||||
var choreId = $(e.currentTarget).attr('data-chore-id');
|
||||
Grocy.Api.Get('chores/' + choreId + '/printlabel', function(labelData)
|
||||
{
|
||||
if (Grocy.Webhooks.labelprinter !== undefined)
|
||||
{
|
||||
Grocy.Api.Get('chores/' + choreId + '/printlabel', function(labelData) {
|
||||
if (Grocy.Webhooks.labelprinter !== undefined) {
|
||||
Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, labelData);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
Grocy.Components.BarcodeScanner = {};
|
||||
|
||||
Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted = false;
|
||||
Grocy.Components.BarcodeScanner.CheckCapabilities = async function()
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.CheckCapabilities = async function() {
|
||||
var track = Quagga.CameraAccess.getActiveTrack();
|
||||
var capabilities = {};
|
||||
if (typeof track.getCapabilities === 'function')
|
||||
{
|
||||
if (typeof track.getCapabilities === 'function') {
|
||||
capabilities = track.getCapabilities();
|
||||
}
|
||||
|
||||
// If there is more than 1 camera, show the camera selection
|
||||
var cameras = await Quagga.CameraAccess.enumerateVideoDevices();
|
||||
var cameraSelect = document.querySelector('.cameraSelect-wrapper');
|
||||
if (cameraSelect)
|
||||
{
|
||||
if (cameraSelect) {
|
||||
cameraSelect.style.display = cameras.length > 1 ? 'inline-block' : 'none';
|
||||
}
|
||||
|
||||
|
|
@ -22,29 +19,23 @@ Grocy.Components.BarcodeScanner.CheckCapabilities = async function()
|
|||
var canTorch = typeof capabilities.torch === 'boolean' && capabilities.torch
|
||||
// Remove the torch button, if either the device can not torch or AutoTorchOn is set.
|
||||
var node = document.querySelector('.torch');
|
||||
if (node)
|
||||
{
|
||||
if (node) {
|
||||
node.style.display = canTorch && !Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA ? 'inline-block' : 'none';
|
||||
}
|
||||
// If AutoTorchOn is set, turn on the torch.
|
||||
if (canTorch && Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA)
|
||||
{
|
||||
if (canTorch && Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA) {
|
||||
Grocy.Components.BarcodeScanner.TorchOn(track);
|
||||
}
|
||||
|
||||
// Reduce the height of the video, if it's higher than then the viewport
|
||||
if (!Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted)
|
||||
{
|
||||
if (!Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted) {
|
||||
var bc = document.getElementById('barcodescanner-container');
|
||||
if (bc)
|
||||
{
|
||||
if (bc) {
|
||||
var bcAspectRatio = bc.offsetWidth / bc.offsetHeight;
|
||||
var settings = track.getSettings();
|
||||
if (bcAspectRatio > settings.aspectRatio)
|
||||
{
|
||||
if (bcAspectRatio > settings.aspectRatio) {
|
||||
var v = document.querySelector('#barcodescanner-livestream video')
|
||||
if (v)
|
||||
{
|
||||
if (v) {
|
||||
var c = document.querySelector('#barcodescanner-livestream canvas')
|
||||
var newWidth = v.clientWidth / bcAspectRatio * settings.aspectRatio + 'px';
|
||||
v.style.width = newWidth;
|
||||
|
|
@ -57,8 +48,7 @@ Grocy.Components.BarcodeScanner.CheckCapabilities = async function()
|
|||
}
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.StartScanning = function()
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.StartScanning = function() {
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesCount = 0;
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesErrorCount = 0;
|
||||
|
||||
|
|
@ -69,7 +59,9 @@ Grocy.Components.BarcodeScanner.StartScanning = function()
|
|||
target: document.querySelector("#barcodescanner-livestream"),
|
||||
constraints: {
|
||||
facingMode: "environment",
|
||||
...(window.localStorage.getItem('cameraId') && { deviceId: window.localStorage.getItem('cameraId') }) // If preferred cameraId is set, request to use that specific camera
|
||||
...(window.localStorage.getItem('cameraId') && {
|
||||
deviceId: window.localStorage.getItem('cameraId')
|
||||
}) // If preferred cameraId is set, request to use that specific camera
|
||||
}
|
||||
},
|
||||
locator: {
|
||||
|
|
@ -115,15 +107,12 @@ Grocy.Components.BarcodeScanner.StartScanning = function()
|
|||
}
|
||||
},
|
||||
locate: true
|
||||
}, function(error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
}, function(error) {
|
||||
if (error) {
|
||||
Grocy.FrontendHelpers.ShowGenericError("Error while initializing the barcode scanning library", error.message);
|
||||
toastr.info(__t("Camera access is only possible when supported and allowed by your browser and when grocy is served via a secure (https://) connection"));
|
||||
window.localStorage.removeItem("cameraId");
|
||||
setTimeout(function()
|
||||
{
|
||||
setTimeout(function() {
|
||||
bootbox.hideAll();
|
||||
}, 500);
|
||||
return;
|
||||
|
|
@ -135,8 +124,7 @@ Grocy.Components.BarcodeScanner.StartScanning = function()
|
|||
});
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.StopScanning = function()
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.StopScanning = function() {
|
||||
Quagga.stop();
|
||||
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesCount = 0;
|
||||
|
|
@ -145,76 +133,77 @@ Grocy.Components.BarcodeScanner.StopScanning = function()
|
|||
bootbox.hideAll();
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.TorchOn = function(track)
|
||||
{
|
||||
if (track)
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.TorchOn = function(track) {
|
||||
if (track) {
|
||||
track.applyConstraints({
|
||||
advanced: [
|
||||
{
|
||||
advanced: [{
|
||||
torch: true
|
||||
}
|
||||
]
|
||||
}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Quagga.onDetected(function(result)
|
||||
{
|
||||
$.each(result.codeResult.decodedCodes, function(id, error)
|
||||
{
|
||||
if (error.error != undefined)
|
||||
{
|
||||
Quagga.onDetected(function(result) {
|
||||
$.each(result.codeResult.decodedCodes, function(id, error) {
|
||||
if (error.error != undefined) {
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesCount++;
|
||||
Grocy.Components.BarcodeScanner.DecodedCodesErrorCount += parseFloat(error.error);
|
||||
}
|
||||
});
|
||||
|
||||
if ((Grocy.Components.BarcodeScanner.DecodedCodesErrorCount / Grocy.Components.BarcodeScanner.DecodedCodesCount < 0.15) ||
|
||||
(Grocy.Components.BarcodeScanner.DecodedCodesErrorCount == 0 && Grocy.Components.BarcodeScanner.DecodedCodesCount == 0 && result.codeResult.code.length != 0))
|
||||
{
|
||||
(Grocy.Components.BarcodeScanner.DecodedCodesErrorCount == 0 && Grocy.Components.BarcodeScanner.DecodedCodesCount == 0 && result.codeResult.code.length != 0)) {
|
||||
Grocy.Components.BarcodeScanner.StopScanning();
|
||||
$(document).trigger("Grocy.BarcodeScanned", [result.codeResult.code, Grocy.Components.BarcodeScanner.CurrentTarget]);
|
||||
}
|
||||
});
|
||||
|
||||
Quagga.onProcessed(function(result)
|
||||
{
|
||||
Quagga.onProcessed(function(result) {
|
||||
var drawingCtx = Quagga.canvas.ctx.overlay;
|
||||
var drawingCanvas = Quagga.canvas.dom.overlay;
|
||||
|
||||
if (result)
|
||||
{
|
||||
if (result.boxes)
|
||||
{
|
||||
if (result) {
|
||||
if (result.boxes) {
|
||||
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
|
||||
result.boxes.filter(function(box)
|
||||
{
|
||||
result.boxes.filter(function(box) {
|
||||
return box !== result.box;
|
||||
}).forEach(function(box)
|
||||
{
|
||||
Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "yellow", lineWidth: 4 });
|
||||
}).forEach(function(box) {
|
||||
Quagga.ImageDebug.drawPath(box, {
|
||||
x: 0,
|
||||
y: 1
|
||||
}, drawingCtx, {
|
||||
color: "yellow",
|
||||
lineWidth: 4
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (result.box)
|
||||
{
|
||||
Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 4 });
|
||||
if (result.box) {
|
||||
Quagga.ImageDebug.drawPath(result.box, {
|
||||
x: 0,
|
||||
y: 1
|
||||
}, drawingCtx, {
|
||||
color: "green",
|
||||
lineWidth: 4
|
||||
});
|
||||
}
|
||||
|
||||
if (result.codeResult && result.codeResult.code)
|
||||
{
|
||||
Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: "red", lineWidth: 4 });
|
||||
if (result.codeResult && result.codeResult.code) {
|
||||
Quagga.ImageDebug.drawPath(result.line, {
|
||||
x: 'x',
|
||||
y: 'y'
|
||||
}, drawingCtx, {
|
||||
color: "red",
|
||||
lineWidth: 4
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("click", "#barcodescanner-start-button", async function(e)
|
||||
{
|
||||
$(document).on("click", "#barcodescanner-start-button", async function(e) {
|
||||
e.preventDefault();
|
||||
var inputElement = $(e.currentTarget).prev();
|
||||
if (inputElement.hasAttr("disabled"))
|
||||
{
|
||||
if (inputElement.hasAttr("disabled")) {
|
||||
// Do nothing and disable the barcode scanner start button
|
||||
$(e.currentTarget).addClass("disabled");
|
||||
return;
|
||||
|
|
@ -225,8 +214,7 @@ $(document).on("click", "#barcodescanner-start-button", async function(e)
|
|||
var dialog = bootbox.dialog({
|
||||
message: '<div id="barcodescanner-container" class="col"><div id="barcodescanner-livestream"></div></div>',
|
||||
title: __t('Scan a barcode'),
|
||||
onEscape: function()
|
||||
{
|
||||
onEscape: function() {
|
||||
Grocy.Components.BarcodeScanner.StopScanning();
|
||||
},
|
||||
size: 'big',
|
||||
|
|
@ -236,8 +224,7 @@ $(document).on("click", "#barcodescanner-start-button", async function(e)
|
|||
torch: {
|
||||
label: '<i class="far fa-lightbulb"></i>',
|
||||
className: 'btn-warning responsive-button torch',
|
||||
callback: function()
|
||||
{
|
||||
callback: function() {
|
||||
Grocy.Components.BarcodeScanner.TorchOn(Quagga.CameraAccess.getActiveTrack());
|
||||
return false;
|
||||
}
|
||||
|
|
@ -245,8 +232,7 @@ $(document).on("click", "#barcodescanner-start-button", async function(e)
|
|||
cancel: {
|
||||
label: __t('Cancel'),
|
||||
className: 'btn-secondary responsive-button',
|
||||
callback: function()
|
||||
{
|
||||
callback: function() {
|
||||
Grocy.Components.BarcodeScanner.StopScanning();
|
||||
}
|
||||
}
|
||||
|
|
@ -258,8 +244,7 @@ $(document).on("click", "#barcodescanner-start-button", async function(e)
|
|||
var cameraSelect = document.querySelector('.cameraSelect');
|
||||
|
||||
var cameras = await Quagga.CameraAccess.enumerateVideoDevices();
|
||||
cameras.forEach(camera =>
|
||||
{
|
||||
cameras.forEach(camera => {
|
||||
var option = document.createElement("option");
|
||||
option.text = camera.label ? camera.label : camera.deviceId; // Use camera label if it exists, else show device id
|
||||
option.value = camera.deviceId;
|
||||
|
|
@ -269,8 +254,7 @@ $(document).on("click", "#barcodescanner-start-button", async function(e)
|
|||
// Set initial value to preferred camera if one exists - and if not, start out empty
|
||||
cameraSelect.value = window.localStorage.getItem('cameraId');
|
||||
|
||||
cameraSelect.onchange = function()
|
||||
{
|
||||
cameraSelect.onchange = function() {
|
||||
window.localStorage.setItem('cameraId', cameraSelect.value);
|
||||
Quagga.stop();
|
||||
Grocy.Components.BarcodeScanner.StartScanning();
|
||||
|
|
@ -280,29 +264,25 @@ $(document).on("click", "#barcodescanner-start-button", async function(e)
|
|||
});
|
||||
|
||||
Grocy.Components.BarcodeScanner.InitDone = false;
|
||||
Grocy.Components.BarcodeScanner.Init = function()
|
||||
{
|
||||
if (Grocy.Components.BarcodeScanner.InitDone)
|
||||
{
|
||||
Grocy.Components.BarcodeScanner.Init = function() {
|
||||
if (Grocy.Components.BarcodeScanner.InitDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(".barcodescanner-input:visible").each(function()
|
||||
{
|
||||
if ($(this).hasAttr("disabled"))
|
||||
{
|
||||
$(this).after('<a id="barcodescanner-start-button" class="btn btn-sm btn-primary text-white disabled" data-target="' + $(this).attr("data-target") + '"><i class="fas fa-camera"></i></a>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$(this).after('<a id="barcodescanner-start-button" class="btn btn-sm btn-primary text-white" data-target="' + $(this).attr("data-target") + '"><i class="fas fa-camera"></i></a>');
|
||||
$(".barcodescanner-input:visible").each(function() {
|
||||
var isSelect2 = $(this).hasClass('select2') && $(this).parent().hasClass('input-group');
|
||||
var html = '<a id="barcodescanner-start-button" class="btn' + (isSelect2 ? '' : ' btn-sm') + ' btn-primary text-white' + ($(this).hasAttr("disabled") ? ' disabled' : '') + '" data-target="' + $(this).attr("data-target") + '"><i class="fas fa-camera"></i></a>';
|
||||
if (isSelect2) {
|
||||
html = '<div class="input-group-prepend" id="barcodescanner-start-button-container">' + html + '</div>'
|
||||
$(this).parent().prepend(html);
|
||||
} else {
|
||||
$(this).after(html);
|
||||
}
|
||||
|
||||
Grocy.Components.BarcodeScanner.InitDone = true;
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
setTimeout(function() {
|
||||
Grocy.Components.BarcodeScanner.Init();
|
||||
}, 50);
|
||||
|
|
|
|||
|
|
@ -1,195 +1,341 @@
|
|||
Grocy.Components.ProductPicker = {};
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker = function() {
|
||||
return $('#product_id');
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.GetInputElement = function()
|
||||
{
|
||||
return $('#product_id_text_input');
|
||||
Grocy.Components.ProductPicker.GetValue = function() {
|
||||
return this.GetPicker().val();
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.GetValue = function()
|
||||
{
|
||||
return $('#product_id').val();
|
||||
Grocy.Components.ProductPicker.GetOption = function(key) {
|
||||
return this.GetPicker().parents('.form-group').data(key);
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().val(value);
|
||||
Grocy.Components.ProductPicker.GetInputElement().trigger('change');
|
||||
Grocy.Components.ProductPicker.GetState = function(key) {
|
||||
return this.GetPicker().data(key);
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.SetId = function(value)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().val(value);
|
||||
Grocy.Components.ProductPicker.GetPicker().data('combobox').refresh();
|
||||
Grocy.Components.ProductPicker.GetInputElement().trigger('change');
|
||||
Grocy.Components.ProductPicker.SetState = function(key, value) {
|
||||
this.GetPicker().data(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.Clear = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
Grocy.Components.ProductPicker.SetId(null);
|
||||
Grocy.Components.ProductPicker.SetId = function(value, callback) {
|
||||
if (this.GetPicker().find('option[value="' + value + '"]').length) {
|
||||
this.GetPicker().val(value).trigger('change');
|
||||
} else {
|
||||
Grocy.Api.Get('objects/products/' + encodeURIComponent(value),
|
||||
function(result) {
|
||||
var option = new Option(result.name, value, true, true);
|
||||
if (typeof callback === 'function') {
|
||||
Grocy.Components.ProductPicker.GetPicker().one('change.select2', callback);
|
||||
}
|
||||
Grocy.Components.ProductPicker.GetPicker().append(option).trigger('change').select2('close');
|
||||
},
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.InProductAddWorkflow = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.Clear = function() {
|
||||
this.GetPicker().val(null).trigger('change');
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.InProductAddWorkflow = function() {
|
||||
return GetUriParam('flow') == "InplaceNewProductWithName";
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.InProductModifyWorkflow = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.InProductModifyWorkflow = function() {
|
||||
return GetUriParam('flow') == "InplaceAddBarcodeToExistingProduct";
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.InAnyFlow = function()
|
||||
{
|
||||
return Grocy.Components.ProductPicker.InProductAddWorkflow() || Grocy.Components.ProductPicker.InProductModifyWorkflow();
|
||||
Grocy.Components.ProductPicker.InAnyFlow = function() {
|
||||
return GetUriParam('flow') !== undefined;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.FinishFlow = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.FinishFlow = function() {
|
||||
RemoveUriParam("flow");
|
||||
RemoveUriParam("barcode");
|
||||
RemoveUriParam("product-name");
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.ShowCustomError = function(text)
|
||||
{
|
||||
Grocy.Components.ProductPicker.ShowCustomError = function(text) {
|
||||
var element = $("#custom-productpicker-error");
|
||||
element.text(text);
|
||||
element.removeClass("d-none");
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.HideCustomError = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.HideCustomError = function() {
|
||||
$("#custom-productpicker-error").addClass("d-none");
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.Disable = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().attr("disabled", "");
|
||||
Grocy.Components.ProductPicker.Disable = function() {
|
||||
this.GetPicker().prop("disabled", true);
|
||||
$("#barcodescanner-start-button").attr("disabled", "");
|
||||
$("#barcodescanner-start-button").addClass("disabled");
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.Enable = function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().removeAttr("disabled");
|
||||
Grocy.Components.ProductPicker.Enable = function() {
|
||||
this.GetPicker().prop("disabled", false);
|
||||
$("#barcodescanner-start-button").removeAttr("disabled");
|
||||
$("#barcodescanner-start-button").removeClass("disabled");
|
||||
return this;
|
||||
}
|
||||
|
||||
$('.product-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4',
|
||||
clearIfNoMatch: false
|
||||
Grocy.Components.ProductPicker.Require = function() {
|
||||
this.GetPicker().prop("required", true);
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.Optional = function() {
|
||||
this.GetPicker().prop("required", false);
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.Focus = function() {
|
||||
this.GetPicker().select2('open');
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.Validate = function() {
|
||||
this.GetPicker().trigger('change');
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.OnChange = function(eventHandler) {
|
||||
this.GetPicker().on('change', eventHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.Search = function(term, callback) {
|
||||
var $picker = this.GetPicker();
|
||||
var doSearch = function() {
|
||||
var $search = $picker.data('select2').dropdown.$search || $picker.data('select2').selection.$search;
|
||||
$search.val(term).trigger('input');
|
||||
// must wait for debounce before listening for 'results:all' event
|
||||
setTimeout(function() {
|
||||
$picker.one('Grocy.ResultsUpdated', function() {
|
||||
if (typeof callback === 'function') {
|
||||
$picker.one('select2:close', callback);
|
||||
}
|
||||
$picker.select2('close');
|
||||
});
|
||||
}, 150);
|
||||
};
|
||||
if ($picker.data('select2').isOpen()) {
|
||||
doSearch();
|
||||
} else {
|
||||
$picker.one('select2:open', doSearch);
|
||||
$picker.select2('open');
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.IsOpen = function() {
|
||||
return this.GetPicker().parent().find('.select2-container').hasClass('select2-container--open');
|
||||
}
|
||||
|
||||
// initialize Select2 product picker
|
||||
var lastProductSearchTerm = '';
|
||||
Grocy.Components.ProductPicker.GetPicker().select2({
|
||||
selectOnClose: true,
|
||||
ajax: {
|
||||
delay: 150,
|
||||
transport: function(params, success, failure) {
|
||||
var results_per_page = 10;
|
||||
var page = params.data.page || 1;
|
||||
var term = params.data.term || "";
|
||||
var termIsGrocycode = term.startsWith("grcy");
|
||||
lastProductSearchTerm = term;
|
||||
|
||||
// reset grocycode/barcode state
|
||||
Grocy.Components.ProductPicker.SetState('barcode', 'null');
|
||||
Grocy.Components.ProductPicker.SetState('grocycode', false);
|
||||
|
||||
// build search queries
|
||||
var baseQuery = Grocy.Components.ProductPicker.GetOption('products-query').split('&');
|
||||
baseQuery.push('limit=' + encodeURIComponent(results_per_page));
|
||||
baseQuery.push('offset=' + encodeURIComponent((page - 1) * results_per_page));
|
||||
var queries = [];
|
||||
if (term.length > 0) {
|
||||
queries = [
|
||||
// search product fields (name, etc.)
|
||||
baseQuery.concat('search=' + encodeURIComponent(term)).join('&'),
|
||||
];
|
||||
// grocycode handling
|
||||
if (termIsGrocycode) {
|
||||
var gc = term.split(":");
|
||||
if (gc[1] == "p") {
|
||||
queries.push(baseQuery.concat('query%5B%5D=id%3D' + encodeURIComponent(gc[2])).join('&'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
queries = [baseQuery.join('&')];
|
||||
}
|
||||
|
||||
// execute all queries in parallel, return first non-empty response
|
||||
var complete = 0;
|
||||
var responded = false;
|
||||
var xhrs = [];
|
||||
var handleEmptyResponse = function() {
|
||||
if (responded || complete < xhrs.length) return;
|
||||
success({
|
||||
results: [],
|
||||
pagination: {
|
||||
more: false
|
||||
}
|
||||
});
|
||||
};
|
||||
var handleResponse = function(results, meta, cur_xhr) {
|
||||
// track complete queries
|
||||
complete++;
|
||||
|
||||
// abort if we already responded
|
||||
if (responded) return;
|
||||
|
||||
// abort if no results
|
||||
if (results.length === 0) {
|
||||
handleEmptyResponse();
|
||||
return;
|
||||
}
|
||||
|
||||
// track whether we have responded
|
||||
responded = true;
|
||||
|
||||
// abort all other queries
|
||||
xhrs.forEach(function(xhr) {
|
||||
if (xhr !== cur_xhr) xhr.abort();
|
||||
});
|
||||
|
||||
// update grocycode/barcode state
|
||||
Grocy.Components.ProductPicker.SetState('grocycode', termIsGrocycode);
|
||||
Grocy.Components.ProductPicker.SetState('barcode', term);
|
||||
|
||||
success({
|
||||
results: results.map(function(result) {
|
||||
return {
|
||||
id: result.id,
|
||||
text: result.name
|
||||
};
|
||||
}),
|
||||
pagination: {
|
||||
more: page * results_per_page < meta.recordsFiltered
|
||||
}
|
||||
});
|
||||
};
|
||||
var handleErrors = function(xhr) {
|
||||
console.error(xhr);
|
||||
|
||||
// track complete queries
|
||||
complete++;
|
||||
|
||||
// abort if we already responded
|
||||
if (responded) return;
|
||||
|
||||
handleEmptyResponse();
|
||||
};
|
||||
if (term.length > 0) {
|
||||
xhrs.push(Grocy.Api.Get('objects/product_barcodes?limit=1&query%5B%5D=barcode%3D' + encodeURIComponent(term),
|
||||
function(results, meta) {
|
||||
// track complete queries
|
||||
complete++;
|
||||
|
||||
// abort if we already responded
|
||||
if (responded) return;
|
||||
|
||||
// abort if no results
|
||||
if (results.length === 0) {
|
||||
handleEmptyResponse();
|
||||
return;
|
||||
}
|
||||
|
||||
var cur_xhr = Grocy.Api.Get('objects/products?' + baseQuery.concat('query%5B%5D=id%3D' + encodeURIComponent(results[0].product_id)).join('&'),
|
||||
function(results, meta) {
|
||||
handleResponse(results, meta, cur_xhr);
|
||||
},
|
||||
handleErrors
|
||||
);
|
||||
xhrs.push(cur_xhr);
|
||||
},
|
||||
handleErrors
|
||||
));
|
||||
}
|
||||
xhrs = xhrs.concat(queries.map(function(query) {
|
||||
var cur_xhr = Grocy.Api.Get('objects/products' + (query.length > 0 ? '?' + query : ''),
|
||||
function(results, meta) {
|
||||
handleResponse(results, meta, cur_xhr);
|
||||
},
|
||||
handleErrors
|
||||
);
|
||||
return cur_xhr;
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var prefillProduct = GetUriParam('product-name');
|
||||
var prefillProduct2 = Grocy.Components.ProductPicker.GetPicker().parent().data('prefill-by-name').toString();
|
||||
if (!prefillProduct2.isEmpty())
|
||||
{
|
||||
prefillProduct = prefillProduct2;
|
||||
}
|
||||
if (typeof prefillProduct !== "undefined")
|
||||
{
|
||||
var possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + prefillProduct + "\"]").first();
|
||||
if (possibleOptionElement.length === 0)
|
||||
{
|
||||
possibleOptionElement = $("#product_id option:contains(\"" + prefillProduct + "\")").first();
|
||||
}
|
||||
// forward 'results:all' event
|
||||
Grocy.Components.ProductPicker.GetPicker().data('select2').on('results:all', function(data) {
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('Grocy.ResultsUpdated', data);
|
||||
});
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
$('#product_id').val(possibleOptionElement.val());
|
||||
$('#product_id').data('combobox').refresh();
|
||||
$('#product_id').trigger('change');
|
||||
// handle barcode scanning
|
||||
$(document).on("Grocy.BarcodeScanned", function(e, barcode, target) {
|
||||
// check that the barcode scan is for the product picker
|
||||
if (!(target == "@productpicker" || target == "undefined" || target == undefined)) return;
|
||||
|
||||
var nextInputElement = $(Grocy.Components.ProductPicker.GetPicker().parent().data('next-input-selector').toString());
|
||||
nextInputElement.focus();
|
||||
}
|
||||
}
|
||||
Grocy.Components.ProductPicker.Search(barcode);
|
||||
});
|
||||
|
||||
var prefillProductId = GetUriParam("product");
|
||||
var prefillProductId2 = Grocy.Components.ProductPicker.GetPicker().parent().data('prefill-by-id').toString();
|
||||
if (!prefillProductId2.isEmpty())
|
||||
{
|
||||
prefillProductId = prefillProductId2;
|
||||
}
|
||||
if (typeof prefillProductId !== "undefined")
|
||||
{
|
||||
$('#product_id').val(prefillProductId);
|
||||
$('#product_id').data('combobox').refresh();
|
||||
$('#product_id').trigger('change');
|
||||
// fix placement of bootbox footer buttons
|
||||
$(document).on("shown.bs.modal", function() {
|
||||
$(".modal-footer").addClass("d-block").addClass("d-sm-flex");
|
||||
$(".modal-footer").find("button").addClass("mt-2").addClass("mt-sm-0");
|
||||
});
|
||||
|
||||
var nextInputElement = $(Grocy.Components.ProductPicker.GetPicker().parent().data('next-input-selector').toString());
|
||||
nextInputElement.focus();
|
||||
}
|
||||
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct")
|
||||
{
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct") {
|
||||
$('#InplaceAddBarcodeToExistingProduct').text(GetUriParam("barcode"));
|
||||
$('#flow-info-InplaceAddBarcodeToExistingProduct').removeClass('d-none');
|
||||
$('#barcode-lookup-disabled-hint').removeClass('d-none');
|
||||
$('#barcode-lookup-hint').addClass('d-none');
|
||||
}
|
||||
|
||||
// prefill by name
|
||||
var prefillProduct = Grocy.Components.ProductPicker.GetOption('prefill-by-name') || GetUriParam('product-name');
|
||||
if (typeof prefillProduct === 'string' && !prefillProduct.isEmpty()) {
|
||||
Grocy.Components.ProductPicker.Search(prefillProduct, function() {
|
||||
$(Grocy.Components.ProductPicker.GetOption('next-input-selector')).trigger('focus');
|
||||
});
|
||||
}
|
||||
|
||||
// prefill by ID
|
||||
var prefillProduct = Grocy.Components.ProductPicker.GetOption('prefill-by-id') || GetUriParam('product');
|
||||
if (typeof prefillProduct === 'string' && !prefillProduct.isEmpty()) {
|
||||
Grocy.Components.ProductPicker.SetId(prefillProduct, function() {
|
||||
$(Grocy.Components.ProductPicker.GetOption('next-input-selector')).trigger('focus');
|
||||
});
|
||||
}
|
||||
|
||||
// open create product/barcode dialog if no results
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
$('#product_id_text_input').on('blur', function(e)
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
$('#product_id').attr("barcode", "null");
|
||||
Grocy.Components.ProductPicker.GetPicker().on('select2:close', function() {
|
||||
if (Grocy.Components.ProductPicker.PopupOpen || Grocy.Components.ProductPicker.GetPicker().select2('data').length > 0) return;
|
||||
|
||||
var input = $('#product_id_text_input').val().toString();
|
||||
var possibleOptionElement = [];
|
||||
|
||||
// grocycode handling
|
||||
if (input.startsWith("grcy"))
|
||||
{
|
||||
var gc = input.split(":");
|
||||
if (gc[1] == "p")
|
||||
{
|
||||
possibleOptionElement = $("#product_id option[value=\"" + gc[2] + "\"]").first();
|
||||
$("#product_id").data("grocycode", true);
|
||||
}
|
||||
}
|
||||
else // Normal product barcode handling
|
||||
{
|
||||
possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + input + ",\"]").first();
|
||||
}
|
||||
|
||||
if (GetUriParam('flow') === undefined && input.length > 0 && possibleOptionElement.length > 0)
|
||||
{
|
||||
$('#product_id').val(possibleOptionElement.val());
|
||||
$('#product_id').attr("barcode", input);
|
||||
$('#product_id').data('combobox').refresh();
|
||||
$('#product_id').trigger('change');
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.PopupOpen === true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var optionElement = $("#product_id option:contains(\"" + input + "\")").first();
|
||||
if (input.length > 0 && optionElement.length === 0 && GetUriParam('flow') === undefined && Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-all-product-workflows').toString() === "false")
|
||||
{
|
||||
var addProductWorkflowsAdditionalCssClasses = "";
|
||||
if (Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-add-product-workflows').toString() === "true")
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetOption('disallow-add-product-workflows')) {
|
||||
addProductWorkflowsAdditionalCssClasses = "d-none";
|
||||
}
|
||||
|
||||
var embedded = "";
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
embedded = "embedded";
|
||||
}
|
||||
|
||||
|
|
@ -197,58 +343,51 @@ $('#product_id_text_input').on('blur', function(e)
|
|||
cancel: {
|
||||
label: __t('Cancel'),
|
||||
className: 'btn-secondary responsive-button',
|
||||
callback: function()
|
||||
{
|
||||
callback: function() {
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
Grocy.Components.ProductPicker.Clear();
|
||||
}
|
||||
},
|
||||
addnewproduct: {
|
||||
label: '<strong>P</strong> ' + __t('Add as new product'),
|
||||
className: 'btn-success add-new-product-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses,
|
||||
callback: function()
|
||||
{
|
||||
callback: function() {
|
||||
// Not the best place here - this is only relevant when this flow is started from the shopping list item form
|
||||
// (to select the correct shopping list on return)
|
||||
if (GetUriParam("list") !== undefined)
|
||||
{
|
||||
if (GetUriParam("list") !== undefined) {
|
||||
embedded += "&list=" + GetUriParam("list");
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
window.location.href = U('/product/new?flow=InplaceNewProductWithName&name=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceNewProductWithName&" + embedded) + "&" + embedded);
|
||||
window.location.href = U('/product/new?flow=InplaceNewProductWithName&name=' + encodeURIComponent(lastProductSearchTerm) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceNewProductWithName&" + embedded) + "&" + embedded);
|
||||
}
|
||||
},
|
||||
addbarcode: {
|
||||
label: '<strong>B</strong> ' + __t('Add as barcode to existing product'),
|
||||
className: 'btn-info add-new-barcode-dialog-button responsive-button',
|
||||
callback: function()
|
||||
{
|
||||
callback: function() {
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
window.location.href = U(Grocy.CurrentUrlRelative + '?flow=InplaceAddBarcodeToExistingProduct&barcode=' + encodeURIComponent(input) + "&" + embedded);
|
||||
window.location.href = U(Grocy.CurrentUrlRelative + '?flow=InplaceAddBarcodeToExistingProduct&barcode=' + encodeURIComponent(lastProductSearchTerm) + "&" + embedded);
|
||||
}
|
||||
},
|
||||
addnewproductwithbarcode: {
|
||||
label: '<strong>A</strong> ' + __t('Add as new product and prefill barcode'),
|
||||
className: 'btn-warning add-new-product-with-barcode-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses,
|
||||
callback: function()
|
||||
{
|
||||
callback: function() {
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
window.location.href = U('/product/new?flow=InplaceNewProductWithBarcode&barcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceAddBarcodeToExistingProduct&barcode=" + input + "&" + embedded) + "&" + embedded);
|
||||
window.location.href = U('/product/new?flow=InplaceNewProductWithBarcode&barcode=' + encodeURIComponent(lastProductSearchTerm) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceAddBarcodeToExistingProduct&barcode=" + lastProductSearchTerm + "&" + embedded) + "&" + embedded);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_DISABLE_BROWSER_BARCODE_CAMERA_SCANNING)
|
||||
{
|
||||
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_DISABLE_BROWSER_BARCODE_CAMERA_SCANNING) {
|
||||
buttons.retrycamerascanning = {
|
||||
label: '<strong>C</strong> <i class="fas fa-camera"></i>',
|
||||
className: 'btn-primary responsive-button retry-camera-scanning-button',
|
||||
callback: function()
|
||||
{
|
||||
callback: function() {
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
$("#barcodescanner-start-button").click();
|
||||
Grocy.Components.ProductPicker.Clear();
|
||||
$("#barcodescanner-start-button").trigger('click');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -259,119 +398,60 @@ $('#product_id_text_input').on('blur', function(e)
|
|||
// otherwise an error validation message that the product is not in stock
|
||||
var existsAsProduct = false;
|
||||
var existsAsBarcode = false;
|
||||
Grocy.Api.Get('objects/product_barcodes?query[]=barcode=' + input,
|
||||
function(barcodeResult)
|
||||
{
|
||||
if (barcodeResult.length > 0)
|
||||
{
|
||||
Grocy.Api.Get('objects/product_barcodes?query[]=barcode=' + lastProductSearchTerm,
|
||||
function(barcodeResult) {
|
||||
if (barcodeResult.length > 0) {
|
||||
existsAsProduct = true;
|
||||
}
|
||||
|
||||
Grocy.Api.Get('objects/products?query[]=name=' + input,
|
||||
function(productResult)
|
||||
{
|
||||
if (productResult.length > 0)
|
||||
{
|
||||
Grocy.Api.Get('objects/products?query[]=name=' + lastProductSearchTerm,
|
||||
function(productResult) {
|
||||
if (productResult.length > 0) {
|
||||
existsAsProduct = true;
|
||||
}
|
||||
|
||||
if (!existsAsBarcode && !existsAsProduct)
|
||||
{
|
||||
if (!existsAsBarcode && !existsAsProduct) {
|
||||
Grocy.Components.ProductPicker.PopupOpen = true;
|
||||
bootbox.dialog({
|
||||
message: __t('"%s" could not be resolved to a product, how do you want to proceed?', input),
|
||||
message: __t('"%s" could not be resolved to a product, how do you want to proceed?', lastProductSearchTerm),
|
||||
title: __t('Create or assign product'),
|
||||
onEscape: function()
|
||||
{
|
||||
onEscape: function() {
|
||||
Grocy.Components.ProductPicker.PopupOpen = false;
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
Grocy.Components.ProductPicker.Clear();
|
||||
},
|
||||
size: 'large',
|
||||
backdrop: true,
|
||||
closeButton: false,
|
||||
buttons: buttons
|
||||
}).on('keypress', function(e)
|
||||
{
|
||||
if (e.key === 'B' || e.key === 'b')
|
||||
{
|
||||
$('.add-new-barcode-dialog-button').not(".d-none").click();
|
||||
}).on('keypress', function(e) {
|
||||
if (e.key === 'B' || e.key === 'b') {
|
||||
$('.add-new-barcode-dialog-button').not(".d-none").trigger('click');
|
||||
}
|
||||
if (e.key === 'p' || e.key === 'P')
|
||||
{
|
||||
$('.add-new-product-dialog-button').not(".d-none").click();
|
||||
if (e.key === 'p' || e.key === 'P') {
|
||||
$('.add-new-product-dialog-button').not(".d-none").trigger('click');
|
||||
}
|
||||
if (e.key === 'a' || e.key === 'A')
|
||||
{
|
||||
$('.add-new-product-with-barcode-dialog-button').not(".d-none").click();
|
||||
if (e.key === 'a' || e.key === 'A') {
|
||||
$('.add-new-product-with-barcode-dialog-button').not(".d-none").trigger('click');
|
||||
}
|
||||
if (e.key === 'c' || e.key === 'C')
|
||||
{
|
||||
$('.retry-camera-scanning-button').not(".d-none").click();
|
||||
if (e.key === 'c' || e.key === 'C') {
|
||||
$('.retry-camera-scanning-button').not(".d-none").trigger('click');
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.Components.ProductAmountPicker.Reset();
|
||||
Grocy.Components.ProductPicker.Clear();
|
||||
Grocy.FrontendHelpers.ValidateForm('consume-form');
|
||||
Grocy.Components.ProductPicker.ShowCustomError(__t('This product is not in stock'));
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("Grocy.BarcodeScanned", function(e, barcode, target)
|
||||
{
|
||||
if (!(target == "@productpicker" || target == "undefined" || target == undefined)) // Default target
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't know why the blur event does not fire immediately ... this works...
|
||||
Grocy.Components.ProductPicker.GetInputElement().focusout();
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.GetInputElement().blur();
|
||||
|
||||
Grocy.Components.ProductPicker.GetInputElement().val(barcode);
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focusout();
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.GetInputElement().blur();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
$(document).on("shown.bs.modal", function(e)
|
||||
{
|
||||
$(".modal-footer").addClass("d-block").addClass("d-sm-flex");
|
||||
$(".modal-footer").find("button").addClass("mt-2").addClass("mt-sm-0");
|
||||
})
|
||||
|
||||
// Make that ENTER behaves the same like TAB (trigger blur to start workflows, but only when the dropdown is not opened)
|
||||
$('#product_id_text_input').keydown(function(event)
|
||||
{
|
||||
if (event.keyCode === 13) // Enter
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$("#product_id_text_input").trigger("blur");
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
$('#save-consume-button').on('click', function(e)
|
||||
{
|
||||
$('#save-consume-button').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if ($(".combobox-menu-visible").length)
|
||||
{
|
||||
if ($(".combobox-menu-visible").length) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -18,51 +16,42 @@
|
|||
jsonData.spoiled = $('#spoiled').is(':checked');
|
||||
jsonData.allow_subproduct_substitution = true;
|
||||
|
||||
if ($("#use_specific_stock_entry").is(":checked"))
|
||||
{
|
||||
if ($("#use_specific_stock_entry").is(":checked")) {
|
||||
jsonData.stock_entry_id = jsonForm.specific_stock_entry;
|
||||
}
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) {
|
||||
jsonData.location_id = $("#location_id").val();
|
||||
}
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_RECIPES && Grocy.Components.RecipePicker.GetValue().toString().length > 0)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_RECIPES && Grocy.Components.RecipePicker.GetValue().toString().length > 0) {
|
||||
jsonData.recipe_id = Grocy.Components.RecipePicker.GetValue();
|
||||
}
|
||||
|
||||
var bookingResponse = null;
|
||||
Grocy.Api.Get('stock/products/' + jsonForm.product_id,
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
Grocy.Api.Post(apiUrl, jsonData,
|
||||
function(result)
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_consume_enabled))
|
||||
{
|
||||
function(result) {
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_consume_enabled)) {
|
||||
Grocy.UISound.Success();
|
||||
}
|
||||
|
||||
bookingResponse = result;
|
||||
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct")
|
||||
{
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct") {
|
||||
var jsonDataBarcode = {};
|
||||
jsonDataBarcode.barcode = GetUriParam("barcode");
|
||||
jsonDataBarcode.product_id = jsonForm.product_id;
|
||||
|
||||
Grocy.Api.Post('objects/product_barcodes', jsonDataBarcode,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
$("#flow-info-InplaceAddBarcodeToExistingProduct").addClass("d-none");
|
||||
$('#barcode-lookup-disabled-hint').addClass('d-none');
|
||||
$('#barcode-lookup-hint').removeClass('d-none');
|
||||
window.history.replaceState({}, document.title, U("/consume"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("consume-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response);
|
||||
}
|
||||
|
|
@ -70,28 +59,21 @@
|
|||
}
|
||||
|
||||
$("#specific_stock_entry").find("option").remove().end().append("<option></option>");
|
||||
if ($("#use_specific_stock_entry").is(":checked"))
|
||||
{
|
||||
if ($("#use_specific_stock_entry").is(":checked")) {
|
||||
$("#use_specific_stock_entry").click();
|
||||
}
|
||||
|
||||
if (productDetails.product.enable_tare_weight_handling == 1 && !jsonData.exact_amount)
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1 && !jsonData.exact_amount) {
|
||||
var successMessage = __t('Removed %1$s of %2$s from stock', Math.abs(jsonForm.amount - (parseFloat(productDetails.product.tare_weight) + parseFloat(productDetails.stock_amount))) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockTransaction(\'' + bookingResponse[0].transaction_id + '\')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
var successMessage = __t('Removed %1$s of %2$s from stock', Math.abs(jsonForm.amount) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockTransaction(\'' + bookingResponse[0].transaction_id + '\')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>';
|
||||
}
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
window.parent.postMessage(WindowMessageBag("ProductChanged", jsonForm.product_id), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", successMessage), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.FrontendHelpers.EndUiBusy("consume-form");
|
||||
toastr.success(successMessage);
|
||||
Grocy.Components.ProductPicker.FinishFlow();
|
||||
|
|
@ -99,54 +81,45 @@
|
|||
Grocy.Components.ProductAmountPicker.Reset();
|
||||
$("#display_amount").attr("min", Grocy.DefaultMinAmount);
|
||||
$("#display_amount").removeAttr("max");
|
||||
if (BoolVal(Grocy.UserSettings.stock_default_consume_amount_use_quick_consume_amount))
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.stock_default_consume_amount_use_quick_consume_amount)) {
|
||||
$('#display_amount').val(productDetails.product.quick_consume_amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#display_amount').val(parseFloat(Grocy.UserSettings.stock_default_consume_amount));
|
||||
}
|
||||
RefreshLocaleNumberInput();
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
$("#tare-weight-handling-info").addClass("d-none");
|
||||
Grocy.Components.ProductPicker.Clear();
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_RECIPES)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_RECIPES) {
|
||||
Grocy.Components.RecipePicker.Clear();
|
||||
}
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) {
|
||||
$("#location_id").find("option").remove().end().append("<option></option>");
|
||||
}
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
Grocy.Components.ProductCard.Refresh(jsonForm.product_id);
|
||||
Grocy.FrontendHelpers.ValidateForm('consume-form');
|
||||
$("#consume-exact-amount-group").addClass("d-none");
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response);
|
||||
Grocy.FrontendHelpers.EndUiBusy("consume-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("consume-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$('#save-mark-as-open-button').on('click', function(e)
|
||||
{
|
||||
$('#save-mark-as-open-button').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if ($(".combobox-menu-visible").length)
|
||||
{
|
||||
if ($(".combobox-menu-visible").length) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -159,123 +132,98 @@ $('#save-mark-as-open-button').on('click', function(e)
|
|||
jsonData.amount = jsonForm.amount;
|
||||
jsonData.allow_subproduct_substitution = true;
|
||||
|
||||
if ($("#use_specific_stock_entry").is(":checked"))
|
||||
{
|
||||
if ($("#use_specific_stock_entry").is(":checked")) {
|
||||
jsonData.stock_entry_id = jsonForm.specific_stock_entry;
|
||||
}
|
||||
|
||||
Grocy.Api.Get('stock/products/' + jsonForm.product_id,
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
Grocy.Api.Post(apiUrl, jsonData,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
$("#specific_stock_entry").find("option").remove().end().append("<option></option>");
|
||||
if ($("#use_specific_stock_entry").is(":checked"))
|
||||
{
|
||||
if ($("#use_specific_stock_entry").is(":checked")) {
|
||||
$("#use_specific_stock_entry").click();
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.EndUiBusy("consume-form");
|
||||
toastr.success(__t('Marked %1$s of %2$s as opened', parseFloat(jsonForm.amount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockTransaction(\'' + result[0].transaction_id + '\')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>');
|
||||
toastr.success(__t('Marked %1$s of %2$s as opened', parseFloat(jsonForm.amount).toLocaleString({
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts
|
||||
}) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockTransaction(\'' + result[0].transaction_id + '\')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>');
|
||||
|
||||
if (BoolVal(Grocy.UserSettings.stock_default_consume_amount_use_quick_consume_amount))
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.stock_default_consume_amount_use_quick_consume_amount)) {
|
||||
$('#display_amount').val(productDetails.product.quick_consume_amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#display_amount').val(parseFloat(Grocy.UserSettings.stock_default_consume_amount));
|
||||
}
|
||||
RefreshLocaleNumberInput();
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
Grocy.Components.ProductPicker.Clear();
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
Grocy.FrontendHelpers.ValidateForm('consume-form');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("consume-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("consume-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
});
|
||||
var sumValue = 0;
|
||||
$("#location_id").on('change', function(e)
|
||||
{
|
||||
$("#location_id").on('change', function(e) {
|
||||
var locationId = $(e.target).val();
|
||||
|
||||
$("#specific_stock_entry").find("option").remove().end().append("<option></option>");
|
||||
if ($("#use_specific_stock_entry").is(":checked"))
|
||||
{
|
||||
$("#use_specific_stock_entry").click();
|
||||
if ($("#use_specific_stock_entry").is(":checked")) {
|
||||
$("#use_specific_stock_entry").trigger('click');
|
||||
}
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
OnLocationChange(locationId, GetUriParam('stockId'));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// try to get stock id from grocycode
|
||||
if ($("#product_id").data("grocycode"))
|
||||
{
|
||||
var gc = $("#product_id").attr("barcode").split(":");
|
||||
if (gc.length == 4)
|
||||
{
|
||||
Grocy.Api.Get("stock/products/" + Grocy.Components.ProductPicker.GetValue() + '/entries?query[]=stock_id=' + gc[3],
|
||||
function(stockEntries)
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetState('grocycode')) {
|
||||
var gc = Grocy.Components.ProductPicker.GetState('barcode').split(':');
|
||||
if (gc.length == 4) {
|
||||
Grocy.Api.Get("stock/products/" + Grocy.Components.ProductPicker.GetValue() + '/entries?query%5B%5D=stock_id%3D' + gc[3],
|
||||
function(stockEntries) {
|
||||
OnLocationChange(stockEntries[0].location_id, gc[3]);
|
||||
$('#display_amount').val(stockEntries[0].amount);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
OnLocationChange(locationId, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function OnLocationChange(locationId, stockId)
|
||||
{
|
||||
function OnLocationChange(locationId, stockId) {
|
||||
sumValue = 0;
|
||||
|
||||
if (locationId)
|
||||
{
|
||||
if ($("#location_id").val() != locationId)
|
||||
{
|
||||
if (locationId) {
|
||||
if ($("#location_id").val() != locationId) {
|
||||
$("#location_id").val(locationId);
|
||||
}
|
||||
|
||||
Grocy.Api.Get("stock/products/" + Grocy.Components.ProductPicker.GetValue() + '/entries?include_sub_products=true',
|
||||
function(stockEntries)
|
||||
{
|
||||
stockEntries.forEach(stockEntry =>
|
||||
{
|
||||
function(stockEntries) {
|
||||
stockEntries.forEach(stockEntry => {
|
||||
var openTxt = __t("Not opened");
|
||||
if (stockEntry.open == 1)
|
||||
{
|
||||
if (stockEntry.open == 1) {
|
||||
openTxt = __t("Opened");
|
||||
}
|
||||
|
||||
if (stockEntry.location_id == locationId)
|
||||
{
|
||||
if ($("#specific_stock_entry option[value='" + stockEntry.stock_id + "']").length == 0)
|
||||
{
|
||||
if (stockEntry.location_id == locationId) {
|
||||
if ($("#specific_stock_entry option[value='" + stockEntry.stock_id + "']").length == 0) {
|
||||
$("#specific_stock_entry").append($("<option>", {
|
||||
value: stockEntry.stock_id,
|
||||
amount: stockEntry.amount,
|
||||
|
|
@ -285,8 +233,7 @@ function OnLocationChange(locationId, stockId)
|
|||
|
||||
sumValue = sumValue + parseFloat(stockEntry.amount || 0);
|
||||
|
||||
if (stockEntry.stock_id == stockId)
|
||||
{
|
||||
if (stockEntry.stock_id == stockId) {
|
||||
$("#use_specific_stock_entry").click();
|
||||
$("#specific_stock_entry").val(stockId);
|
||||
}
|
||||
|
|
@ -294,63 +241,51 @@ function OnLocationChange(locationId, stockId)
|
|||
});
|
||||
|
||||
Grocy.Api.Get('stock/products/' + Grocy.Components.ProductPicker.GetValue(),
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
current_productDetails = productDetails;
|
||||
RefreshForm();
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
||||
if (document.getElementById("product_id").getAttribute("barcode") == "null")
|
||||
{
|
||||
if (document.getElementById("product_id").getAttribute("barcode") == "null") {
|
||||
ScanModeSubmit();
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_consume_enabled))
|
||||
{
|
||||
Grocy.Components.ProductPicker.OnChange(function(e) {
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_consume_enabled)) {
|
||||
Grocy.UISound.BarcodeScannerBeep();
|
||||
}
|
||||
|
||||
$("#specific_stock_entry").find("option").remove().end().append("<option></option>");
|
||||
if ($("#use_specific_stock_entry").is(":checked"))
|
||||
{
|
||||
if ($("#use_specific_stock_entry").is(":checked")) {
|
||||
$("#use_specific_stock_entry").click();
|
||||
}
|
||||
$("#location_id").val("");
|
||||
|
||||
var productId = $(e.target).val();
|
||||
|
||||
if (productId)
|
||||
{
|
||||
if (productId) {
|
||||
Grocy.Components.ProductCard.Refresh(productId);
|
||||
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
current_productDetails = productDetails;
|
||||
|
||||
Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id);
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(productDetails.quantity_unit_stock.id);
|
||||
if (BoolVal(Grocy.UserSettings.stock_default_consume_amount_use_quick_consume_amount))
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.stock_default_consume_amount_use_quick_consume_amount)) {
|
||||
$('#display_amount').val(productDetails.product.quick_consume_amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#display_amount').val(parseFloat(Grocy.UserSettings.stock_default_consume_amount));
|
||||
}
|
||||
RefreshLocaleNumberInput();
|
||||
|
|
@ -358,13 +293,10 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
|
||||
$("#location_id").find("option").remove().end().append("<option></option>");
|
||||
Grocy.Api.Get("stock/products/" + productId + '/locations?include_sub_products=true',
|
||||
function(stockLocations)
|
||||
{
|
||||
function(stockLocations) {
|
||||
var setDefault = 0;
|
||||
stockLocations.forEach(stockLocation =>
|
||||
{
|
||||
if (productDetails.location.id == stockLocation.location_id)
|
||||
{
|
||||
stockLocations.forEach(stockLocation => {
|
||||
if (productDetails.location.id == stockLocation.location_id) {
|
||||
$("#location_id").append($("<option>", {
|
||||
value: stockLocation.location_id,
|
||||
text: stockLocation.location_name + " (" + __t("Default location") + ")"
|
||||
|
|
@ -372,41 +304,32 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
$("#location_id").val(productDetails.location.id);
|
||||
$("#location_id").trigger('change');
|
||||
setDefault = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#location_id").append($("<option>", {
|
||||
value: stockLocation.location_id,
|
||||
text: stockLocation.location_name
|
||||
}));
|
||||
}
|
||||
|
||||
if (setDefault == 0)
|
||||
{
|
||||
if (setDefault == 0) {
|
||||
$("#location_id").val(stockLocation.location_id);
|
||||
$("#location_id").trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
if (document.getElementById("product_id").getAttribute("barcode") != "null")
|
||||
{
|
||||
if (document.getElementById("product_id").getAttribute("barcode") != "null") {
|
||||
Grocy.Api.Get('objects/product_barcodes?query[]=barcode=' + document.getElementById("product_id").getAttribute("barcode"),
|
||||
function(barcodeResult)
|
||||
{
|
||||
if (barcodeResult != null)
|
||||
{
|
||||
function(barcodeResult) {
|
||||
if (barcodeResult != null) {
|
||||
var barcode = barcodeResult[0];
|
||||
|
||||
if (barcode != null)
|
||||
{
|
||||
if (barcode.amount != null && !barcode.amount.isEmpty())
|
||||
{
|
||||
if (barcode != null) {
|
||||
if (barcode.amount != null && !barcode.amount.isEmpty()) {
|
||||
$("#display_amount").val(barcode.amount);
|
||||
$("#display_amount").select();
|
||||
}
|
||||
|
||||
if (barcode.qu_id != null && !barcode.qu_id.isEmpty())
|
||||
{
|
||||
if (barcode.qu_id != null && !barcode.qu_id.isEmpty()) {
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(barcode.qu_id);
|
||||
}
|
||||
|
||||
|
|
@ -417,27 +340,22 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
}
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1) {
|
||||
$("#display_amount").attr("min", productDetails.product.tare_weight);
|
||||
$('#display_amount').attr('max', parseFloat(productDetails.stock_amount) + parseFloat(productDetails.product.tare_weight));
|
||||
$("#tare-weight-handling-info").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#display_amount").attr("min", Grocy.DefaultMinAmount);
|
||||
$("#tare-weight-handling-info").addClass("d-none");
|
||||
}
|
||||
|
|
@ -446,17 +364,13 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
Grocy.FrontendHelpers.ValidateForm('consume-form');
|
||||
$('#display_amount').focus();
|
||||
|
||||
if (productDetails.stock_amount == productDetails.stock_amount_opened || productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
if (productDetails.stock_amount == productDetails.stock_amount_opened || productDetails.product.enable_tare_weight_handling == 1) {
|
||||
$("#save-mark-as-open-button").addClass("disabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#save-mark-as-open-button").removeClass("disabled");
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
@ -467,29 +381,24 @@ $('#display_amount').val(parseFloat(Grocy.UserSettings.stock_default_consume_amo
|
|||
$(".input-group-productamountpicker").trigger("change");
|
||||
Grocy.FrontendHelpers.ValidateForm('consume-form');
|
||||
|
||||
$('#display_amount').on('focus', function(e)
|
||||
{
|
||||
$('#display_amount').on('focus', function(e) {
|
||||
$(this).select();
|
||||
});
|
||||
|
||||
$('#price').on('focus', function(e)
|
||||
{
|
||||
$('#price').on('focus', function(e) {
|
||||
$(this).select();
|
||||
});
|
||||
|
||||
|
||||
$('#consume-form input').keyup(function(event)
|
||||
{
|
||||
$('#consume-form input').keyup(function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('consume-form');
|
||||
});
|
||||
|
||||
$('#consume-form select').change(function(event)
|
||||
{
|
||||
$('#consume-form select').change(function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('consume-form');
|
||||
});
|
||||
|
||||
$('#consume-form input').keydown(function(event)
|
||||
{
|
||||
$('#consume-form input').keydown(function(event) {
|
||||
if (event.keyCode === 13) //Enter
|
||||
{
|
||||
event.preventDefault();
|
||||
|
|
@ -497,58 +406,43 @@ $('#consume-form input').keydown(function(event)
|
|||
if (document.getElementById('consume-form').checkValidity() === false) //There is at least one validation error
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#save-consume-button').click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#specific_stock_entry").on("change", function(e)
|
||||
{
|
||||
if ($(e.target).val() == "")
|
||||
{
|
||||
$("#specific_stock_entry").on("change", function(e) {
|
||||
if ($(e.target).val() == "") {
|
||||
sumValue = 0;
|
||||
Grocy.Api.Get("stock/products/" + Grocy.Components.ProductPicker.GetValue() + '/entries?include_sub_products=true',
|
||||
function(stockEntries)
|
||||
{
|
||||
stockEntries.forEach(stockEntry =>
|
||||
{
|
||||
if (stockEntry.location_id == $("#location_id").val() || stockEntry.location_id == "")
|
||||
{
|
||||
function(stockEntries) {
|
||||
stockEntries.forEach(stockEntry => {
|
||||
if (stockEntry.location_id == $("#location_id").val() || stockEntry.location_id == "") {
|
||||
sumValue = sumValue + parseFloat(stockEntry.amount_aggregated);
|
||||
}
|
||||
});
|
||||
$("#display_amount").attr("max", sumValue);
|
||||
if (sumValue == 0)
|
||||
{
|
||||
if (sumValue == 0) {
|
||||
$("#display_amount").parent().find(".invalid-feedback").text(__t('There are no units available at this location'));
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#display_amount").attr("max", $('option:selected', this).attr('amount'));
|
||||
}
|
||||
});
|
||||
|
||||
$("#use_specific_stock_entry").on("change", function()
|
||||
{
|
||||
$("#use_specific_stock_entry").on("change", function() {
|
||||
var value = $(this).is(":checked");
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (value) {
|
||||
$("#specific_stock_entry").removeAttr("disabled");
|
||||
$("#specific_stock_entry").attr("required", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#specific_stock_entry").attr("disabled", "");
|
||||
$("#specific_stock_entry").removeAttr("required");
|
||||
$("#specific_stock_entry").val("");
|
||||
|
|
@ -558,146 +452,114 @@ $("#use_specific_stock_entry").on("change", function()
|
|||
Grocy.FrontendHelpers.ValidateForm("consume-form");
|
||||
});
|
||||
|
||||
$("#qu_id").on("change", function()
|
||||
{
|
||||
$("#qu_id").on("change", function() {
|
||||
RefreshForm();
|
||||
});
|
||||
|
||||
function UndoStockBooking(bookingId)
|
||||
{
|
||||
function UndoStockBooking(bookingId) {
|
||||
Grocy.Api.Post('stock/bookings/' + bookingId.toString() + '/undo', {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
toastr.success(__t("Booking successfully undone"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function UndoStockTransaction(transactionId)
|
||||
{
|
||||
function UndoStockTransaction(transactionId) {
|
||||
Grocy.Api.Post('stock/transactions/' + transactionId.toString() + '/undo', {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
toastr.success(__t("Transaction successfully undone"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
var locationId = GetUriParam('locationId');
|
||||
|
||||
if (typeof locationId === 'undefined')
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
}
|
||||
else
|
||||
{
|
||||
if (typeof locationId === 'undefined') {
|
||||
Grocy.Components.ProductPicker.Validate();
|
||||
} else {
|
||||
$("#location_id").val(locationId);
|
||||
$("#location_id").trigger('change');
|
||||
$("#use_specific_stock_entry").click();
|
||||
$("#use_specific_stock_entry").trigger('click');
|
||||
$("#use_specific_stock_entry").trigger('change');
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
Grocy.Components.ProductPicker.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
// Default input field
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
|
||||
$(document).on("change", "#scan-mode", function(e)
|
||||
{
|
||||
if ($(this).prop("checked"))
|
||||
{
|
||||
$(document).on("change", "#scan-mode", function(e) {
|
||||
if ($(this).prop("checked")) {
|
||||
Grocy.UISound.AskForPermission();
|
||||
}
|
||||
});
|
||||
|
||||
$("#scan-mode-button").on("click", function(e)
|
||||
{
|
||||
$("#scan-mode-button").on("click", function(e) {
|
||||
document.activeElement.blur();
|
||||
$("#scan-mode").click();
|
||||
$("#scan-mode").trigger('click');
|
||||
$("#scan-mode-button").toggleClass("btn-success").toggleClass("btn-danger");
|
||||
if ($("#scan-mode").prop("checked"))
|
||||
{
|
||||
if ($("#scan-mode").prop("checked")) {
|
||||
$("#scan-mode-status").text(__t("on"));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#scan-mode-status").text(__t("off"));
|
||||
}
|
||||
});
|
||||
|
||||
$('#consume-exact-amount').on('change', RefreshForm);
|
||||
var current_productDetails;
|
||||
function RefreshForm()
|
||||
{
|
||||
|
||||
function RefreshForm() {
|
||||
var productDetails = current_productDetails;
|
||||
if (!productDetails)
|
||||
{
|
||||
if (!productDetails) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1) {
|
||||
$("#consume-exact-amount-group").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#consume-exact-amount-group").addClass("d-none");
|
||||
}
|
||||
|
||||
if (productDetails.product.enable_tare_weight_handling == 1 && !$('#consume-exact-amount').is(':checked'))
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1 && !$('#consume-exact-amount').is(':checked')) {
|
||||
$("#display_amount").attr("min", productDetails.product.tare_weight);
|
||||
$('#display_amount').attr('max', sumValue + parseFloat(productDetails.product.tare_weight));
|
||||
$("#tare-weight-handling-info").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#tare-weight-handling-info").addClass("d-none");
|
||||
|
||||
$("#display_amount").attr("min", Grocy.DefaultMinAmount);
|
||||
$('#display_amount').attr('max', sumValue * $("#qu_id option:selected").attr("data-qu-factor"));
|
||||
|
||||
if (sumValue == 0)
|
||||
{
|
||||
if (sumValue == 0) {
|
||||
$("#display_amount").parent().find(".invalid-feedback").text(__t('There are no units available at this location'));
|
||||
}
|
||||
}
|
||||
|
||||
if (productDetails.has_childs)
|
||||
{
|
||||
if (productDetails.has_childs) {
|
||||
$("#display_amount").removeAttr("max");
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.ValidateForm("consume-form");
|
||||
}
|
||||
|
||||
function ScanModeSubmit(singleUnit = true)
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_consume_enabled))
|
||||
{
|
||||
if (singleUnit)
|
||||
{
|
||||
function ScanModeSubmit(singleUnit = true) {
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_consume_enabled)) {
|
||||
if (singleUnit) {
|
||||
$("#display_amount").val(1);
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.ValidateForm("consume-form");
|
||||
if (document.getElementById("consume-form").checkValidity() === true)
|
||||
{
|
||||
if (document.getElementById("consume-form").checkValidity() === true) {
|
||||
$('#save-consume-button').click();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
toastr.warning(__t("Scan mode is on but not all required fields could be populated automatically"));
|
||||
Grocy.UISound.Error();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
$('#save-inventory-button').on('click', function(e)
|
||||
{
|
||||
$('#save-inventory-button').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if ($(".combobox-menu-visible").length)
|
||||
{
|
||||
if ($(".combobox-menu-visible").length) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -11,11 +9,9 @@
|
|||
Grocy.FrontendHelpers.BeginUiBusy("inventory-form");
|
||||
|
||||
Grocy.Api.Get('stock/products/' + jsonForm.product_id,
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
var price = "";
|
||||
if (!jsonForm.price.toString().isEmpty())
|
||||
{
|
||||
if (!jsonForm.price.toString().isEmpty()) {
|
||||
price = parseFloat(jsonForm.price).toFixed(Grocy.UserSettings.stock_decimal_places_prices);
|
||||
}
|
||||
|
||||
|
|
@ -23,16 +19,13 @@
|
|||
jsonData.new_amount = jsonForm.amount;
|
||||
jsonData.best_before_date = Grocy.Components.DateTimePicker.GetValue();
|
||||
jsonData.stock_label_type = jsonForm.stock_label_type;
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) {
|
||||
jsonData.shopping_location_id = Grocy.Components.ShoppingLocationPicker.GetValue();
|
||||
}
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) {
|
||||
jsonData.location_id = Grocy.Components.LocationPicker.GetValue();
|
||||
}
|
||||
if (Grocy.UserSettings.show_purchased_date_on_purchase)
|
||||
{
|
||||
if (Grocy.UserSettings.show_purchased_date_on_purchase) {
|
||||
jsonData.purchased_date = Grocy.Components.DateTimePicker2.GetValue();
|
||||
}
|
||||
|
||||
|
|
@ -41,69 +34,57 @@
|
|||
var bookingResponse = null;
|
||||
|
||||
Grocy.Api.Post('stock/products/' + jsonForm.product_id + '/inventory', jsonData,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
bookingResponse = result;
|
||||
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct")
|
||||
{
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct") {
|
||||
var jsonDataBarcode = {};
|
||||
jsonDataBarcode.barcode = GetUriParam("barcode");
|
||||
jsonDataBarcode.product_id = jsonForm.product_id;
|
||||
jsonDataBarcode.shopping_location_id = jsonForm.shopping_location_id;
|
||||
|
||||
Grocy.Api.Post('objects/product_barcodes', jsonDataBarcode,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
$("#flow-info-InplaceAddBarcodeToExistingProduct").addClass("d-none");
|
||||
$('#barcode-lookup-disabled-hint').addClass('d-none');
|
||||
$('#barcode-lookup-hint').removeClass('d-none');
|
||||
window.history.replaceState({}, document.title, U("/inventory"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("inventory-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER && parseFloat($("#display_amount").attr("data-estimated-booking-amount")) > 0)
|
||||
{
|
||||
if (Grocy.Webhooks.labelprinter !== undefined)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER && parseFloat($("#display_amount").attr("data-estimated-booking-amount")) > 0) {
|
||||
if (Grocy.Webhooks.labelprinter !== undefined) {
|
||||
if (jsonForm.stock_label_type == 1) // Single label
|
||||
{
|
||||
var webhookData = {};
|
||||
webhookData.product = productDetails.product.name;
|
||||
webhookData.grocycode = 'grcy:p:' + jsonForm.product_id + ":" + result[0].stock_id;
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) {
|
||||
webhookData.due_date = __t('DD') + ': ' + result[0].best_before_date;
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, webhookData);
|
||||
}
|
||||
else if (jsonForm.stock_label_type == 2) // Label per unit
|
||||
} else if (jsonForm.stock_label_type == 2) // Label per unit
|
||||
{
|
||||
Grocy.Api.Get('stock/transactions/' + result[0].transaction_id,
|
||||
function(stockEntries)
|
||||
{
|
||||
stockEntries.forEach(stockEntry =>
|
||||
{
|
||||
function(stockEntries) {
|
||||
stockEntries.forEach(stockEntry => {
|
||||
var webhookData = {};
|
||||
webhookData.product = productDetails.product.name;
|
||||
webhookData.grocycode = 'grcy:p:' + jsonForm.product_id + ":" + stockEntry.stock_id;
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) {
|
||||
webhookData.due_date = __t('DD') + ': ' + result[0].best_before_date;
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, webhookData);
|
||||
});
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
@ -112,18 +93,14 @@
|
|||
}
|
||||
|
||||
Grocy.Api.Get('stock/products/' + jsonForm.product_id,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
var successMessage = __t('Stock amount of %1$s is now %2$s', result.product.name, result.stock_amount + " " + __n(result.stock_amount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural, true)) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockTransaction(\'' + bookingResponse[0].transaction_id + '\')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>';
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
window.parent.postMessage(WindowMessageBag("ProductChanged", jsonForm.product_id), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", successMessage), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.FrontendHelpers.EndUiBusy("inventory-form");
|
||||
toastr.success(successMessage);
|
||||
Grocy.Components.ProductPicker.FinishFlow();
|
||||
|
|
@ -137,125 +114,99 @@
|
|||
$(".input-group-productamountpicker").trigger("change");
|
||||
$('#price').val('');
|
||||
Grocy.Components.DateTimePicker.Clear();
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
Grocy.Components.ProductPicker.Clear();
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) {
|
||||
Grocy.Components.ShoppingLocationPicker.SetValue('');
|
||||
}
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
Grocy.Components.ProductCard.Refresh(jsonForm.product_id);
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER) {
|
||||
$("#stock_label_type").val(0);
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.ValidateForm('inventory-form');
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy();
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("inventory-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("inventory-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
Grocy.Components.ProductPicker.OnChange(function(e) {
|
||||
var productId = $(e.target).val();
|
||||
|
||||
if (productId)
|
||||
{
|
||||
if (productId) {
|
||||
Grocy.Components.ProductCard.Refresh(productId);
|
||||
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id);
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(productDetails.quantity_unit_stock.id);
|
||||
|
||||
$('#display_amount').attr("data-stock-amount", productDetails.stock_amount)
|
||||
$('#display_amount').attr('data-not-equal', productDetails.stock_amount * $("#qu_id option:selected").attr("data-qu-factor"));
|
||||
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1) {
|
||||
$("#display_amount").attr("min", productDetails.product.tare_weight);
|
||||
$("#tare-weight-handling-info").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#display_amount").attr("min", "0");
|
||||
$("#tare-weight-handling-info").addClass("d-none");
|
||||
}
|
||||
|
||||
$('#price').val(parseFloat(productDetails.last_price));
|
||||
RefreshLocaleNumberInput();
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) {
|
||||
Grocy.Components.ShoppingLocationPicker.SetId(productDetails.last_shopping_location_id);
|
||||
}
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) {
|
||||
Grocy.Components.LocationPicker.SetId(productDetails.location.id);
|
||||
}
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
if (productDetails.product.default_best_before_days.toString() !== '0')
|
||||
{
|
||||
if (productDetails.product.default_best_before_days == -1)
|
||||
{
|
||||
if (!$("#datetimepicker-shortcut").is(":checked"))
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) {
|
||||
if (productDetails.product.default_best_before_days.toString() !== '0') {
|
||||
if (productDetails.product.default_best_before_days == -1) {
|
||||
if (!$("#datetimepicker-shortcut").is(":checked")) {
|
||||
$("#datetimepicker-shortcut").click();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.Components.DateTimePicker.SetValue(moment().add(productDetails.product.default_best_before_days, 'days').format('YYYY-MM-DD'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER) {
|
||||
$("#stock_label_type").val(productDetails.product.default_stock_label_type);
|
||||
}
|
||||
|
||||
if (document.getElementById("product_id").getAttribute("barcode") != "null")
|
||||
{
|
||||
if (document.getElementById("product_id").getAttribute("barcode") != "null") {
|
||||
Grocy.Api.Get('objects/product_barcodes?query[]=barcode=' + document.getElementById("product_id").getAttribute("barcode"),
|
||||
function(barcodeResult)
|
||||
{
|
||||
if (barcodeResult != null)
|
||||
{
|
||||
function(barcodeResult) {
|
||||
if (barcodeResult != null) {
|
||||
var barcode = barcodeResult[0];
|
||||
|
||||
if (barcode != null)
|
||||
{
|
||||
if (barcode.amount != null && !barcode.amount.isEmpty())
|
||||
{
|
||||
if (barcode != null) {
|
||||
if (barcode.amount != null && !barcode.amount.isEmpty()) {
|
||||
$("#display_amount").val(barcode.amount);
|
||||
$("#display_amount").select();
|
||||
}
|
||||
|
||||
if (barcode.qu_id != null && !barcode.qu_id.isEmpty())
|
||||
{
|
||||
if (barcode.qu_id != null && !barcode.qu_id.isEmpty()) {
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(barcode.qu_id);
|
||||
}
|
||||
|
||||
|
|
@ -265,8 +216,7 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
}
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
@ -278,8 +228,7 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
$('#display_amount').focus();
|
||||
$('#display_amount').trigger('keyup');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
@ -290,39 +239,29 @@ $('#display_amount').val('');
|
|||
$(".input-group-productamountpicker").trigger("change");
|
||||
Grocy.FrontendHelpers.ValidateForm('inventory-form');
|
||||
|
||||
if (Grocy.Components.ProductPicker.InAnyFlow() === false && GetUriParam("embedded") === undefined)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
if (Grocy.Components.ProductPicker.InAnyFlow() === false && GetUriParam("embedded") === undefined) {
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
} else {
|
||||
Grocy.Components.ProductPicker.Validate();
|
||||
|
||||
if (Grocy.Components.ProductPicker.InProductModifyWorkflow())
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
if (Grocy.Components.ProductPicker.InProductModifyWorkflow()) {
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
$('#display_amount').on('focus', function(e)
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetValue().length === 0)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#display_amount').on('focus', function(e) {
|
||||
if (Grocy.Components.ProductPicker.GetValue().length === 0) {
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
} else {
|
||||
$(this).select();
|
||||
}
|
||||
});
|
||||
|
||||
$('#inventory-form input').keyup(function(event)
|
||||
{
|
||||
$('#inventory-form input').keyup(function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('inventory-form');
|
||||
});
|
||||
|
||||
$('#inventory-form input').keydown(function(event)
|
||||
{
|
||||
$('#inventory-form input').keydown(function(event) {
|
||||
if (event.keyCode === 13) //Enter
|
||||
{
|
||||
event.preventDefault();
|
||||
|
|
@ -330,46 +269,37 @@ $('#inventory-form input').keydown(function(event)
|
|||
if (document.getElementById('inventory-form').checkValidity() === false) //There is at least one validation error
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#save-inventory-button').click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$('#qu_id').on('change', function(e)
|
||||
{
|
||||
$('#qu_id').on('change', function(e) {
|
||||
$('#display_amount').attr('data-not-equal', parseFloat($('#display_amount').attr('data-stock-amount')) * parseFloat($("#qu_id option:selected").attr("data-qu-factor")));
|
||||
Grocy.FrontendHelpers.ValidateForm('inventory-form');
|
||||
});
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('change', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('change', function(e) {
|
||||
Grocy.FrontendHelpers.ValidateForm('inventory-form');
|
||||
});
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('keypress', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('keypress', function(e) {
|
||||
Grocy.FrontendHelpers.ValidateForm('inventory-form');
|
||||
});
|
||||
|
||||
$('#display_amount').on('keyup', function(e)
|
||||
{
|
||||
$('#display_amount').on('keyup', function(e) {
|
||||
var productId = Grocy.Components.ProductPicker.GetValue();
|
||||
var newAmount = parseFloat($('#amount').val());
|
||||
|
||||
if (productId)
|
||||
{
|
||||
if (productId) {
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
var productStockAmount = parseFloat(productDetails.stock_amount || parseFloat('0'));
|
||||
|
||||
var containerWeight = parseFloat("0");
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1) {
|
||||
containerWeight = parseFloat(productDetails.product.tare_weight);
|
||||
}
|
||||
|
||||
|
|
@ -378,71 +308,54 @@ $('#display_amount').on('keyup', function(e)
|
|||
estimatedBookingAmount = Math.abs(estimatedBookingAmount);
|
||||
$('#inventory-change-info').removeClass('d-none');
|
||||
|
||||
if (productDetails.product.enable_tare_weight_handling == 1 && newAmount < containerWeight)
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1 && newAmount < containerWeight) {
|
||||
$('#inventory-change-info').addClass('d-none');
|
||||
}
|
||||
else if (newAmount > productStockAmount + containerWeight)
|
||||
{
|
||||
} else if (newAmount > productStockAmount + containerWeight) {
|
||||
$('#inventory-change-info').text(__t('This means %s will be added to stock', estimatedBookingAmount.toLocaleString() + ' ' + __n(estimatedBookingAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true)));
|
||||
Grocy.Components.DateTimePicker.GetInputElement().attr('required', '');
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) {
|
||||
Grocy.Components.LocationPicker.GetInputElement().attr('required', '');
|
||||
}
|
||||
}
|
||||
else if (newAmount < productStockAmount + containerWeight)
|
||||
{
|
||||
} else if (newAmount < productStockAmount + containerWeight) {
|
||||
$('#inventory-change-info').text(__t('This means %s will be removed from stock', estimatedBookingAmount.toLocaleString() + ' ' + __n(estimatedBookingAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true)));
|
||||
Grocy.Components.DateTimePicker.GetInputElement().removeAttr('required');
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) {
|
||||
Grocy.Components.LocationPicker.GetInputElement().removeAttr('required');
|
||||
}
|
||||
}
|
||||
else if (newAmount == productStockAmount)
|
||||
{
|
||||
} else if (newAmount == productStockAmount) {
|
||||
$('#inventory-change-info').addClass('d-none');
|
||||
}
|
||||
|
||||
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) {
|
||||
Grocy.Components.DateTimePicker.GetInputElement().removeAttr('required');
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.ValidateForm('inventory-form');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function UndoStockBooking(bookingId)
|
||||
{
|
||||
function UndoStockBooking(bookingId) {
|
||||
Grocy.Api.Post('stock/bookings/' + bookingId.toString() + '/undo', {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
toastr.success(__t("Booking successfully undone"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function UndoStockTransaction(transactionId)
|
||||
{
|
||||
function UndoStockTransaction(transactionId) {
|
||||
Grocy.Api.Post('stock/transactions/' + transactionId.toString() + '/undo', {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
toastr.success(__t("Transaction successfully undone"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,85 +1,59 @@
|
|||
function saveProductPicture(result, location, jsonData)
|
||||
{
|
||||
function saveProductPicture(result, location, jsonData) {
|
||||
var productId = Grocy.EditObjectId || result.created_object_id;
|
||||
Grocy.EditObjectId = productId; // Grocy.EditObjectId is not yet set when adding a product
|
||||
|
||||
Grocy.Components.UserfieldsForm.Save(() =>
|
||||
{
|
||||
if (jsonData.hasOwnProperty("picture_file_name") && !Grocy.DeleteProductPictureOnSave)
|
||||
{
|
||||
Grocy.Components.UserfieldsForm.Save(() => {
|
||||
if (jsonData.hasOwnProperty("picture_file_name") && !Grocy.DeleteProductPictureOnSave) {
|
||||
Grocy.Api.UploadFile($("#product-picture")[0].files[0], 'productpictures', jsonData.picture_file_name,
|
||||
(result) =>
|
||||
{
|
||||
if (Grocy.ProductEditFormRedirectUri == "reload")
|
||||
{
|
||||
(result) => {
|
||||
if (Grocy.ProductEditFormRedirectUri == "reload") {
|
||||
window.location.reload();
|
||||
return
|
||||
}
|
||||
|
||||
var returnTo = GetUriParam('returnto');
|
||||
if (GetUriParam("closeAfterCreation") !== undefined)
|
||||
{
|
||||
if (GetUriParam("closeAfterCreation") !== undefined) {
|
||||
window.close();
|
||||
}
|
||||
else if (returnTo !== undefined)
|
||||
{
|
||||
if (GetUriParam("flow") !== undefined)
|
||||
{
|
||||
} else if (returnTo !== undefined) {
|
||||
if (GetUriParam("flow") !== undefined) {
|
||||
window.location.href = U(returnTo) + '&product-name=' + encodeURIComponent($('#name').val());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
window.location.href = U(returnTo);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
window.location.href = U(location + productId);
|
||||
}
|
||||
|
||||
},
|
||||
(xhr) =>
|
||||
{
|
||||
(xhr) => {
|
||||
Grocy.FrontendHelpers.EndUiBusy("product-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Grocy.ProductEditFormRedirectUri == "reload")
|
||||
{
|
||||
} else {
|
||||
if (Grocy.ProductEditFormRedirectUri == "reload") {
|
||||
window.location.reload();
|
||||
return
|
||||
}
|
||||
|
||||
var returnTo = GetUriParam('returnto');
|
||||
if (GetUriParam("closeAfterCreation") !== undefined)
|
||||
{
|
||||
if (GetUriParam("closeAfterCreation") !== undefined) {
|
||||
window.close();
|
||||
}
|
||||
else if (returnTo !== undefined)
|
||||
{
|
||||
if (GetUriParam("flow") !== undefined)
|
||||
{
|
||||
} else if (returnTo !== undefined) {
|
||||
if (GetUriParam("flow") !== undefined) {
|
||||
window.location.href = U(returnTo) + '&product-name=' + encodeURIComponent($('#name').val());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
window.location.href = U(returnTo);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
window.location.href = U(location + productId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('.save-product-button').on('click', function(e)
|
||||
{
|
||||
$('.save-product-button').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var jsonData = $('#product-form').serializeJSON();
|
||||
|
|
@ -88,42 +62,35 @@ $('.save-product-button').on('click', function(e)
|
|||
jsonData.parent_product_id = parentProductId;
|
||||
Grocy.FrontendHelpers.BeginUiBusy("product-form");
|
||||
|
||||
if (jsonData.parent_product_id.toString().isEmpty())
|
||||
{
|
||||
if (jsonData.parent_product_id.toString().isEmpty()) {
|
||||
jsonData.parent_product_id = null;
|
||||
}
|
||||
|
||||
if ($("#product-picture")[0].files.length > 0)
|
||||
{
|
||||
if ($("#product-picture")[0].files.length > 0) {
|
||||
var someRandomStuff = Math.random().toString(36).substring(2, 100) + Math.random().toString(36).substring(2, 100);
|
||||
jsonData.picture_file_name = someRandomStuff + CleanFileName($("#product-picture")[0].files[0].name);
|
||||
}
|
||||
|
||||
const location = $(e.currentTarget).attr('data-location') == 'return' ? '/products?product=' : '/product/';
|
||||
|
||||
if (Grocy.EditMode == 'create')
|
||||
{
|
||||
if (Grocy.EditMode == 'create') {
|
||||
Grocy.Api.Post('objects/products', jsonData,
|
||||
(result) => saveProductPicture(result, location, jsonData),
|
||||
(xhr) =>
|
||||
{
|
||||
(xhr) => {
|
||||
Grocy.FrontendHelpers.EndUiBusy("product-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (Grocy.DeleteProductPictureOnSave)
|
||||
{
|
||||
if (Grocy.DeleteProductPictureOnSave) {
|
||||
jsonData.picture_file_name = null;
|
||||
|
||||
Grocy.Api.DeleteFile(Grocy.ProductPictureFileName, 'productpictures', {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
// Nothing to do
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("product-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
|
|
@ -132,55 +99,44 @@ $('.save-product-button').on('click', function(e)
|
|||
|
||||
Grocy.Api.Put('objects/products/' + Grocy.EditObjectId, jsonData,
|
||||
(result) => saveProductPicture(result, location, jsonData),
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("product-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (Grocy.EditMode == "edit")
|
||||
{
|
||||
if (Grocy.EditMode == "edit") {
|
||||
Grocy.Api.Get('objects/stock_log?limit=1&query[]=product_id=' + Grocy.EditObjectId,
|
||||
function(productJournalEntries)
|
||||
{
|
||||
if (productJournalEntries.length == 0)
|
||||
{
|
||||
function(productJournalEntries) {
|
||||
if (productJournalEntries.length == 0) {
|
||||
$('#qu_id_stock').removeAttr("disabled");
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (GetUriParam("flow") == "InplaceNewProductWithName")
|
||||
{
|
||||
if (GetUriParam("flow") == "InplaceNewProductWithName") {
|
||||
$('#name').val(GetUriParam("name"));
|
||||
$('#name').focus();
|
||||
}
|
||||
|
||||
if (GetUriParam("flow") !== undefined || GetUriParam("returnto") !== undefined)
|
||||
{
|
||||
if (GetUriParam("flow") !== undefined || GetUriParam("returnto") !== undefined) {
|
||||
$("#save-hint").addClass("d-none");
|
||||
$(".save-product-button[data-location='return']").addClass("d-none");
|
||||
}
|
||||
|
||||
$('.input-group-qu').on('change', function(e)
|
||||
{
|
||||
$('.input-group-qu').on('change', function(e) {
|
||||
var quIdPurchase = $("#qu_id_purchase").val();
|
||||
var quIdStock = $("#qu_id_stock").val();
|
||||
|
||||
if (Grocy.EditMode == "create" && !quIdPurchase.toString().isEmpty() && !quIdStock.toString().isEmpty() && quIdPurchase != quIdStock)
|
||||
{
|
||||
if (Grocy.EditMode == "create" && !quIdPurchase.toString().isEmpty() && !quIdStock.toString().isEmpty() && quIdPurchase != quIdStock) {
|
||||
Grocy.Api.Get("objects/quantity_unit_conversions?query[]=product_id=null&query[]=from_qu_id=" + quIdPurchase + "&query[]=to_qu_id=" + quIdStock,
|
||||
function(response)
|
||||
{
|
||||
if (response != null && response.length > 0)
|
||||
{
|
||||
function(response) {
|
||||
if (response != null && response.length > 0) {
|
||||
var conversion = response[0];
|
||||
|
||||
$("#qu_factor_purchase_to_stock").val(conversion.factor);
|
||||
|
|
@ -188,14 +144,11 @@ $('.input-group-qu').on('change', function(e)
|
|||
RefreshQuConversionInfo();
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
RefreshQuConversionInfo();
|
||||
}
|
||||
|
||||
|
|
@ -206,25 +159,20 @@ $('.input-group-qu').on('change', function(e)
|
|||
Grocy.FrontendHelpers.ValidateForm('product-form');
|
||||
});
|
||||
|
||||
function RefreshQuConversionInfo()
|
||||
{
|
||||
function RefreshQuConversionInfo() {
|
||||
var quIdPurchase = $("#qu_id_purchase").val();
|
||||
var quIdStock = $("#qu_id_stock").val();
|
||||
var factor = $('#qu_factor_purchase_to_stock').val();
|
||||
|
||||
if (factor > 1 || quIdPurchase != quIdStock)
|
||||
{
|
||||
if (factor > 1 || quIdPurchase != quIdStock) {
|
||||
$('#qu-conversion-info').text(__t('This means 1 %1$s purchased will be converted into %2$s %3$s in stock', $("#qu_id_purchase option:selected").text(), (1 * factor).toString(), __n((1 * factor).toString(), $("#qu_id_stock option:selected").text(), $("#qu_id_stock option:selected").data("plural-form"), true)));
|
||||
$('#qu-conversion-info').removeClass('d-none');
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#qu-conversion-info').addClass('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
$('#product-form input').keyup(function(event)
|
||||
{
|
||||
$('#product-form input').on('keyup', function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('product-form');
|
||||
$(".input-group-qu").trigger("change");
|
||||
$("#product-form select").trigger("select");
|
||||
|
|
@ -232,9 +180,7 @@ $('#product-form input').keyup(function(event)
|
|||
if (document.getElementById('product-form').checkValidity() === false) //There is at least one validation error
|
||||
{
|
||||
$("#qu-conversion-add-button").addClass("disabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#qu-conversion-add-button").removeClass("disabled");
|
||||
}
|
||||
|
||||
|
|
@ -244,13 +190,11 @@ $('#product-form input').keyup(function(event)
|
|||
}
|
||||
});
|
||||
|
||||
$('#location_id').change(function(event)
|
||||
{
|
||||
$('#location_id').on('change', function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('product-form');
|
||||
});
|
||||
|
||||
$('#product-form input').keydown(function(event)
|
||||
{
|
||||
$('#product-form input').on('keydown', function(event) {
|
||||
if (event.keyCode === 13) //Enter
|
||||
{
|
||||
event.preventDefault();
|
||||
|
|
@ -258,30 +202,23 @@ $('#product-form input').keydown(function(event)
|
|||
if (document.getElementById('product-form').checkValidity() === false) //There is at least one validation error
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('.default-submit-button').click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#enable_tare_weight_handling").on("click", function()
|
||||
{
|
||||
if (this.checked)
|
||||
{
|
||||
$("#enable_tare_weight_handling").on("click", function() {
|
||||
if (this.checked) {
|
||||
$("#tare_weight").removeAttr("disabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#tare_weight").attr("disabled", "");
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.ValidateForm("product-form");
|
||||
});
|
||||
|
||||
$("#product-picture").on("change", function(e)
|
||||
{
|
||||
$("#product-picture").on("change", function(e) {
|
||||
$("#product-picture-label").removeClass("d-none");
|
||||
$("#product-picture-label-none").addClass("d-none");
|
||||
$("#delete-current-product-picture-on-save-hint").addClass("d-none");
|
||||
|
|
@ -290,8 +227,7 @@ $("#product-picture").on("change", function(e)
|
|||
});
|
||||
|
||||
Grocy.DeleteProductPictureOnSave = false;
|
||||
$("#delete-current-product-picture-button").on("click", function(e)
|
||||
{
|
||||
$("#delete-current-product-picture-button").on("click", function(e) {
|
||||
Grocy.DeleteProductPictureOnSave = true;
|
||||
$("#current-product-picture").addClass("d-none");
|
||||
$("#delete-current-product-picture-on-save-hint").removeClass("d-none");
|
||||
|
|
@ -300,12 +236,24 @@ $("#delete-current-product-picture-button").on("click", function(e)
|
|||
});
|
||||
|
||||
var quConversionsTable = $('#qu-conversions-table-products').DataTable({
|
||||
'order': [[1, 'asc']],
|
||||
"orderFixed": [[4, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
{ 'searchable': false, "targets": 0 },
|
||||
{ 'visible': false, 'targets': 4 }
|
||||
'order': [
|
||||
[1, 'asc']
|
||||
],
|
||||
"orderFixed": [
|
||||
[4, 'asc']
|
||||
],
|
||||
'columnDefs': [{
|
||||
'orderable': false,
|
||||
'targets': 0
|
||||
},
|
||||
{
|
||||
'searchable': false,
|
||||
"targets": 0
|
||||
},
|
||||
{
|
||||
'visible': false,
|
||||
'targets': 4
|
||||
}
|
||||
].concat($.fn.dataTable.defaults.columnDefs),
|
||||
'rowGroup': {
|
||||
enable: true,
|
||||
|
|
@ -316,13 +264,28 @@ $('#qu-conversions-table-products tbody').removeClass("d-none");
|
|||
quConversionsTable.columns.adjust().draw();
|
||||
|
||||
var barcodeTable = $('#barcode-table').DataTable({
|
||||
'order': [[1, 'asc']],
|
||||
"orderFixed": [[1, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
{ 'searchable': false, "targets": 0 },
|
||||
{ 'visible': false, 'targets': 5 },
|
||||
{ 'visible': false, 'targets': 6 }
|
||||
'order': [
|
||||
[1, 'asc']
|
||||
],
|
||||
"orderFixed": [
|
||||
[1, 'asc']
|
||||
],
|
||||
'columnDefs': [{
|
||||
'orderable': false,
|
||||
'targets': 0
|
||||
},
|
||||
{
|
||||
'searchable': false,
|
||||
"targets": 0
|
||||
},
|
||||
{
|
||||
'visible': false,
|
||||
'targets': 5
|
||||
},
|
||||
{
|
||||
'visible': false,
|
||||
'targets': 6
|
||||
}
|
||||
].concat($.fn.dataTable.defaults.columnDefs)
|
||||
});
|
||||
$('#barcode-table tbody').removeClass("d-none");
|
||||
|
|
@ -330,27 +293,23 @@ barcodeTable.columns.adjust().draw();
|
|||
|
||||
Grocy.Components.UserfieldsForm.Load();
|
||||
$("#name").trigger("keyup");
|
||||
$('#name').focus();
|
||||
$('#name').trigger('focus');
|
||||
$('.input-group-qu').trigger('change');
|
||||
Grocy.FrontendHelpers.ValidateForm('product-form');
|
||||
|
||||
$(document).on('click', '.product-grocycode-label-print', function(e)
|
||||
{
|
||||
$(document).on('click', '.product-grocycode-label-print', function(e) {
|
||||
e.preventDefault();
|
||||
document.activeElement.blur();
|
||||
|
||||
var productId = $(e.currentTarget).attr('data-product-id');
|
||||
Grocy.Api.Get('stock/products/' + productId + '/printlabel', function(labelData)
|
||||
{
|
||||
if (Grocy.Webhooks.labelprinter !== undefined)
|
||||
{
|
||||
Grocy.Api.Get('stock/products/' + productId + '/printlabel', function(labelData) {
|
||||
if (Grocy.Webhooks.labelprinter !== undefined) {
|
||||
Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, labelData);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('click', '.qu-conversion-delete-button', function(e)
|
||||
{
|
||||
$(document).on('click', '.qu-conversion-delete-button', function(e) {
|
||||
var objectId = $(e.currentTarget).attr('data-qu-conversion-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
@ -366,18 +325,14 @@ $(document).on('click', '.qu-conversion-delete-button', function(e)
|
|||
className: 'btn-danger'
|
||||
}
|
||||
},
|
||||
callback: function(result)
|
||||
{
|
||||
if (result === true)
|
||||
{
|
||||
callback: function(result) {
|
||||
if (result === true) {
|
||||
Grocy.Api.Delete('objects/quantity_unit_conversions/' + objectId, {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
Grocy.ProductEditFormRedirectUri = "reload";
|
||||
$('#save-product-button').click();
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
@ -386,8 +341,7 @@ $(document).on('click', '.qu-conversion-delete-button', function(e)
|
|||
});
|
||||
});
|
||||
|
||||
$(document).on('click', '.barcode-delete-button', function(e)
|
||||
{
|
||||
$(document).on('click', '.barcode-delete-button', function(e) {
|
||||
var objectId = $(e.currentTarget).attr('data-barcode-id');
|
||||
|
||||
bootbox.confirm({
|
||||
|
|
@ -403,18 +357,14 @@ $(document).on('click', '.barcode-delete-button', function(e)
|
|||
className: 'btn-danger'
|
||||
}
|
||||
},
|
||||
callback: function(result)
|
||||
{
|
||||
if (result === true)
|
||||
{
|
||||
callback: function(result) {
|
||||
if (result === true) {
|
||||
Grocy.Api.Delete('objects/product_barcodes/' + objectId, {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
Grocy.ProductEditFormRedirectUri = "reload";
|
||||
$('#save-product-button').click();
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
@ -423,14 +373,12 @@ $(document).on('click', '.barcode-delete-button', function(e)
|
|||
});
|
||||
});
|
||||
|
||||
$('#qu_id_stock').change(function(e)
|
||||
{
|
||||
$('#qu_id_stock').on('change', function(e) {
|
||||
// Preset QU purchase with stock QU if unset
|
||||
var quIdStock = $('#qu_id_stock');
|
||||
var quIdPurchase = $('#qu_id_purchase');
|
||||
|
||||
if (quIdPurchase[0].selectedIndex === 0 && quIdStock[0].selectedIndex !== 0)
|
||||
{
|
||||
if (quIdPurchase[0].selectedIndex === 0 && quIdStock[0].selectedIndex !== 0) {
|
||||
quIdPurchase[0].selectedIndex = quIdStock[0].selectedIndex;
|
||||
Grocy.FrontendHelpers.ValidateForm('product-form');
|
||||
}
|
||||
|
|
@ -438,59 +386,47 @@ $('#qu_id_stock').change(function(e)
|
|||
RefreshQuConversionInfo();
|
||||
});
|
||||
|
||||
$(window).on("message", function(e)
|
||||
{
|
||||
$(window).on("message", function(e) {
|
||||
var data = e.originalEvent.data;
|
||||
|
||||
if (data.Message === "ProductBarcodesChanged" || data.Message === "ProductQUConversionChanged")
|
||||
{
|
||||
if (data.Message === "ProductBarcodesChanged" || data.Message === "ProductQUConversionChanged") {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
if (Grocy.EditMode == "create" && GetUriParam("copy-of") != undefined)
|
||||
{
|
||||
if (Grocy.EditMode == "create" && GetUriParam("copy-of") != undefined) {
|
||||
Grocy.Api.Get('objects/products/' + GetUriParam("copy-of"),
|
||||
function(sourceProduct)
|
||||
{
|
||||
if (sourceProduct.parent_product_id != null)
|
||||
{
|
||||
function(sourceProduct) {
|
||||
if (sourceProduct.parent_product_id != null) {
|
||||
Grocy.Components.ProductPicker.SetId(sourceProduct.parent_product_id);
|
||||
}
|
||||
if (sourceProduct.description != null)
|
||||
{
|
||||
if (sourceProduct.description != null) {
|
||||
$("#description").summernote("pasteHTML", sourceProduct.description);
|
||||
}
|
||||
$("#location_id").val(sourceProduct.location_id);
|
||||
if (sourceProduct.shopping_location_id != null)
|
||||
{
|
||||
if (sourceProduct.shopping_location_id != null) {
|
||||
Grocy.Components.ShoppingLocationPicker.SetId(sourceProduct.shopping_location_id);
|
||||
}
|
||||
$("#min_stock_amount").val(sourceProduct.min_stock_amount);
|
||||
if (BoolVal(sourceProduct.cumulate_min_stock_amount_of_sub_products))
|
||||
{
|
||||
if (BoolVal(sourceProduct.cumulate_min_stock_amount_of_sub_products)) {
|
||||
$("#cumulate_min_stock_amount_of_sub_products").prop("checked", true);
|
||||
}
|
||||
$("#default_best_before_days").val(sourceProduct.default_best_before_days);
|
||||
$("#default_best_before_days_after_open").val(sourceProduct.default_best_before_days_after_open);
|
||||
if (sourceProduct.product_group_id != null)
|
||||
{
|
||||
if (sourceProduct.product_group_id != null) {
|
||||
$("#product_group_id").val(sourceProduct.product_group_id);
|
||||
}
|
||||
$("#qu_id_stock").val(sourceProduct.qu_id_stock);
|
||||
$("#qu_id_purchase").val(sourceProduct.qu_id_purchase);
|
||||
$("#qu_factor_purchase_to_stock").val(sourceProduct.qu_factor_purchase_to_stock);
|
||||
if (BoolVal(sourceProduct.enable_tare_weight_handling))
|
||||
{
|
||||
if (BoolVal(sourceProduct.enable_tare_weight_handling)) {
|
||||
$("#enable_tare_weight_handling").prop("checked", true);
|
||||
}
|
||||
$("#tare_weight").val(sourceProduct.tare_weight);
|
||||
if (BoolVal(sourceProduct.not_check_stock_fulfillment_for_recipes))
|
||||
{
|
||||
if (BoolVal(sourceProduct.not_check_stock_fulfillment_for_recipes)) {
|
||||
$("#not_check_stock_fulfillment_for_recipes").prop("checked", true);
|
||||
}
|
||||
if (sourceProduct.calories != null)
|
||||
{
|
||||
if (sourceProduct.calories != null) {
|
||||
$("#calories").val(sourceProduct.calories);
|
||||
}
|
||||
$("#default_best_before_days_after_freezing").val(sourceProduct.default_best_before_days_after_freezing);
|
||||
|
|
@ -499,75 +435,57 @@ if (Grocy.EditMode == "create" && GetUriParam("copy-of") != undefined)
|
|||
|
||||
Grocy.FrontendHelpers.ValidateForm('product-form');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
else if (Grocy.EditMode === 'create')
|
||||
{
|
||||
if (Grocy.UserSettings.product_presets_location_id.toString() !== '-1')
|
||||
{
|
||||
} else if (Grocy.EditMode === 'create') {
|
||||
if (Grocy.UserSettings.product_presets_location_id.toString() !== '-1') {
|
||||
$("#location_id").val(Grocy.UserSettings.product_presets_location_id);
|
||||
}
|
||||
|
||||
if (Grocy.UserSettings.product_presets_product_group_id.toString() !== '-1')
|
||||
{
|
||||
if (Grocy.UserSettings.product_presets_product_group_id.toString() !== '-1') {
|
||||
$("#product_group_id").val(Grocy.UserSettings.product_presets_product_group_id);
|
||||
}
|
||||
|
||||
if (Grocy.UserSettings.product_presets_qu_id.toString() !== '-1')
|
||||
{
|
||||
if (Grocy.UserSettings.product_presets_qu_id.toString() !== '-1') {
|
||||
$("select.input-group-qu").val(Grocy.UserSettings.product_presets_qu_id);
|
||||
}
|
||||
|
||||
if (Grocy.UserSettings.product_presets_default_due_days.toString() !== '0')
|
||||
{
|
||||
if (Grocy.UserSettings.product_presets_default_due_days.toString() !== '0') {
|
||||
$("#default_best_before_days").val(Grocy.UserSettings.product_presets_default_due_days);
|
||||
}
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING) {
|
||||
$("#treat_opened_as_out_of_stock").prop("checked", BoolVal(Grocy.UserSettings.product_presets_treat_opened_as_out_of_stock));
|
||||
}
|
||||
}
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
Grocy.Components.ProductPicker.OnChange(function(e) {
|
||||
var parentProductId = $(e.target).val();
|
||||
|
||||
if (parentProductId)
|
||||
{
|
||||
if (parentProductId) {
|
||||
Grocy.Api.Get('objects/products/' + parentProductId,
|
||||
function(parentProduct)
|
||||
{
|
||||
if (BoolVal(parentProduct.cumulate_min_stock_amount_of_sub_products))
|
||||
{
|
||||
function(parentProduct) {
|
||||
if (BoolVal(parentProduct.cumulate_min_stock_amount_of_sub_products)) {
|
||||
|
||||
$("#min_stock_amount").attr("disabled", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#min_stock_amount').removeAttr("disabled");
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#min_stock_amount').removeAttr("disabled");
|
||||
}
|
||||
});
|
||||
|
||||
Grocy.FrontendHelpers.ValidateForm("product-form");
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger("change");
|
||||
Grocy.Components.ProductPicker.Validate();
|
||||
|
||||
if (Grocy.EditMode == "edit")
|
||||
{
|
||||
if (Grocy.EditMode == "edit") {
|
||||
$(".save-product-button").toggleClass("default-submit-button");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,61 +1,180 @@
|
|||
var productsTable = $('#products-table').DataTable({
|
||||
'order': [[1, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
{ 'searchable': false, "targets": 0 },
|
||||
{ 'visible': false, 'targets': 7 },
|
||||
{ "type": "html-num-fmt", "targets": 3 }
|
||||
].concat($.fn.dataTable.defaults.columnDefs)
|
||||
var userfieldsColumns = userfields.map(function(userfield) {
|
||||
if (userfield.show_as_column_in_tables != 1) return null;
|
||||
return {
|
||||
data: 'userfields.' + userfield.name,
|
||||
defaultContent: ''
|
||||
};
|
||||
}).filter(function(userfield) {
|
||||
return userfield !== null;
|
||||
});
|
||||
$('#products-table tbody').removeClass("d-none");
|
||||
productsTable.columns.adjust().draw();
|
||||
|
||||
$("#search").on("keyup", Delay(function()
|
||||
{
|
||||
var productsTable = $('#products-table').DataTable({
|
||||
ajax: function(data, callback, settings) {
|
||||
Grocy.FrontendHelpers.BeginUiBusy();
|
||||
|
||||
var query = [];
|
||||
if (GetUriParam('only_in_stock')) {
|
||||
query.push('only_in_stock=true');
|
||||
}
|
||||
if (!GetUriParam('include_disabled')) {
|
||||
query.push('query%5B%5D=' + encodeURIComponent('active=1'));
|
||||
}
|
||||
|
||||
data.columns.forEach(function(column) {
|
||||
var search = column.search.value.trim();
|
||||
if (search.length > 0) {
|
||||
query.push('query%5B%5D=' + encodeURIComponent(column.data + '=' + search));
|
||||
}
|
||||
});
|
||||
|
||||
var search = data.search.value.trim();
|
||||
if (search.length > 0) {
|
||||
query.push('search=' + encodeURIComponent(search));
|
||||
}
|
||||
|
||||
query.push('limit=' + encodeURIComponent(data.length));
|
||||
query.push('offset=' + encodeURIComponent(data.start));
|
||||
query.push('order=' + encodeURIComponent(data.order.map(function(order) {
|
||||
return data.columns[order.column].data + ':' + order.dir;
|
||||
}).join(',')));
|
||||
|
||||
Grocy.Api.Get('objects/products' + (query.length > 0 ? '?' + query.join('&') : ''),
|
||||
function(result, meta) {
|
||||
callback({
|
||||
data: result,
|
||||
recordsTotal: meta.recordsTotal,
|
||||
recordsFiltered: meta.recordsFiltered,
|
||||
});
|
||||
Grocy.FrontendHelpers.EndUiBusy();
|
||||
},
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy();
|
||||
Grocy.FrontendHelpers.ShowGenericError('Server error', xhr.response);
|
||||
}
|
||||
);
|
||||
},
|
||||
paging: true,
|
||||
serverSide: true,
|
||||
deferRender: true,
|
||||
autoWidth: true,
|
||||
order: [
|
||||
[1, 'asc']
|
||||
],
|
||||
columns: [{
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
render: function(data, type, row, meta) {
|
||||
return '<a class="btn btn-info btn-sm" href="/product/' + encodeURIComponent(row.id) + '"' +
|
||||
' data-toggle="tooltip" title="' + __t('Edit this item') + '">' +
|
||||
' <i class="fas fa-edit"></i>' +
|
||||
'</a>' +
|
||||
'<a class="btn btn-danger btn-sm product-delete-button" href="#"' +
|
||||
' data-product-id="' + row.id + '" data-product-name="' + row.name + '"' +
|
||||
' data-toggle="tooltip" title="' + __t('Delete this item') + '">' +
|
||||
' <i class="fas fa-trash"></i>' +
|
||||
'</a>' +
|
||||
'<div class="dropdown d-inline-block">' +
|
||||
' <button class="btn btn-sm btn-light text-secondary" type="button"' +
|
||||
' data-toggle="dropdown">' +
|
||||
' <i class="fas fa-ellipsis-v"></i>' +
|
||||
' </button>' +
|
||||
' <div class="table-inline-menu dropdown-menu dropdown-menu-right">' +
|
||||
' <a class="dropdown-item" type="button"' +
|
||||
' href="/product/new?copy-of=' + encodeURIComponent(row.id) + '">' +
|
||||
' <span class="dropdown-item-text">' + __t('Copy') + '</span>' +
|
||||
' </a>' +
|
||||
' <a class="dropdown-item merge-products-button"' +
|
||||
' data-product-id="' + row.id + '" data-product-name="' + row.name + '"' +
|
||||
' type="button" href="#" >' +
|
||||
' <span class="dropdown-item-text">' + __t('Merge') + '</span>' +
|
||||
' </a>' +
|
||||
' </div>' +
|
||||
'</div>';
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'name',
|
||||
searchable: true,
|
||||
render: function(data, type, row, meta) {
|
||||
return data + (row.picture_file_name ? (
|
||||
' <i class="fas fa-image text-muted" data-toggle="tooltip" ' +
|
||||
'title="' + __t('This product has a picture') + '"></i>'
|
||||
) : '');
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'location.name',
|
||||
defaultContent: '',
|
||||
visible: Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING
|
||||
},
|
||||
{
|
||||
data: 'min_stock_amount',
|
||||
type: 'html-num-fmt',
|
||||
render: function(data, type, row, meta) {
|
||||
return '<span class="locale-number locale-number-quantity-amount">' + data + '</span>';
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'qu_purchase.name',
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
data: 'qu_stock.name',
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
data: 'product_group.name',
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
data: 'shopping_location.name',
|
||||
defaultContent: '',
|
||||
visible: Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING
|
||||
}
|
||||
].concat(userfieldsColumns),
|
||||
});
|
||||
|
||||
productsTable.on('draw', function() {
|
||||
$('[data-toggle=tooltip]').tooltip();
|
||||
$('[data-toggle=dropdown]').dropdown();
|
||||
});
|
||||
|
||||
$('#search').on('keyup', Delay(function() {
|
||||
var value = $(this).val();
|
||||
if (value === "all")
|
||||
{
|
||||
value = "";
|
||||
if (value === 'all') {
|
||||
value = '';
|
||||
}
|
||||
|
||||
productsTable.search(value).draw();
|
||||
}, 200));
|
||||
}, 500));
|
||||
|
||||
$("#product-group-filter").on("change", function()
|
||||
{
|
||||
var value = $("#product-group-filter option:selected").text();
|
||||
if (value === __t("All"))
|
||||
{
|
||||
value = "";
|
||||
$('#product-group-filter').on('change', function() {
|
||||
var value = $('#product-group-filter option:selected').text();
|
||||
if (value === __t('All')) {
|
||||
value = '';
|
||||
}
|
||||
|
||||
productsTable.column(productsTable.colReorder.transpose(6)).search(value).draw();
|
||||
});
|
||||
|
||||
$("#clear-filter-button").on("click", function()
|
||||
{
|
||||
$("#search").val("");
|
||||
$("#product-group-filter").val("all");
|
||||
productsTable.column(productsTable.colReorder.transpose(6)).search("").draw();
|
||||
productsTable.search("").draw();
|
||||
if ($("#show-disabled").is(":checked") || $("#show-only-in-stock").is(":checked"))
|
||||
{
|
||||
$("#show-disabled").prop("checked", false);
|
||||
$("#show-only-in-stock").prop("checked", false);
|
||||
RemoveUriParam("include_disabled");
|
||||
RemoveUriParam("only_in_stock");
|
||||
window.location.reload();
|
||||
$('#clear-filter-button').on('click', function() {
|
||||
$('#search').val('');
|
||||
$('#product-group-filter').val('all');
|
||||
productsTable.column(productsTable.colReorder.transpose(6)).search('');
|
||||
productsTable.search('');
|
||||
if ($('#show-disabled').is(':checked') || $('#show-only-in-stock').is(':checked')) {
|
||||
$('#show-disabled').prop('checked', false);
|
||||
$('#show-only-in-stock').prop('checked', false);
|
||||
RemoveUriParam('include_disabled');
|
||||
RemoveUriParam('only_in_stock');
|
||||
}
|
||||
productsTable.draw();
|
||||
});
|
||||
|
||||
if (typeof GetUriParam("product-group") !== "undefined")
|
||||
{
|
||||
$("#product-group-filter").val(GetUriParam("product-group"));
|
||||
$("#product-group-filter").trigger("change");
|
||||
if (typeof GetUriParam('product-group') !== 'undefined') {
|
||||
$('#product-group-filter').val(GetUriParam('product-group'));
|
||||
$('#product-group-filter').trigger('change');
|
||||
}
|
||||
|
||||
$(document).on('click', '.product-delete-button', function(e)
|
||||
{
|
||||
$(document).on('click', '.product-delete-button', function(e) {
|
||||
var objectName = $(e.currentTarget).attr('data-product-name');
|
||||
var objectId = $(e.currentTarget).attr('data-product-id');
|
||||
|
||||
|
|
@ -72,19 +191,15 @@ $(document).on('click', '.product-delete-button', function(e)
|
|||
className: 'btn-danger'
|
||||
}
|
||||
},
|
||||
callback: function(result)
|
||||
{
|
||||
if (result === true)
|
||||
{
|
||||
callback: function(result) {
|
||||
if (result === true) {
|
||||
jsonData = {};
|
||||
jsonData.active = 0;
|
||||
Grocy.Api.Delete('objects/products/' + objectId, {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
window.location.href = U('/products');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
@ -93,61 +208,96 @@ $(document).on('click', '.product-delete-button', function(e)
|
|||
});
|
||||
});
|
||||
|
||||
$("#show-disabled").change(function()
|
||||
{
|
||||
if (this.checked)
|
||||
{
|
||||
UpdateUriParam("include_disabled", "true");
|
||||
$('#show-disabled').on('change', function() {
|
||||
if (this.checked) {
|
||||
UpdateUriParam('include_disabled', 'true');
|
||||
} else {
|
||||
RemoveUriParam('include_disabled');
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveUriParam("include_disabled");
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
productsTable.draw();
|
||||
});
|
||||
|
||||
$("#show-only-in-stock").change(function()
|
||||
{
|
||||
if (this.checked)
|
||||
{
|
||||
UpdateUriParam("only_in_stock", "true");
|
||||
$('#show-only-in-stock').on('change', function() {
|
||||
if (this.checked) {
|
||||
UpdateUriParam('only_in_stock', 'true');
|
||||
} else {
|
||||
RemoveUriParam('only_in_stock');
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveUriParam("only_in_stock");
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
productsTable.draw();
|
||||
});
|
||||
|
||||
if (GetUriParam('include_disabled'))
|
||||
{
|
||||
$("#show-disabled").prop('checked', true);
|
||||
if (GetUriParam('include_disabled')) {
|
||||
$('#show-disabled').prop('checked', true);
|
||||
}
|
||||
|
||||
$(document).on('click', '.merge-products-button', function(e) {
|
||||
var $button = $(e.currentTarget);
|
||||
var $mergeKeep = $('#merge-products-keep');
|
||||
|
||||
$(".merge-products-button").on("click", function(e)
|
||||
{
|
||||
var productId = $(e.currentTarget).attr("data-product-id");
|
||||
$("#merge-products-keep").val(productId);
|
||||
$("#merge-products-remove").val("");
|
||||
$("#merge-products-modal").modal("show");
|
||||
var optionId = $button.attr('data-product-id');
|
||||
var optionText = $button.attr('data-product-name');
|
||||
|
||||
if ($mergeKeep.find('option[value="' + optionId + '"]').length) {
|
||||
$mergeKeep.val(optionId).trigger('change');
|
||||
} else {
|
||||
var option = new Option(optionText, optionId, true, true);
|
||||
$mergeKeep.append(option).trigger('change');
|
||||
}
|
||||
|
||||
$('#merge-products-remove').val(null).trigger('change');
|
||||
$('#merge-products-modal').modal('show');
|
||||
});
|
||||
|
||||
$("#merge-products-save-button").on("click", function()
|
||||
{
|
||||
var productIdToKeep = $("#merge-products-keep").val();
|
||||
var productIdToRemove = $("#merge-products-remove").val();
|
||||
$('#merge-products-save-button').on('click', function() {
|
||||
var productIdToKeep = $('#merge-products-keep').val();
|
||||
var productIdToRemove = $('#merge-products-remove').val();
|
||||
|
||||
Grocy.Api.Post("stock/products/" + productIdToKeep.toString() + "/merge/" + productIdToRemove.toString(), {},
|
||||
function(result)
|
||||
{
|
||||
Grocy.Api.Post('stock/products/' + productIdToKeep.toString() + '/merge/' + productIdToRemove.toString(), {},
|
||||
function(result) {
|
||||
window.location.href = U('/products');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while merging', xhr.response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$('#merge-products-keep, #merge-products-remove').select2({
|
||||
dropdownParent: $('#merge-products-modal'),
|
||||
ajax: {
|
||||
delay: 150,
|
||||
transport: function(params, success, failure) {
|
||||
var results_per_page = 10;
|
||||
var page = params.data.page || 1;
|
||||
var term = params.data.term || "";
|
||||
|
||||
var query = [];
|
||||
query.push('query%5B%5D=active%3D1');
|
||||
query.push('limit=' + encodeURIComponent(results_per_page));
|
||||
query.push('offset=' + encodeURIComponent((page - 1) * results_per_page));
|
||||
query.push('order=name%3Acollate%20nocase');
|
||||
if (term.length > 0) {
|
||||
query.push('search=' + encodeURIComponent(term));
|
||||
}
|
||||
|
||||
Grocy.Api.Get('objects/products' + (query.length > 0 ? '?' + query.join('&') : ''),
|
||||
function(results, meta) {
|
||||
success({
|
||||
results: results.map(function(result) {
|
||||
return {
|
||||
id: result.id,
|
||||
text: result.name
|
||||
};
|
||||
}),
|
||||
pagination: {
|
||||
more: page * results_per_page < meta.recordsFiltered
|
||||
}
|
||||
});
|
||||
},
|
||||
function(xhr) {
|
||||
failure();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
var CurrentProductDetails;
|
||||
|
||||
$('#save-purchase-button').on('click', function(e)
|
||||
{
|
||||
$('#save-purchase-button').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if ($(".combobox-menu-visible").length)
|
||||
{
|
||||
if ($(".combobox-menu-visible").length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($(".combobox-menu-visible").length)
|
||||
{
|
||||
if ($(".combobox-menu-visible").length) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -19,140 +16,116 @@ $('#save-purchase-button').on('click', function(e)
|
|||
Grocy.FrontendHelpers.BeginUiBusy("purchase-form");
|
||||
|
||||
Grocy.Api.Get('stock/products/' + jsonForm.product_id,
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
var jsonData = {};
|
||||
jsonData.amount = jsonForm.amount;
|
||||
jsonData.stock_label_type = jsonForm.stock_label_type;
|
||||
|
||||
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) {
|
||||
jsonData.price = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
var amount = jsonForm.display_amount;
|
||||
if (BoolVal(productDetails.product.enable_tare_weight_handling))
|
||||
{
|
||||
if (BoolVal(productDetails.product.enable_tare_weight_handling)) {
|
||||
amount -= parseFloat(productDetails.product.tare_weight);
|
||||
}
|
||||
|
||||
var price = parseFloat(jsonForm.price * $("#qu_id option:selected").attr("data-qu-factor")).toFixed(Grocy.UserSettings.stock_decimal_places_prices);
|
||||
if ($("input[name='price-type']:checked").val() == "total-price")
|
||||
{
|
||||
if ($("input[name='price-type']:checked").val() == "total-price") {
|
||||
price = parseFloat(price / amount).toFixed(Grocy.UserSettings.stock_decimal_places_prices);
|
||||
}
|
||||
|
||||
jsonData.price = price;
|
||||
}
|
||||
|
||||
if (BoolVal(Grocy.UserSettings.show_purchased_date_on_purchase))
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.show_purchased_date_on_purchase)) {
|
||||
jsonData.purchased_date = Grocy.Components.DateTimePicker2.GetValue();
|
||||
}
|
||||
|
||||
if (Grocy.Components.DateTimePicker)
|
||||
{
|
||||
if (Grocy.Components.DateTimePicker) {
|
||||
jsonData.best_before_date = Grocy.Components.DateTimePicker.GetValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
jsonData.best_before_date = null;
|
||||
}
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) {
|
||||
jsonData.shopping_location_id = Grocy.Components.ShoppingLocationPicker.GetValue();
|
||||
}
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) {
|
||||
jsonData.location_id = Grocy.Components.LocationPicker.GetValue();
|
||||
}
|
||||
|
||||
Grocy.Api.Post('stock/products/' + jsonForm.product_id + '/add', jsonData,
|
||||
function(result)
|
||||
{
|
||||
if ($("#purchase-form").hasAttr("data-used-barcode"))
|
||||
{
|
||||
Grocy.Api.Put('objects/product_barcodes/' + $("#purchase-form").attr("data-used-barcode"), { last_price: $("#price").val() },
|
||||
function(result)
|
||||
{ },
|
||||
function(xhr)
|
||||
{ }
|
||||
function(result) {
|
||||
if ($("#purchase-form").hasAttr("data-used-barcode")) {
|
||||
Grocy.Api.Put('objects/product_barcodes/' + $("#purchase-form").attr("data-used-barcode"), {
|
||||
last_price: $("#price").val()
|
||||
},
|
||||
function(result) {},
|
||||
function(xhr) {}
|
||||
);
|
||||
}
|
||||
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_purchase_enabled))
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_purchase_enabled)) {
|
||||
Grocy.UISound.Success();
|
||||
}
|
||||
|
||||
if (GetUriParam("flow") == "InplaceAddBarcodeToExistingProduct")
|
||||
{
|
||||
if (GetUriParam("flow") == "InplaceAddBarcodeToExistingProduct") {
|
||||
var jsonDataBarcode = {};
|
||||
jsonDataBarcode.barcode = GetUriParam("barcode");
|
||||
jsonDataBarcode.product_id = jsonForm.product_id;
|
||||
jsonDataBarcode.shopping_location_id = jsonForm.shopping_location_id;
|
||||
|
||||
Grocy.Api.Post('objects/product_barcodes', jsonDataBarcode,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
$("#flow-info-InplaceAddBarcodeToExistingProduct").addClass("d-none");
|
||||
$('#barcode-lookup-disabled-hint').addClass('d-none');
|
||||
$('#barcode-lookup-hint').removeClass('d-none');
|
||||
window.history.replaceState({}, document.title, U("/purchase"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("purchase-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var amountMessage = parseFloat(jsonForm.amount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts });
|
||||
if (BoolVal(productDetails.product.enable_tare_weight_handling))
|
||||
{
|
||||
var amountMessage = parseFloat(jsonForm.amount).toLocaleString({
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts
|
||||
});
|
||||
if (BoolVal(productDetails.product.enable_tare_weight_handling)) {
|
||||
amountMessage = parseFloat(jsonForm.amount) - parseFloat(productDetails.stock_amount) - parseFloat(productDetails.product.tare_weight);
|
||||
}
|
||||
var successMessage = __t('Added %1$s of %2$s to stock', amountMessage + " " + __n(amountMessage, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockTransaction(\'' + result[0].transaction_id + '\')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>';
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
{
|
||||
if (Grocy.Webhooks.labelprinter !== undefined)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER) {
|
||||
if (Grocy.Webhooks.labelprinter !== undefined) {
|
||||
if (jsonForm.stock_label_type == 1) // Single label
|
||||
{
|
||||
var webhookData = {};
|
||||
webhookData.product = productDetails.product.name;
|
||||
webhookData.grocycode = 'grcy:p:' + jsonForm.product_id + ":" + result[0].stock_id;
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) {
|
||||
webhookData.due_date = __t('DD') + ': ' + result[0].best_before_date;
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, webhookData);
|
||||
}
|
||||
else if (jsonForm.stock_label_type == 2) // Label per unit
|
||||
} else if (jsonForm.stock_label_type == 2) // Label per unit
|
||||
{
|
||||
Grocy.Api.Get('stock/transactions/' + result[0].transaction_id,
|
||||
function(stockEntries)
|
||||
{
|
||||
stockEntries.forEach(stockEntry =>
|
||||
{
|
||||
function(stockEntries) {
|
||||
stockEntries.forEach(stockEntry => {
|
||||
var webhookData = {};
|
||||
webhookData.product = productDetails.product.name;
|
||||
webhookData.grocycode = 'grcy:p:' + jsonForm.product_id + ":" + stockEntry.stock_id;
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) {
|
||||
webhookData.due_date = __t('DD') + ': ' + result[0].best_before_date;
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, webhookData);
|
||||
});
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
@ -160,24 +133,19 @@ $('#save-purchase-button').on('click', function(e)
|
|||
}
|
||||
}
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
window.parent.postMessage(WindowMessageBag("ProductChanged", jsonForm.product_id), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("AfterItemAdded", GetUriParam("listitemid")), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", successMessage), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("Ready"), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.FrontendHelpers.EndUiBusy("purchase-form");
|
||||
toastr.success(successMessage);
|
||||
Grocy.Components.ProductPicker.FinishFlow();
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && BoolVal(Grocy.UserSettings.show_warning_on_purchase_when_due_date_is_earlier_than_next))
|
||||
{
|
||||
if (moment(jsonData.best_before_date).isBefore(CurrentProductDetails.next_due_date))
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && BoolVal(Grocy.UserSettings.show_warning_on_purchase_when_due_date_is_earlier_than_next)) {
|
||||
if (moment(jsonData.best_before_date).isBefore(CurrentProductDetails.next_due_date)) {
|
||||
toastr.warning(__t("This is due earlier than already in-stock items"));
|
||||
}
|
||||
}
|
||||
|
|
@ -189,23 +157,19 @@ $('#save-purchase-button').on('click', function(e)
|
|||
$(".input-group-productamountpicker").trigger("change");
|
||||
$('#price').val('');
|
||||
$("#tare-weight-handling-info").addClass("d-none");
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) {
|
||||
Grocy.Components.LocationPicker.Clear();
|
||||
}
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) {
|
||||
Grocy.Components.DateTimePicker.Clear();
|
||||
}
|
||||
Grocy.Components.ProductPicker.SetValue('');
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
Grocy.Components.ProductPicker.Clear();
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) {
|
||||
Grocy.Components.ShoppingLocationPicker.SetValue('');
|
||||
}
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
Grocy.Components.ProductCard.Refresh(jsonForm.product_id);
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER) {
|
||||
$("#stock_label_type").val(0);
|
||||
}
|
||||
|
||||
|
|
@ -217,85 +181,66 @@ $('#save-purchase-button').on('click', function(e)
|
|||
Grocy.FrontendHelpers.ValidateForm('purchase-form');
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("purchase-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("purchase-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (Grocy.Components.ProductPicker !== undefined)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_purchase_enabled))
|
||||
{
|
||||
if (Grocy.Components.ProductPicker !== undefined) {
|
||||
Grocy.Components.ProductPicker.OnChange(function(e) {
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_purchase_enabled)) {
|
||||
Grocy.UISound.BarcodeScannerBeep();
|
||||
}
|
||||
|
||||
var productId = $(e.target).val();
|
||||
|
||||
if (productId)
|
||||
{
|
||||
if (productId) {
|
||||
Grocy.Components.ProductCard.Refresh(productId);
|
||||
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
CurrentProductDetails = productDetails;
|
||||
|
||||
Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id);
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1) {
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(productDetails.quantity_unit_stock.id);
|
||||
$("#qu_id").attr("disabled", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(productDetails.default_quantity_unit_purchase.id);
|
||||
}
|
||||
$('#display_amount').val(parseFloat(Grocy.UserSettings.stock_default_purchase_amount));
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
|
||||
if (GetUriParam("flow") === "shoppinglistitemtostock")
|
||||
{
|
||||
if (GetUriParam("flow") === "shoppinglistitemtostock") {
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(GetUriParam("quId"));
|
||||
$('#display_amount').val(parseFloat(GetUriParam("amount") * $("#qu_id option:selected").attr("data-qu-factor")));
|
||||
}
|
||||
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
if (productDetails.last_shopping_location_id != null)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) {
|
||||
if (productDetails.last_shopping_location_id != null) {
|
||||
Grocy.Components.ShoppingLocationPicker.SetId(productDetails.last_shopping_location_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.Components.ShoppingLocationPicker.SetId(productDetails.default_shopping_location_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) {
|
||||
Grocy.Components.LocationPicker.SetId(productDetails.location.id);
|
||||
}
|
||||
|
||||
if (productDetails.last_price == null || productDetails.last_price == 0)
|
||||
{
|
||||
if (productDetails.last_price == null || productDetails.last_price == 0) {
|
||||
$("#price").val("")
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#price').val(parseFloat(productDetails.last_price / $("#qu_id option:selected").attr("data-qu-factor")));
|
||||
}
|
||||
|
||||
|
|
@ -305,65 +250,52 @@ if (Grocy.Components.ProductPicker !== undefined)
|
|||
|
||||
refreshPriceHint();
|
||||
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1) {
|
||||
var minAmount = parseFloat(productDetails.product.tare_weight) / $("#qu_id option:selected").attr("data-qu-factor") + parseFloat(productDetails.stock_amount);
|
||||
$("#display_amount").attr("min", minAmount);
|
||||
$("#tare-weight-handling-info").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#display_amount").attr("min", Grocy.DefaultMinAmount);
|
||||
$("#tare-weight-handling-info").addClass("d-none");
|
||||
}
|
||||
|
||||
PrefillBestBeforeDate(productDetails.product, productDetails.location);
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER) {
|
||||
$("#stock_label_type").val(productDetails.product.default_stock_label_type);
|
||||
}
|
||||
|
||||
$("#display_amount").focus();
|
||||
|
||||
Grocy.FrontendHelpers.ValidateForm('purchase-form');
|
||||
if (GetUriParam("flow") === "shoppinglistitemtostock" && BoolVal(Grocy.UserSettings.shopping_list_to_stock_workflow_auto_submit_when_prefilled) && document.getElementById("purchase-form").checkValidity() === true)
|
||||
{
|
||||
if (GetUriParam("flow") === "shoppinglistitemtostock" && BoolVal(Grocy.UserSettings.shopping_list_to_stock_workflow_auto_submit_when_prefilled) && document.getElementById("purchase-form").checkValidity() === true) {
|
||||
$("#save-purchase-button").click();
|
||||
}
|
||||
|
||||
RefreshLocaleNumberInput();
|
||||
|
||||
if (document.getElementById("product_id").getAttribute("barcode") != "null")
|
||||
{
|
||||
if (document.getElementById("product_id").getAttribute("barcode") != "null") {
|
||||
Grocy.Api.Get('objects/product_barcodes?query[]=barcode=' + document.getElementById("product_id").getAttribute("barcode"),
|
||||
function(barcodeResult)
|
||||
{
|
||||
if (barcodeResult != null)
|
||||
{
|
||||
function(barcodeResult) {
|
||||
if (barcodeResult != null) {
|
||||
var barcode = barcodeResult[0];
|
||||
$("#purchase-form").attr("data-used-barcode", barcode.id);
|
||||
|
||||
if (barcode != null)
|
||||
{
|
||||
if (barcode.amount != null && !barcode.amount.isEmpty())
|
||||
{
|
||||
if (barcode != null) {
|
||||
if (barcode.amount != null && !barcode.amount.isEmpty()) {
|
||||
$("#display_amount").val(barcode.amount);
|
||||
$("#display_amount").select();
|
||||
}
|
||||
|
||||
if (barcode.qu_id != null && !barcode.qu_id.isEmpty())
|
||||
{
|
||||
if (barcode.qu_id != null && !barcode.qu_id.isEmpty()) {
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(barcode.qu_id);
|
||||
}
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING && barcode.shopping_location_id != null)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING && barcode.shopping_location_id != null) {
|
||||
Grocy.Components.ShoppingLocationPicker.SetId(barcode.shopping_location_id);
|
||||
}
|
||||
|
||||
if (barcode.last_price != null && !barcode.last_price.isEmpty())
|
||||
{
|
||||
if (barcode.last_price != null && !barcode.last_price.isEmpty()) {
|
||||
$("#price").val(barcode.last_price);
|
||||
$("#price-type-total-price").click();
|
||||
}
|
||||
|
|
@ -376,22 +308,18 @@ if (Grocy.Components.ProductPicker !== undefined)
|
|||
|
||||
ScanModeSubmit(false);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#purchase-form").removeAttr("data-used-barcode");
|
||||
ScanModeSubmit();
|
||||
}
|
||||
|
||||
$('#display_amount').trigger("keyup");
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
@ -399,51 +327,36 @@ if (Grocy.Components.ProductPicker !== undefined)
|
|||
});
|
||||
}
|
||||
|
||||
function PrefillBestBeforeDate(product, location)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
function PrefillBestBeforeDate(product, location) {
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) {
|
||||
var dueDays;
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRODUCT_FREEZING && BoolVal(location.is_freezer))
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRODUCT_FREEZING && BoolVal(location.is_freezer)) {
|
||||
dueDays = product.default_best_before_days_after_freezing;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
dueDays = product.default_best_before_days;
|
||||
}
|
||||
|
||||
dueDays = parseFloat(dueDays);
|
||||
if (dueDays != 0)
|
||||
{
|
||||
if (dueDays == -1)
|
||||
{
|
||||
if (!$("#datetimepicker-shortcut").is(":checked"))
|
||||
{
|
||||
if (dueDays != 0) {
|
||||
if (dueDays == -1) {
|
||||
if (!$("#datetimepicker-shortcut").is(":checked")) {
|
||||
$("#datetimepicker-shortcut").click();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.Components.DateTimePicker.SetValue(moment().add(dueDays, 'days').format('YYYY-MM-DD'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Grocy.Components.LocationPicker !== undefined)
|
||||
{
|
||||
Grocy.Components.LocationPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRODUCT_FREEZING)
|
||||
{
|
||||
if (Grocy.Components.LocationPicker !== undefined) {
|
||||
Grocy.Components.LocationPicker.GetPicker().on('change', function(e) {
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRODUCT_FREEZING) {
|
||||
Grocy.Api.Get('objects/locations/' + Grocy.Components.LocationPicker.GetValue(),
|
||||
function(location)
|
||||
{
|
||||
function(location) {
|
||||
PrefillBestBeforeDate(CurrentProductDetails.product, location);
|
||||
},
|
||||
function(xhr)
|
||||
{ }
|
||||
function(xhr) {}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -454,47 +367,35 @@ RefreshLocaleNumberInput();
|
|||
$(".input-group-productamountpicker").trigger("change");
|
||||
Grocy.FrontendHelpers.ValidateForm('purchase-form');
|
||||
|
||||
if (Grocy.Components.ProductPicker)
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.InAnyFlow() === false && GetUriParam("embedded") === undefined)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
if (Grocy.Components.ProductPicker) {
|
||||
if (Grocy.Components.ProductPicker.InAnyFlow() === false && GetUriParam("embedded") === undefined) {
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
} else {
|
||||
Grocy.Components.ProductPicker.Validate();
|
||||
|
||||
if (Grocy.Components.ProductPicker.InProductModifyWorkflow())
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
if (Grocy.Components.ProductPicker.InProductModifyWorkflow()) {
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$('#display_amount').on('focus', function(e)
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetValue().length === 0)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#display_amount').on('focus', function(e) {
|
||||
if (Grocy.Components.ProductPicker.GetValue().length === 0) {
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
} else {
|
||||
$(this).select();
|
||||
}
|
||||
});
|
||||
|
||||
$('#price').on('focus', function(e)
|
||||
{
|
||||
$('#price').on('focus', function(e) {
|
||||
$(this).select();
|
||||
});
|
||||
|
||||
$('#purchase-form input').keyup(function(event)
|
||||
{
|
||||
$('#purchase-form input').keyup(function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('purchase-form');
|
||||
});
|
||||
|
||||
$('#purchase-form input').keydown(function(event)
|
||||
{
|
||||
$('#purchase-form input').keydown(function(event) {
|
||||
if (event.keyCode === 13) //Enter
|
||||
{
|
||||
event.preventDefault();
|
||||
|
|
@ -502,191 +403,154 @@ $('#purchase-form input').keydown(function(event)
|
|||
if (document.getElementById('purchase-form').checkValidity() === false) //There is at least one validation error
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#save-purchase-button').click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (Grocy.Components.DateTimePicker)
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('change', function(e)
|
||||
{
|
||||
if (Grocy.Components.DateTimePicker) {
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('change', function(e) {
|
||||
Grocy.FrontendHelpers.ValidateForm('purchase-form');
|
||||
});
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('keypress', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('keypress', function(e) {
|
||||
Grocy.FrontendHelpers.ValidateForm('purchase-form');
|
||||
});
|
||||
}
|
||||
|
||||
if (Grocy.Components.DateTimePicker2)
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().on('change', function(e)
|
||||
{
|
||||
if (Grocy.Components.DateTimePicker2) {
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().on('change', function(e) {
|
||||
Grocy.FrontendHelpers.ValidateForm('purchase-form');
|
||||
});
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().on('keypress', function(e)
|
||||
{
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().on('keypress', function(e) {
|
||||
Grocy.FrontendHelpers.ValidateForm('purchase-form');
|
||||
});
|
||||
|
||||
Grocy.Components.DateTimePicker2.GetInputElement().trigger("input");
|
||||
}
|
||||
|
||||
$('#price').on('keyup', function(e)
|
||||
{
|
||||
$('#price').on('keyup', function(e) {
|
||||
refreshPriceHint();
|
||||
});
|
||||
|
||||
$('#price-type-unit-price').on('change', function(e)
|
||||
{
|
||||
$('#price-type-unit-price').on('change', function(e) {
|
||||
refreshPriceHint();
|
||||
});
|
||||
|
||||
$('#price-type-total-price').on('change', function(e)
|
||||
{
|
||||
$('#price-type-total-price').on('change', function(e) {
|
||||
refreshPriceHint();
|
||||
});
|
||||
|
||||
$('#display_amount').on('change', function(e)
|
||||
{
|
||||
$('#display_amount').on('change', function(e) {
|
||||
refreshPriceHint();
|
||||
Grocy.FrontendHelpers.ValidateForm('purchase-form');
|
||||
});
|
||||
|
||||
function refreshPriceHint()
|
||||
{
|
||||
if ($('#amount').val() == 0 || $('#price').val() == 0)
|
||||
{
|
||||
function refreshPriceHint() {
|
||||
if ($('#amount').val() == 0 || $('#price').val() == 0) {
|
||||
$('#price-hint').text("");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($("input[name='price-type']:checked").val() == "total-price" || $("#qu_id").attr("data-destination-qu-name") != $("#qu_id option:selected").text())
|
||||
{
|
||||
if ($("input[name='price-type']:checked").val() == "total-price" || $("#qu_id").attr("data-destination-qu-name") != $("#qu_id option:selected").text()) {
|
||||
var amount = $('#display_amount').val();
|
||||
if (BoolVal(CurrentProductDetails.product.enable_tare_weight_handling))
|
||||
{
|
||||
if (BoolVal(CurrentProductDetails.product.enable_tare_weight_handling)) {
|
||||
amount -= parseFloat(CurrentProductDetails.product.tare_weight);
|
||||
}
|
||||
|
||||
var price = parseFloat($('#price').val() * $("#qu_id option:selected").attr("data-qu-factor")).toFixed(Grocy.UserSettings.stock_decimal_places_prices);
|
||||
if ($("input[name='price-type']:checked").val() == "total-price")
|
||||
{
|
||||
if ($("input[name='price-type']:checked").val() == "total-price") {
|
||||
price = parseFloat(price / amount).toFixed(Grocy.UserSettings.stock_decimal_places_prices);
|
||||
}
|
||||
|
||||
$('#price-hint').text(__t('means %1$s per %2$s', price.toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }), $("#qu_id").attr("data-destination-qu-name")));
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#price-hint').text(__t('means %1$s per %2$s', price.toLocaleString(undefined, {
|
||||
style: "currency",
|
||||
currency: Grocy.Currency,
|
||||
minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices,
|
||||
maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices
|
||||
}), $("#qu_id").attr("data-destination-qu-name")));
|
||||
} else {
|
||||
$('#price-hint').text("");
|
||||
}
|
||||
};
|
||||
|
||||
function UndoStockBooking(bookingId)
|
||||
{
|
||||
function UndoStockBooking(bookingId) {
|
||||
Grocy.Api.Post('stock/bookings/' + bookingId.toString() + '/undo', {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
toastr.success(__t("Booking successfully undone"));
|
||||
|
||||
Grocy.Api.Get('stock/bookings/' + bookingId.toString(),
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
window.postMessage(WindowMessageBag("ProductChanged", result.product_id), Grocy.BaseUrl);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function UndoStockTransaction(transactionId)
|
||||
{
|
||||
function UndoStockTransaction(transactionId) {
|
||||
Grocy.Api.Post('stock/transactions/' + transactionId.toString() + '/undo', {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
toastr.success(__t("Transaction successfully undone"));
|
||||
|
||||
Grocy.Api.Get('stock/transactions/' + transactionId.toString(),
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
window.postMessage(WindowMessageBag("ProductChanged", result[0].product_id), Grocy.BaseUrl);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$("#scan-mode").on("change", function(e)
|
||||
{
|
||||
if ($(this).prop("checked"))
|
||||
{
|
||||
$("#scan-mode").on("change", function(e) {
|
||||
if ($(this).prop("checked")) {
|
||||
Grocy.UISound.AskForPermission();
|
||||
}
|
||||
});
|
||||
|
||||
$("#scan-mode-button").on("click", function(e)
|
||||
{
|
||||
$("#scan-mode-button").on("click", function(e) {
|
||||
document.activeElement.blur();
|
||||
$("#scan-mode").click();
|
||||
$("#scan-mode-button").toggleClass("btn-success").toggleClass("btn-danger");
|
||||
if ($("#scan-mode").prop("checked"))
|
||||
{
|
||||
if ($("#scan-mode").prop("checked")) {
|
||||
$("#scan-mode-status").text(__t("on"));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#scan-mode-status").text(__t("off"));
|
||||
}
|
||||
});
|
||||
|
||||
$('#qu_id').on('change', function(e)
|
||||
{
|
||||
$('#qu_id').on('change', function(e) {
|
||||
var priceTypeUnitPrice = $("#price-type-unit-price");
|
||||
var priceTypeUnitPriceLabel = $("[for=" + priceTypeUnitPrice.attr("id") + "]");
|
||||
priceTypeUnitPriceLabel.text($("#qu_id option:selected").text() + " " + __t("price"));
|
||||
refreshPriceHint();
|
||||
});
|
||||
|
||||
function ScanModeSubmit(singleUnit = true)
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_purchase_enabled))
|
||||
{
|
||||
if (singleUnit)
|
||||
{
|
||||
function ScanModeSubmit(singleUnit = true) {
|
||||
if (BoolVal(Grocy.UserSettings.scan_mode_purchase_enabled)) {
|
||||
if (singleUnit) {
|
||||
$("#display_amount").val(1);
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
}
|
||||
|
||||
Grocy.FrontendHelpers.ValidateForm("purchase-form");
|
||||
if (document.getElementById("purchase-form").checkValidity() === true)
|
||||
{
|
||||
if (document.getElementById("purchase-form").checkValidity() === true) {
|
||||
$('#save-purchase-button').click();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
toastr.warning(__t("Scan mode is on but not all required fields could be populated automatically"));
|
||||
Grocy.UISound.Error();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
Grocy.RecipePosFormInitialLoadDone = false;
|
||||
|
||||
$('#save-recipe-pos-button').on('click', function(e)
|
||||
{
|
||||
$('#save-recipe-pos-button').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if ($(".combobox-menu-visible").length)
|
||||
{
|
||||
if ($(".combobox-menu-visible").length) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -15,31 +13,24 @@ $('#save-recipe-pos-button').on('click', function(e)
|
|||
|
||||
Grocy.FrontendHelpers.BeginUiBusy("recipe-pos-form");
|
||||
|
||||
if (Grocy.EditMode === 'create')
|
||||
{
|
||||
if (Grocy.EditMode === 'create') {
|
||||
Grocy.Api.Post('objects/recipes_pos', jsonData,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
window.parent.postMessage(WindowMessageBag("IngredientsChanged"), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("recipe-pos-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.Api.Put('objects/recipes_pos/' + Grocy.EditObjectId, jsonData,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
window.parent.postMessage(WindowMessageBag("IngredientsChanged"), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("recipe-pos-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
|
|
@ -47,33 +38,25 @@ $('#save-recipe-pos-button').on('click', function(e)
|
|||
}
|
||||
});
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
Grocy.Components.ProductPicker.OnChange(function(e) {
|
||||
var productId = $(e.target).val();
|
||||
|
||||
if (productId)
|
||||
{
|
||||
if (productId) {
|
||||
Grocy.Components.ProductCard.Refresh(productId);
|
||||
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function(productDetails)
|
||||
{
|
||||
if (!Grocy.RecipePosFormInitialLoadDone)
|
||||
{
|
||||
function(productDetails) {
|
||||
if (!Grocy.RecipePosFormInitialLoadDone) {
|
||||
Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id);
|
||||
}
|
||||
|
||||
if (Grocy.Mode == "create")
|
||||
{
|
||||
if (Grocy.Mode == "create") {
|
||||
$("#not_check_stock_fulfillment").prop("checked", productDetails.product.not_check_stock_fulfillment_for_recipes == 1);
|
||||
}
|
||||
|
||||
if (!$("#only_check_single_unit_in_stock").prop("checked") && Grocy.RecipePosFormInitialLoadDone)
|
||||
{
|
||||
if (!$("#only_check_single_unit_in_stock").prop("checked") && Grocy.RecipePosFormInitialLoadDone) {
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(productDetails.quantity_unit_stock.id);
|
||||
}
|
||||
|
||||
|
|
@ -81,8 +64,7 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
Grocy.FrontendHelpers.ValidateForm('recipe-pos-form');
|
||||
Grocy.RecipePosFormInitialLoadDone = true;
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
@ -91,41 +73,32 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
|
||||
Grocy.FrontendHelpers.ValidateForm('recipe-pos-form');
|
||||
|
||||
if (Grocy.Components.ProductPicker.InProductAddWorkflow() === false)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
if (Grocy.Components.ProductPicker.InProductAddWorkflow() === false) {
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
}
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
Grocy.Components.ProductPicker.Validate();
|
||||
|
||||
if (Grocy.EditMode == "create")
|
||||
{
|
||||
if (Grocy.EditMode == "create") {
|
||||
Grocy.RecipePosFormInitialLoadDone = true;
|
||||
}
|
||||
|
||||
$('#display_amount').on('focus', function(e)
|
||||
{
|
||||
if (Grocy.Components.ProductPicker.GetValue().length === 0)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#display_amount').on('focus', function(e) {
|
||||
if (Grocy.Components.ProductPicker.GetValue().length === 0) {
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
} else {
|
||||
$(this).select();
|
||||
}
|
||||
});
|
||||
|
||||
$('#recipe-pos-form input').keyup(function(event)
|
||||
{
|
||||
$('#recipe-pos-form input').keyup(function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('recipe-pos-form');
|
||||
});
|
||||
|
||||
$('#qu_id').change(function(event)
|
||||
{
|
||||
$('#qu_id').change(function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('recipe-pos-form');
|
||||
});
|
||||
|
||||
$('#recipe-pos-form input').keydown(function(event)
|
||||
{
|
||||
$('#recipe-pos-form input').keydown(function(event) {
|
||||
if (event.keyCode === 13) //Enter
|
||||
{
|
||||
event.preventDefault();
|
||||
|
|
@ -133,32 +106,25 @@ $('#recipe-pos-form input').keydown(function(event)
|
|||
if (document.getElementById('recipe-pos-form').checkValidity() === false) //There is at least one validation error
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#save-recipe-pos-button').click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#only_check_single_unit_in_stock").on("change", function()
|
||||
{
|
||||
if (this.checked)
|
||||
{
|
||||
$("#only_check_single_unit_in_stock").on("change", function() {
|
||||
if (this.checked) {
|
||||
$("#display_amount").attr("min", Grocy.DefaultMinAmount);
|
||||
Grocy.Components.ProductAmountPicker.AllowAnyQu(true);
|
||||
Grocy.FrontendHelpers.ValidateForm("recipe-pos-form");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#display_amount").attr("min", "0");
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger("change"); // Selects the default quantity unit of the selected product
|
||||
Grocy.Components.ProductPicker.Validate(); // Selects the default quantity unit of the selected product
|
||||
Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = false;
|
||||
Grocy.FrontendHelpers.ValidateForm("recipe-pos-form");
|
||||
}
|
||||
});
|
||||
|
||||
if ($("#only_check_single_unit_in_stock").prop("checked"))
|
||||
{
|
||||
if ($("#only_check_single_unit_in_stock").prop("checked")) {
|
||||
Grocy.Components.ProductAmountPicker.AllowAnyQu(true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,40 @@
|
|||
Grocy.ShoppingListItemFormInitialLoadDone = false;
|
||||
|
||||
$('#save-shoppinglist-button').on('click', function(e)
|
||||
{
|
||||
$('#save-shoppinglist-button').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if ($(".combobox-menu-visible").length)
|
||||
{
|
||||
if ($(".combobox-menu-visible").length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var jsonData = $('#shoppinglist-form').serializeJSON();
|
||||
var displayAmount = parseFloat(jsonData.display_amount);
|
||||
if (!jsonData.product_id)
|
||||
{
|
||||
if (!jsonData.product_id) {
|
||||
jsonData.amount = jsonData.display_amount;
|
||||
}
|
||||
delete jsonData.display_amount;
|
||||
|
||||
Grocy.FrontendHelpers.BeginUiBusy("shoppinglist-form");
|
||||
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct")
|
||||
{
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct") {
|
||||
var jsonDataBarcode = {};
|
||||
jsonDataBarcode.barcode = GetUriParam("barcode");
|
||||
jsonDataBarcode.product_id = jsonData.product_id;
|
||||
|
||||
Grocy.Api.Post('objects/product_barcodes', jsonDataBarcode,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
$("#flow-info-InplaceAddBarcodeToExistingProduct").addClass("d-none");
|
||||
$('#barcode-lookup-disabled-hint').addClass('d-none');
|
||||
$('#barcode-lookup-hint').removeClass('d-none');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("shoppinglist-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (GetUriParam("updateexistingproduct") !== undefined)
|
||||
{
|
||||
if (GetUriParam("updateexistingproduct") !== undefined) {
|
||||
jsonData.product_amount = jsonData.amount;
|
||||
delete jsonData.amount;
|
||||
|
||||
|
|
@ -49,118 +42,96 @@ $('#save-shoppinglist-button').on('click', function(e)
|
|||
delete jsonData.shopping_list_id;
|
||||
|
||||
Grocy.Api.Post('stock/shoppinglist/add-product', jsonData,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
Grocy.EditObjectId = result.created_object_id;
|
||||
Grocy.Components.UserfieldsForm.Save();
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
Grocy.Api.Get('stock/products/' + jsonData.product_id,
|
||||
function(productDetails)
|
||||
{
|
||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural"), true), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
||||
function(productDetails) {
|
||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts
|
||||
}) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural"), true), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
window.location.href = U('/shoppinglist?list=' + $("#shopping_list_id").val().toString());
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("shoppinglist-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
else if (Grocy.EditMode === 'create')
|
||||
{
|
||||
} else if (Grocy.EditMode === 'create') {
|
||||
Grocy.Api.Post('objects/shopping_list', jsonData,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
Grocy.EditObjectId = result.created_object_id;
|
||||
Grocy.Components.UserfieldsForm.Save();
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (jsonData.product_id)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
if (jsonData.product_id) {
|
||||
Grocy.Api.Get('stock/products/' + jsonData.product_id,
|
||||
function(productDetails)
|
||||
{
|
||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural"), true), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
||||
function(productDetails) {
|
||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts
|
||||
}) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural"), true), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
window.location.href = U('/shoppinglist?list=' + $("#shopping_list_id").val().toString());
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("shoppinglist-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.Api.Put('objects/shopping_list/' + Grocy.EditObjectId, jsonData,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
Grocy.Components.UserfieldsForm.Save();
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (jsonData.product_id)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
if (jsonData.product_id) {
|
||||
Grocy.Api.Get('stock/products/' + jsonData.product_id,
|
||||
function(productDetails)
|
||||
{
|
||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural"), true), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
||||
function(productDetails) {
|
||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", __t("Added %1$s of %2$s to the shopping list \"%3$s\"", displayAmount.toLocaleString({
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts
|
||||
}) + " " + __n(displayAmount, $("#qu_id option:selected").text(), $("#qu_id option:selected").attr("data-qu-name-plural"), true), productDetails.product.name, $("#shopping_list_id option:selected").text())), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
window.parent.postMessage(WindowMessageBag("ShoppingListChanged", $("#shopping_list_id").val().toString()), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
window.location.href = U('/shoppinglist?list=' + $("#shopping_list_id").val().toString());
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("shoppinglist-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
|
|
@ -168,74 +139,57 @@ $('#save-shoppinglist-button').on('click', function(e)
|
|||
}
|
||||
});
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
Grocy.Components.ProductPicker.OnChange(function(e) {
|
||||
var productId = $(e.target).val();
|
||||
|
||||
if (productId)
|
||||
{
|
||||
if (productId) {
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function(productDetails)
|
||||
{
|
||||
if (!Grocy.ShoppingListItemFormInitialLoadDone)
|
||||
{
|
||||
function(productDetails) {
|
||||
if (!Grocy.ShoppingListItemFormInitialLoadDone) {
|
||||
Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id);
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(productDetails.default_quantity_unit_purchase.id);
|
||||
}
|
||||
|
||||
if ($("#display_amount").val().toString().isEmpty())
|
||||
{
|
||||
if ($("#display_amount").val().toString().isEmpty()) {
|
||||
$("#display_amount").val(1);
|
||||
$("#display_amount").trigger("change");
|
||||
}
|
||||
|
||||
$('#display_amount').focus();
|
||||
$('#display_amount').trigger('focus');
|
||||
Grocy.FrontendHelpers.ValidateForm('shoppinglist-form');
|
||||
Grocy.ShoppingListItemFormInitialLoadDone = true;
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: what is the point of this?
|
||||
$("#note").trigger("input");
|
||||
$("#product_id").trigger("input");
|
||||
// Grocy.Components.ProductPicker.GetPicker().trigger("input");
|
||||
});
|
||||
|
||||
Grocy.FrontendHelpers.ValidateForm('shoppinglist-form');
|
||||
setTimeout(function()
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
setTimeout(function() {
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
}, 250);
|
||||
|
||||
if (Grocy.EditMode === "edit")
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
}
|
||||
|
||||
if (Grocy.EditMode == "create")
|
||||
{
|
||||
if (Grocy.EditMode == "create") {
|
||||
Grocy.ShoppingListItemFormInitialLoadDone = true;
|
||||
}
|
||||
|
||||
$('#display_amount').on('focus', function(e)
|
||||
{
|
||||
$(this).select();
|
||||
$('#display_amount').on('focus', function(e) {
|
||||
$(this).trigger('select');
|
||||
});
|
||||
|
||||
$('#shoppinglist-form input').keyup(function(event)
|
||||
{
|
||||
$('#shoppinglist-form input').on('keyup', function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('shoppinglist-form');
|
||||
});
|
||||
|
||||
$('#shoppinglist-form input').keydown(function(event)
|
||||
{
|
||||
$('#shoppinglist-form input').on('keydown', function(event) {
|
||||
if (event.keyCode === 13) //Enter
|
||||
{
|
||||
event.preventDefault();
|
||||
|
|
@ -243,52 +197,48 @@ $('#shoppinglist-form input').keydown(function(event)
|
|||
if (document.getElementById('shoppinglist-form').checkValidity() === false) //There is at least one validation error
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#save-shoppinglist-button').click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (GetUriParam("list") !== undefined)
|
||||
{
|
||||
if (GetUriParam("list") !== undefined) {
|
||||
$("#shopping_list_id").val(GetUriParam("list"));
|
||||
}
|
||||
|
||||
if (GetUriParam("amount") !== undefined)
|
||||
{
|
||||
if (GetUriParam("amount") !== undefined) {
|
||||
$("#display_amount").val(parseFloat(GetUriParam("amount")));
|
||||
RefreshLocaleNumberInput();
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
Grocy.FrontendHelpers.ValidateForm('shoppinglist-form');
|
||||
}
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (GetUriParam("product") !== undefined)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
$("#display_amount").focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
if (GetUriParam("product") !== undefined) {
|
||||
$("#display_amount").trigger('focus');
|
||||
} else {
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
var eitherRequiredFields = $("#product_id,#product_id_text_input,#note");
|
||||
eitherRequiredFields.prop('required', "");
|
||||
eitherRequiredFields.on('input', function()
|
||||
{
|
||||
eitherRequiredFields.not(this).prop('required', !$(this).val().length);
|
||||
$("#note").prop('required', "");
|
||||
$("#note").on('input', function() {
|
||||
if (!$(this).val().length) {
|
||||
Grocy.Components.ProductPicker.Require();
|
||||
} else {
|
||||
Grocy.Components.ProductPicker.Optional();
|
||||
}
|
||||
Grocy.FrontendHelpers.ValidateForm('shoppinglist-form');
|
||||
});
|
||||
|
||||
Grocy.Components.ProductPicker.OnChange(function() {
|
||||
$("#note").prop('required', !$(this).val().length);
|
||||
Grocy.FrontendHelpers.ValidateForm('shoppinglist-form');
|
||||
});
|
||||
|
||||
if (GetUriParam("product-name") != null)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
if (GetUriParam("product-name") != null) {
|
||||
Grocy.Components.ProductPicker.Validate();
|
||||
}
|
||||
|
||||
Grocy.Components.UserfieldsForm.Load();
|
||||
|
|
|
|||
|
|
@ -1,50 +1,68 @@
|
|||
var stockEntriesTable = $('#stockentries-table').DataTable({
|
||||
'order': [[2, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
{ 'searchable': false, "targets": 0 },
|
||||
{ 'visible': false, 'targets': 10 },
|
||||
{ "type": "num", "targets": 1 },
|
||||
{ "type": "num", "targets": 3 },
|
||||
{ "type": "html", "targets": 4 },
|
||||
{ "type": "html-num-fmt", "targets": 7 },
|
||||
{ "type": "html", "targets": 8 },
|
||||
{ "type": "html", "targets": 9 }
|
||||
'order': [
|
||||
[2, 'asc']
|
||||
],
|
||||
'columnDefs': [{
|
||||
'orderable': false,
|
||||
'targets': 0
|
||||
},
|
||||
{
|
||||
'searchable': false,
|
||||
"targets": 0
|
||||
},
|
||||
{
|
||||
'visible': false,
|
||||
'targets': 10
|
||||
},
|
||||
{
|
||||
"type": "num",
|
||||
"targets": 1
|
||||
},
|
||||
{
|
||||
"type": "num",
|
||||
"targets": 3
|
||||
},
|
||||
{
|
||||
"type": "html",
|
||||
"targets": 4
|
||||
},
|
||||
{
|
||||
"type": "html-num-fmt",
|
||||
"targets": 7
|
||||
},
|
||||
{
|
||||
"type": "html",
|
||||
"targets": 8
|
||||
},
|
||||
{
|
||||
"type": "html",
|
||||
"targets": 9
|
||||
}
|
||||
].concat($.fn.dataTable.defaults.columnDefs)
|
||||
});
|
||||
$('#stockentries-table tbody').removeClass("d-none");
|
||||
stockEntriesTable.columns.adjust().draw();
|
||||
|
||||
$.fn.dataTable.ext.search.push(function(settings, data, dataIndex)
|
||||
{
|
||||
$.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
|
||||
var productId = Grocy.Components.ProductPicker.GetValue();
|
||||
|
||||
if ((isNaN(productId) || productId == "" || productId == data[1]))
|
||||
{
|
||||
if ((isNaN(productId) || productId == "" || productId == data[1])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$("#clear-filter-button").on("click", function()
|
||||
{
|
||||
$("#clear-filter-button").on("click", function() {
|
||||
Grocy.Components.ProductPicker.Clear();
|
||||
stockEntriesTable.draw();
|
||||
});
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
Grocy.Components.ProductPicker.OnChange(function(e) {
|
||||
stockEntriesTable.draw();
|
||||
});
|
||||
|
||||
Grocy.Components.ProductPicker.GetInputElement().on('keyup', function(e)
|
||||
{
|
||||
stockEntriesTable.draw();
|
||||
});
|
||||
|
||||
$(document).on('click', '.stock-consume-button', function(e)
|
||||
{
|
||||
$(document).on('click', '.stock-consume-button', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Remove the focus from the current button
|
||||
|
|
@ -61,15 +79,21 @@ $(document).on('click', '.stock-consume-button', function(e)
|
|||
|
||||
var wasSpoiled = $(e.currentTarget).hasClass("stock-consume-button-spoiled");
|
||||
|
||||
Grocy.Api.Post('stock/products/' + productId + '/consume', { 'amount': consumeAmount, 'spoiled': wasSpoiled, 'location_id': locationId, 'stock_entry_id': specificStockEntryId, 'exact_amount': true },
|
||||
function(bookingResponse)
|
||||
{
|
||||
Grocy.Api.Post('stock/products/' + productId + '/consume', {
|
||||
'amount': consumeAmount,
|
||||
'spoiled': wasSpoiled,
|
||||
'location_id': locationId,
|
||||
'stock_entry_id': specificStockEntryId,
|
||||
'exact_amount': true
|
||||
},
|
||||
function(bookingResponse) {
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function(result)
|
||||
{
|
||||
var toastMessage = __t('Removed %1$s of %2$s from stock', parseFloat(consumeAmount).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }) + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural, true), result.product.name) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockBookingEntry(' + bookingResponse[0].id + ',' + stockRowId + ')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>';
|
||||
if (wasSpoiled)
|
||||
{
|
||||
function(result) {
|
||||
var toastMessage = __t('Removed %1$s of %2$s from stock', parseFloat(consumeAmount).toLocaleString({
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts
|
||||
}) + " " + __n(consumeAmount, result.quantity_unit_stock.name, result.quantity_unit_stock.name_plural, true), result.product.name) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockBookingEntry(' + bookingResponse[0].id + ',' + stockRowId + ')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>';
|
||||
if (wasSpoiled) {
|
||||
toastMessage += " (" + __t("Spoiled") + ")";
|
||||
}
|
||||
|
||||
|
|
@ -77,23 +101,20 @@ $(document).on('click', '.stock-consume-button', function(e)
|
|||
RefreshStockEntryRow(stockRowId);
|
||||
toastr.success(toastMessage);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy();
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy();
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$(document).on('click', '.product-open-button', function(e)
|
||||
{
|
||||
$(document).on('click', '.product-open-button', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Remove the focus from the current button
|
||||
|
|
@ -109,65 +130,55 @@ $(document).on('click', '.product-open-button', function(e)
|
|||
var stockRowId = $(e.currentTarget).attr('data-stockrow-id');
|
||||
var button = $(e.currentTarget);
|
||||
|
||||
Grocy.Api.Post('stock/products/' + productId + '/open', { 'amount': 1, 'stock_entry_id': specificStockEntryId },
|
||||
function(bookingResponse)
|
||||
{
|
||||
Grocy.Api.Post('stock/products/' + productId + '/open', {
|
||||
'amount': 1,
|
||||
'stock_entry_id': specificStockEntryId
|
||||
},
|
||||
function(bookingResponse) {
|
||||
button.addClass("disabled");
|
||||
Grocy.FrontendHelpers.EndUiBusy();
|
||||
toastr.success(__t('Marked %1$s of %2$s as opened', 1 + " " + productQuName, productName) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockBookingEntry(' + bookingResponse[0].id + ',' + stockRowId + ')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>');
|
||||
RefreshStockEntryRow(stockRowId);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy();
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$(document).on("click", ".stock-name-cell", function(e)
|
||||
{
|
||||
$(document).on("click", ".stock-name-cell", function(e) {
|
||||
Grocy.Components.ProductCard.Refresh($(e.currentTarget).attr("data-stock-id"));
|
||||
$("#stockentry-productcard-modal").modal("show");
|
||||
});
|
||||
|
||||
$(document).on('click', '.stockentry-grocycode-label-print', function(e)
|
||||
{
|
||||
$(document).on('click', '.stockentry-grocycode-label-print', function(e) {
|
||||
e.preventDefault();
|
||||
document.activeElement.blur();
|
||||
|
||||
var stockId = $(e.currentTarget).attr('data-stock-id');
|
||||
Grocy.Api.Get('stock/entry/' + stockId + '/printlabel', function(labelData)
|
||||
{
|
||||
if (Grocy.Webhooks.labelprinter !== undefined)
|
||||
{
|
||||
Grocy.Api.Get('stock/entry/' + stockId + '/printlabel', function(labelData) {
|
||||
if (Grocy.Webhooks.labelprinter !== undefined) {
|
||||
Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, labelData);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function RefreshStockEntryRow(stockRowId)
|
||||
{
|
||||
function RefreshStockEntryRow(stockRowId) {
|
||||
Grocy.Api.Get("stock/entry/" + stockRowId,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
var stockRow = $('#stock-' + stockRowId + '-row');
|
||||
|
||||
// If the stock row not exists / is invisible (happens after consume/undo because the undone new stock row has different id), just reload the page for now
|
||||
if (!stockRow.length || stockRow.hasClass("d-none"))
|
||||
{
|
||||
if (!stockRow.length || stockRow.hasClass("d-none")) {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
if (result == null || result.amount == 0)
|
||||
{
|
||||
animateCSS("#stock-" + stockRowId + "-row", "fadeOut", function()
|
||||
{
|
||||
if (result == null || result.amount == 0) {
|
||||
animateCSS("#stock-" + stockRowId + "-row", "fadeOut", function() {
|
||||
$("#stock-" + stockRowId + "-row").addClass("d-none");
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
var dueThreshold = moment().add(Grocy.UserSettings.stock_due_soon_days, "days");
|
||||
var now = moment();
|
||||
var bestBeforeDate = moment(result.best_before_date);
|
||||
|
|
@ -177,19 +188,13 @@ function RefreshStockEntryRow(stockRowId)
|
|||
stockRow.removeClass("table-info");
|
||||
stockRow.removeClass("d-none");
|
||||
stockRow.removeAttr("style");
|
||||
if (now.isAfter(bestBeforeDate))
|
||||
{
|
||||
if (stockRow.attr("data-due-type") == 1)
|
||||
{
|
||||
if (now.isAfter(bestBeforeDate)) {
|
||||
if (stockRow.attr("data-due-type") == 1) {
|
||||
stockRow.addClass("table-secondary");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
stockRow.addClass("table-danger");
|
||||
}
|
||||
}
|
||||
else if (bestBeforeDate.isBefore(dueThreshold))
|
||||
{
|
||||
} else if (bestBeforeDate.isBefore(dueThreshold)) {
|
||||
stockRow.addClass("table-warning");
|
||||
}
|
||||
|
||||
|
|
@ -203,15 +208,13 @@ function RefreshStockEntryRow(stockRowId)
|
|||
|
||||
var locationName = "";
|
||||
Grocy.Api.Get("objects/locations/" + result.location_id,
|
||||
function(locationResult)
|
||||
{
|
||||
function(locationResult) {
|
||||
locationName = locationResult.name;
|
||||
|
||||
$('#stock-' + stockRowId + '-location').attr('data-location-id', result.location_id);
|
||||
$('#stock-' + stockRowId + '-location').text(locationName);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
|
@ -222,74 +225,61 @@ function RefreshStockEntryRow(stockRowId)
|
|||
|
||||
var shoppingLocationName = "";
|
||||
Grocy.Api.Get("objects/shopping_locations/" + result.shopping_location_id,
|
||||
function(shoppingLocationResult)
|
||||
{
|
||||
function(shoppingLocationResult) {
|
||||
shoppingLocationName = shoppingLocationResult.name;
|
||||
|
||||
$('#stock-' + stockRowId + '-shopping-location').attr('data-shopping-location-id', result.location_id);
|
||||
$('#stock-' + stockRowId + '-shopping-location').text(shoppingLocationName);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
||||
if (result.open == 1)
|
||||
{
|
||||
if (result.open == 1) {
|
||||
$('#stock-' + stockRowId + '-opened-amount').text(__t('Opened'));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#stock-' + stockRowId + '-opened-amount').text("");
|
||||
$(".product-open-button[data-stockrow-id='" + stockRowId + "']").removeClass("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
// Needs to be delayed because of the animation above the date-text would be wrong if fired immediately...
|
||||
setTimeout(function()
|
||||
{
|
||||
setTimeout(function() {
|
||||
RefreshContextualTimeago("#stock-" + stockRowId + "-row");
|
||||
RefreshLocaleNumberDisplay("#stock-" + stockRowId + "-row");
|
||||
}, 600);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy();
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$(window).on("message", function(e)
|
||||
{
|
||||
$(window).on("message", function(e) {
|
||||
var data = e.originalEvent.data;
|
||||
|
||||
if (data.Message === "StockEntryChanged")
|
||||
{
|
||||
if (data.Message === "StockEntryChanged") {
|
||||
RefreshStockEntryRow(data.Payload);
|
||||
}
|
||||
});
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
Grocy.Components.ProductPicker.Validate();
|
||||
|
||||
function UndoStockBookingEntry(bookingId, stockRowId)
|
||||
{
|
||||
function UndoStockBookingEntry(bookingId, stockRowId) {
|
||||
Grocy.Api.Post('stock/bookings/' + bookingId.toString() + '/undo', {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
window.postMessage(WindowMessageBag("StockEntryChanged", stockRowId), Grocy.BaseUrl);
|
||||
toastr.success(__t("Booking successfully undone"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$(document).on("click", ".product-name-cell", function(e)
|
||||
{
|
||||
$(document).on("click", ".product-name-cell", function(e) {
|
||||
Grocy.Components.ProductCard.Refresh($(e.currentTarget).attr("data-product-id"));
|
||||
$("#productcard-modal").modal("show");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,83 +1,118 @@
|
|||
var stockJournalTable = $('#stock-journal-table').DataTable({
|
||||
'order': [[3, 'desc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
{ 'searchable': false, "targets": 0 }
|
||||
'order': [
|
||||
[3, 'desc']
|
||||
],
|
||||
'columnDefs': [{
|
||||
'orderable': false,
|
||||
'targets': 0
|
||||
},
|
||||
{
|
||||
'searchable': false,
|
||||
"targets": 0
|
||||
}
|
||||
].concat($.fn.dataTable.defaults.columnDefs)
|
||||
});
|
||||
$('#stock-journal-table tbody').removeClass("d-none");
|
||||
stockJournalTable.columns.adjust().draw();
|
||||
|
||||
$("#product-filter").on("change", function()
|
||||
{
|
||||
var value = $(this).val();
|
||||
if (value === "all")
|
||||
{
|
||||
RemoveUriParam("product");
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateUriParam("product", value);
|
||||
$("#product-filter").select2({
|
||||
ajax: {
|
||||
delay: 150,
|
||||
transport: function(params, success, failure) {
|
||||
var results_per_page = 10;
|
||||
var page = params.data.page || 1;
|
||||
var term = params.data.term || "";
|
||||
|
||||
var query = [];
|
||||
query.push('query%5B%5D=active%3D1');
|
||||
query.push('limit=' + encodeURIComponent(results_per_page));
|
||||
query.push('offset=' + encodeURIComponent((page - 1) * results_per_page));
|
||||
query.push('order=name%3Acollate%20nocase');
|
||||
if (term.length > 0) {
|
||||
query.push('search=' + encodeURIComponent(term));
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
Grocy.Api.Get('objects/products' + (query.length > 0 ? '?' + query.join('&') : ''),
|
||||
function(results, meta) {
|
||||
success({
|
||||
results: [{
|
||||
id: 'all',
|
||||
text: __t('All')
|
||||
}].concat(results.map(function(result) {
|
||||
return {
|
||||
id: result.id,
|
||||
text: result.name
|
||||
};
|
||||
})),
|
||||
pagination: {
|
||||
more: page * results_per_page < meta.recordsFiltered
|
||||
}
|
||||
});
|
||||
},
|
||||
function(xhr) {
|
||||
failure();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#transaction-type-filter").on("change", function()
|
||||
{
|
||||
$("#product-filter").on("change", function() {
|
||||
var value = $(this).val();
|
||||
if (value === "all" && GetUriParam("product") !== undefined) {
|
||||
RemoveUriParam("product");
|
||||
window.location.reload();
|
||||
} else if (GetUriParam("product") !== value) {
|
||||
UpdateUriParam("product", value);
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
$("#transaction-type-filter").on("change", function() {
|
||||
var value = $(this).val();
|
||||
var text = $("#transaction-type-filter option:selected").text();
|
||||
if (value === "all")
|
||||
{
|
||||
if (value === "all") {
|
||||
text = "";
|
||||
}
|
||||
|
||||
stockJournalTable.column(stockJournalTable.colReorder.transpose(4)).search(text).draw();
|
||||
});
|
||||
|
||||
$("#location-filter").on("change", function()
|
||||
{
|
||||
$("#location-filter").on("change", function() {
|
||||
var value = $(this).val();
|
||||
var text = $("#location-filter option:selected").text();
|
||||
if (value === "all")
|
||||
{
|
||||
if (value === "all") {
|
||||
text = "";
|
||||
}
|
||||
|
||||
stockJournalTable.column(stockJournalTable.colReorder.transpose(5)).search(text).draw();
|
||||
});
|
||||
|
||||
$("#user-filter").on("change", function()
|
||||
{
|
||||
$("#user-filter").on("change", function() {
|
||||
var value = $(this).val();
|
||||
var text = $("#user-filter option:selected").text();
|
||||
if (value === "all")
|
||||
{
|
||||
if (value === "all") {
|
||||
text = "";
|
||||
}
|
||||
|
||||
stockJournalTable.column(stockJournalTable.colReorder.transpose(6)).search(text).draw();
|
||||
});
|
||||
|
||||
$("#daterange-filter").on("change", function()
|
||||
{
|
||||
$("#daterange-filter").on("change", function() {
|
||||
UpdateUriParam("months", $(this).val());
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
$("#search").on("keyup", Delay(function()
|
||||
{
|
||||
$("#search").on("keyup", Delay(function() {
|
||||
var value = $(this).val();
|
||||
if (value === "all")
|
||||
{
|
||||
if (value === "all") {
|
||||
value = "";
|
||||
}
|
||||
|
||||
stockJournalTable.search(value).draw();
|
||||
}, 200));
|
||||
|
||||
$("#clear-filter-button").on("click", function()
|
||||
{
|
||||
$("#clear-filter-button").on("click", function() {
|
||||
$("#search").val("");
|
||||
$("#transaction-type-filter").val("all");
|
||||
$("#location-filter").val("all");
|
||||
|
|
@ -90,40 +125,47 @@ $("#clear-filter-button").on("click", function()
|
|||
window.location.reload();
|
||||
});
|
||||
|
||||
if (typeof GetUriParam("product") !== "undefined")
|
||||
{
|
||||
$("#product-filter").val(GetUriParam("product"));
|
||||
var prefillProductId = GetUriParam("product");
|
||||
if (typeof prefillProductId !== "undefined") {
|
||||
if ($("#product-filter").find('option[value="' + prefillProductId + '"]').length) {
|
||||
$("#product-filter").val(prefillProductId).trigger('change');
|
||||
} else {
|
||||
Grocy.Api.Get('objects/products/' + encodeURIComponent(prefillProductId),
|
||||
function(result) {
|
||||
var option = new Option(result.name, prefillProductId, true, true);
|
||||
$("#product-filter").append(option).trigger('change');
|
||||
},
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof GetUriParam("months") !== "undefined")
|
||||
{
|
||||
if (typeof GetUriParam("months") !== "undefined") {
|
||||
$("#daterange-filter").val(GetUriParam("months"));
|
||||
}
|
||||
|
||||
$(document).on('click', '.undo-stock-booking-button', function(e)
|
||||
{
|
||||
$(document).on('click', '.undo-stock-booking-button', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var bookingId = $(e.currentTarget).attr('data-booking-id');
|
||||
var correlationId = $("#stock-booking-" + bookingId + "-row").attr("data-correlation-id");
|
||||
|
||||
var correspondingBookingsRoot = $("#stock-booking-" + bookingId + "-row");
|
||||
if (!correlationId.isEmpty())
|
||||
{
|
||||
if (!correlationId.isEmpty()) {
|
||||
correspondingBookingsRoot = $(".stock-booking-correlation-" + correlationId);
|
||||
}
|
||||
|
||||
Grocy.Api.Post('stock/bookings/' + bookingId.toString() + '/undo', {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
correspondingBookingsRoot.addClass("text-muted");
|
||||
correspondingBookingsRoot.find("span.name-anchor").addClass("text-strike-through").after("<br>" + __t("Undone on") + " " + moment().format("YYYY-MM-DD HH:mm:ss") + " <time class='timeago timeago-contextual' datetime='" + moment().format("YYYY-MM-DD HH:mm:ss") + "'></time>");
|
||||
correspondingBookingsRoot.find(".undo-stock-booking-button").addClass("disabled");
|
||||
RefreshContextualTimeago("#stock-booking-" + bookingId + "-row");
|
||||
toastr.success(__t("Booking successfully undone"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
toastr.error(__t(JSON.parse(xhr.response).error_message));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +1,102 @@
|
|||
var journalSummaryTable = $('#stock-journal-summary-table').DataTable({
|
||||
'order': [[1, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
{ 'searchable': false, "targets": 0 }
|
||||
'order': [
|
||||
[1, 'asc']
|
||||
],
|
||||
'columnDefs': [{
|
||||
'orderable': false,
|
||||
'targets': 0
|
||||
},
|
||||
{
|
||||
'searchable': false,
|
||||
"targets": 0
|
||||
}
|
||||
].concat($.fn.dataTable.defaults.columnDefs)
|
||||
});
|
||||
$('#stock-journal-summary-table tbody').removeClass("d-none");
|
||||
journalSummaryTable.columns.adjust().draw();
|
||||
|
||||
$("#product-filter").on("change", function()
|
||||
{
|
||||
var value = $(this).val();
|
||||
var text = $("#product-filter option:selected").text();
|
||||
if (value === "all")
|
||||
{
|
||||
journalSummaryTable.column(journalSummaryTable.colReorder.transpose(1)).search("").draw();
|
||||
$("#product-filter").select2({
|
||||
ajax: {
|
||||
delay: 150,
|
||||
transport: function(params, success, failure) {
|
||||
var results_per_page = 10;
|
||||
var page = params.data.page || 1;
|
||||
var term = params.data.term || "";
|
||||
|
||||
var query = [];
|
||||
query.push('query%5B%5D=active%3D1');
|
||||
query.push('limit=' + encodeURIComponent(results_per_page));
|
||||
query.push('offset=' + encodeURIComponent((page - 1) * results_per_page));
|
||||
query.push('order=name%3Acollate%20nocase');
|
||||
if (term.length > 0) {
|
||||
query.push('search=' + encodeURIComponent(term));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Grocy.Api.Get('objects/products' + (query.length > 0 ? '?' + query.join('&') : ''),
|
||||
function(results, meta) {
|
||||
success({
|
||||
results: [{
|
||||
id: 'all',
|
||||
text: __t('All')
|
||||
}].concat(results.map(function(result) {
|
||||
return {
|
||||
id: result.id,
|
||||
text: result.name
|
||||
};
|
||||
})),
|
||||
pagination: {
|
||||
more: page * results_per_page < meta.recordsFiltered
|
||||
}
|
||||
});
|
||||
},
|
||||
function(xhr) {
|
||||
failure();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#product-filter").on("change", function() {
|
||||
var value = $(this).val();
|
||||
var text = $("#product-filter option:selected").text().trim();
|
||||
if (value === "all") {
|
||||
journalSummaryTable.column(journalSummaryTable.colReorder.transpose(1)).search("").draw();
|
||||
} else {
|
||||
journalSummaryTable.column(journalSummaryTable.colReorder.transpose(1)).search("^" + text + "$", true, false).draw();
|
||||
}
|
||||
});
|
||||
|
||||
$("#transaction-type-filter").on("change", function()
|
||||
{
|
||||
$("#transaction-type-filter").on("change", function() {
|
||||
var value = $(this).val();
|
||||
var text = $("#transaction-type-filter option:selected").text();
|
||||
if (value === "all")
|
||||
{
|
||||
if (value === "all") {
|
||||
text = "";
|
||||
}
|
||||
|
||||
journalSummaryTable.column(journalSummaryTable.colReorder.transpose(2)).search(text).draw();
|
||||
});
|
||||
|
||||
$("#user-filter").on("change", function()
|
||||
{
|
||||
$("#user-filter").on("change", function() {
|
||||
var value = $(this).val();
|
||||
var text = $("#user-filter option:selected").text();
|
||||
if (value === "all")
|
||||
{
|
||||
if (value === "all") {
|
||||
text = "";
|
||||
}
|
||||
|
||||
journalSummaryTable.column(journalSummaryTable.colReorder.transpose(3)).search(text).draw();
|
||||
});
|
||||
|
||||
$("#search").on("keyup", Delay(function()
|
||||
{
|
||||
$("#search").on("keyup", Delay(function() {
|
||||
var value = $(this).val();
|
||||
if (value === "all")
|
||||
{
|
||||
if (value === "all") {
|
||||
value = "";
|
||||
}
|
||||
|
||||
journalSummaryTable.search(value).draw();
|
||||
}, 200));
|
||||
|
||||
$("#clear-filter-button").on("click", function()
|
||||
{
|
||||
$("#clear-filter-button").on("click", function() {
|
||||
$("#search").val("");
|
||||
$("#transaction-type-filter").val("all");
|
||||
$("#location-filter").val("all");
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
$('#save-transfer-button').on('click', function(e)
|
||||
{
|
||||
$('#save-transfer-button').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if ($(".combobox-menu-visible").length)
|
||||
{
|
||||
if ($(".combobox-menu-visible").length) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -17,60 +15,48 @@
|
|||
jsonData.location_id_to = $("#location_id_to").val();
|
||||
jsonData.location_id_from = $("#location_id_from").val();
|
||||
|
||||
if ($("#use_specific_stock_entry").is(":checked"))
|
||||
{
|
||||
if ($("#use_specific_stock_entry").is(":checked")) {
|
||||
jsonData.stock_entry_id = jsonForm.specific_stock_entry;
|
||||
}
|
||||
|
||||
var bookingResponse = null;
|
||||
|
||||
Grocy.Api.Get('stock/products/' + jsonForm.product_id,
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
Grocy.Api.Post(apiUrl, jsonData,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
bookingResponse = result;
|
||||
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct")
|
||||
{
|
||||
if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct") {
|
||||
var jsonDataBarcode = {};
|
||||
jsonDataBarcode.barcode = GetUriParam("barcode");
|
||||
jsonDataBarcode.product_id = jsonForm.product_id;
|
||||
|
||||
Grocy.Api.Post('objects/product_barcodes', jsonDataBarcode,
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
$("#flow-info-InplaceAddBarcodeToExistingProduct").addClass("d-none");
|
||||
$('#barcode-lookup-disabled-hint').addClass('d-none');
|
||||
$('#barcode-lookup-hint').removeClass('d-none');
|
||||
window.history.replaceState({}, document.title, U("/transfer"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("transfer-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1) {
|
||||
var successMessage = __t('Transfered %1$s of %2$s from %3$s to %4$s', Math.abs(jsonForm.amount - parseFloat(productDetails.product.tare_weight)) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name, $('option:selected', "#location_id_from").text(), $('option:selected', "#location_id_to").text()) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockTransaction(\'' + bookingResponse[0].transaction_id + '\')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
var successMessage = __t('Transfered %1$s of %2$s from %3$s to %4$s', Math.abs(jsonForm.amount) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural, true), productDetails.product.name, $('option:selected', "#location_id_from").text(), $('option:selected', "#location_id_to").text()) + '<br><a class="btn btn-secondary btn-sm mt-2" href="#" onclick="UndoStockTransaction(\'' + bookingResponse[0].transaction_id + '\')"><i class="fas fa-undo"></i> ' + __t("Undo") + '</a>';
|
||||
}
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
window.parent.postMessage(WindowMessageBag("ProductChanged", jsonForm.product_id), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("ShowSuccessMessage", successMessage), Grocy.BaseUrl);
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), Grocy.BaseUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Grocy.FrontendHelpers.EndUiBusy("transfer-form");
|
||||
toastr.success(successMessage);
|
||||
Grocy.Components.ProductPicker.FinishFlow();
|
||||
|
|
@ -79,8 +65,7 @@
|
|||
{
|
||||
toastr.info('<span>' + __t("Frozen") + "</span> <i class='fas fa-snowflake'></i>");
|
||||
|
||||
if (BoolVal(productDetails.product.should_not_be_frozen))
|
||||
{
|
||||
if (BoolVal(productDetails.product.should_not_be_frozen)) {
|
||||
toastr.warning(__t("This product shouldn't be frozen"));
|
||||
}
|
||||
}
|
||||
|
|
@ -92,8 +77,7 @@
|
|||
$("#specific_stock_entry").find("option").remove().end().append("<option></option>");
|
||||
$("#specific_stock_entry").attr("disabled", "");
|
||||
$("#specific_stock_entry").removeAttr("required");
|
||||
if ($("#use_specific_stock_entry").is(":checked"))
|
||||
{
|
||||
if ($("#use_specific_stock_entry").is(":checked")) {
|
||||
$("#use_specific_stock_entry").click();
|
||||
}
|
||||
|
||||
|
|
@ -108,67 +92,56 @@
|
|||
Grocy.Components.ProductPicker.Clear();
|
||||
$("#location_id_to").val("");
|
||||
$("#location_id_from").val("");
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
Grocy.Components.ProductCard.Refresh(jsonForm.product_id);
|
||||
Grocy.FrontendHelpers.ValidateForm('transfer-form');
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("transfer-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
Grocy.FrontendHelpers.EndUiBusy("transfer-form");
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
{
|
||||
Grocy.Components.ProductPicker.OnChange(function(e) {
|
||||
$("#specific_stock_entry").find("option").remove().end().append("<option></option>");
|
||||
if ($("#use_specific_stock_entry").is(":checked") && GetUriParam("stockId") == null)
|
||||
{
|
||||
$("#use_specific_stock_entry").click();
|
||||
if ($("#use_specific_stock_entry").is(":checked") && GetUriParam("stockId") == null) {
|
||||
$("#use_specific_stock_entry").trigger('click');
|
||||
}
|
||||
$("#location_id_to").val("");
|
||||
if (GetUriParam("stockId") == null)
|
||||
{
|
||||
if (GetUriParam("stockId") == null) {
|
||||
$("#location_id_from").val("");
|
||||
}
|
||||
|
||||
var productId = $(e.target).val();
|
||||
|
||||
if (productId)
|
||||
{
|
||||
if (productId) {
|
||||
Grocy.Components.ProductCard.Refresh(productId);
|
||||
|
||||
Grocy.Api.Get('stock/products/' + productId,
|
||||
function(productDetails)
|
||||
{
|
||||
function(productDetails) {
|
||||
Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id);
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(productDetails.quantity_unit_stock.id);
|
||||
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().parent().find(".invalid-feedback").text(__t('Products with tare weight enabled are currently not supported for transfer'));
|
||||
if (productDetails.product.enable_tare_weight_handling == 1) {
|
||||
Grocy.Components.ProductPicker.ShowCustomError(__t('Products with tare weight enabled are currently not supported for transfer'));
|
||||
Grocy.Components.ProductPicker.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
$("#location_id_from").find("option").remove().end().append("<option></option>");
|
||||
Grocy.Api.Get("stock/products/" + productId + '/locations',
|
||||
function(stockLocations)
|
||||
{
|
||||
function(stockLocations) {
|
||||
var setDefault = 0;
|
||||
stockLocations.forEach(stockLocation =>
|
||||
{
|
||||
if (productDetails.location.id == stockLocation.location_id)
|
||||
{
|
||||
stockLocations.forEach(stockLocation => {
|
||||
if (productDetails.location.id == stockLocation.location_id) {
|
||||
$("#location_id_from").append($("<option>", {
|
||||
value: stockLocation.location_id,
|
||||
text: stockLocation.location_name + " (" + __t("Default location") + ")",
|
||||
|
|
@ -177,9 +150,7 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
$("#location_id_from").val(productDetails.location.id);
|
||||
$("#location_id_from").trigger('change');
|
||||
setDefault = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#location_id_from").append($("<option>", {
|
||||
value: stockLocation.location_id,
|
||||
text: stockLocation.location_name,
|
||||
|
|
@ -187,44 +158,35 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
}));
|
||||
}
|
||||
|
||||
if (setDefault == 0)
|
||||
{
|
||||
if (setDefault == 0) {
|
||||
$("#location_id_from").val(stockLocation.location_id);
|
||||
$("#location_id_from").trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
if (GetUriParam("locationId") != null)
|
||||
{
|
||||
if (GetUriParam("locationId") != null) {
|
||||
$("#location_id_from").val(GetUriParam("locationId"));
|
||||
$("#location_id_from").trigger("change");
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
|
||||
if (document.getElementById("product_id").getAttribute("barcode") != "null")
|
||||
{
|
||||
if (document.getElementById("product_id").getAttribute("barcode") != "null") {
|
||||
Grocy.Api.Get('objects/product_barcodes?query[]=barcode=' + document.getElementById("product_id").getAttribute("barcode"),
|
||||
function(barcodeResult)
|
||||
{
|
||||
if (barcodeResult != null)
|
||||
{
|
||||
function(barcodeResult) {
|
||||
if (barcodeResult != null) {
|
||||
var barcode = barcodeResult[0];
|
||||
|
||||
if (barcode != null)
|
||||
{
|
||||
if (barcode.amount != null && !barcode.amount.isEmpty())
|
||||
{
|
||||
if (barcode != null) {
|
||||
if (barcode.amount != null && !barcode.amount.isEmpty()) {
|
||||
$("#display_amount").val(barcode.amount);
|
||||
$("#display_amount").select();
|
||||
$("#display_amount").trigger('select');
|
||||
}
|
||||
|
||||
if (barcode.qu_id != null && !barcode.qu_id.isEmpty())
|
||||
{
|
||||
if (barcode.qu_id != null && !barcode.qu_id.isEmpty()) {
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(barcode.qu_id);
|
||||
}
|
||||
|
||||
|
|
@ -234,20 +196,16 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
}
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (productDetails.product.enable_tare_weight_handling == 1)
|
||||
{
|
||||
if (productDetails.product.enable_tare_weight_handling == 1) {
|
||||
$("#display_amount").attr("min", productDetails.product.tare_weight);
|
||||
$("#tare-weight-handling-info").removeClass("d-none");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#display_amount").attr("min", Grocy.DefaultMinAmount);
|
||||
$("#tare-weight-handling-info").addClass("d-none");
|
||||
}
|
||||
|
|
@ -256,11 +214,11 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
|||
|
||||
Grocy.Components.ProductPicker.HideCustomError();
|
||||
Grocy.FrontendHelpers.ValidateForm('transfer-form');
|
||||
$('#display_amount').focus();
|
||||
$('#display_amount').trigger('focus');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
Grocy.Components.ProductPicker.HideCustomError();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -271,45 +229,35 @@ $(".input-group-productamountpicker").trigger("change");
|
|||
Grocy.FrontendHelpers.ValidateForm('transfer-form');
|
||||
RefreshLocaleNumberInput();
|
||||
|
||||
$("#location_id_from").on('change', function(e)
|
||||
{
|
||||
$("#location_id_from").on('change', function(e) {
|
||||
var locationId = $(e.target).val();
|
||||
var sumValue = 0;
|
||||
var stockId = null;
|
||||
|
||||
if (locationId == $("#location_id_to").val())
|
||||
{
|
||||
if (locationId == $("#location_id_to").val()) {
|
||||
$("#location_id_to").val("");
|
||||
}
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
stockId = GetUriParam('stockId');
|
||||
}
|
||||
|
||||
$("#specific_stock_entry").find("option").remove().end().append("<option></option>");
|
||||
if ($("#use_specific_stock_entry").is(":checked") && GetUriParam("stockId") == null)
|
||||
{
|
||||
if ($("#use_specific_stock_entry").is(":checked") && GetUriParam("stockId") == null) {
|
||||
$("#use_specific_stock_entry").click();
|
||||
}
|
||||
|
||||
if (locationId)
|
||||
{
|
||||
if (locationId) {
|
||||
Grocy.Api.Get("stock/products/" + Grocy.Components.ProductPicker.GetValue() + '/entries',
|
||||
function(stockEntries)
|
||||
{
|
||||
stockEntries.forEach(stockEntry =>
|
||||
{
|
||||
function(stockEntries) {
|
||||
stockEntries.forEach(stockEntry => {
|
||||
var openTxt = __t("Not opened");
|
||||
if (stockEntry.open == 1)
|
||||
{
|
||||
if (stockEntry.open == 1) {
|
||||
openTxt = __t("Opened");
|
||||
}
|
||||
|
||||
if (stockEntry.location_id == locationId)
|
||||
{
|
||||
if ($("#specific_stock_entry option[value='" + stockEntry.stock_id + "']").length == 0)
|
||||
{
|
||||
if (stockEntry.location_id == locationId) {
|
||||
if ($("#specific_stock_entry option[value='" + stockEntry.stock_id + "']").length == 0) {
|
||||
$("#specific_stock_entry").append($("<option>", {
|
||||
value: stockEntry.stock_id,
|
||||
amount: stockEntry.amount,
|
||||
|
|
@ -317,8 +265,7 @@ $("#location_id_from").on('change', function(e)
|
|||
}));
|
||||
}
|
||||
|
||||
if (stockEntry.stock_id == stockId)
|
||||
{
|
||||
if (stockEntry.stock_id == stockId) {
|
||||
$("#specific_stock_entry").val(stockId);
|
||||
}
|
||||
|
||||
|
|
@ -326,52 +273,43 @@ $("#location_id_from").on('change', function(e)
|
|||
}
|
||||
});
|
||||
$("#display_amount").attr("max", sumValue * $("#qu_id option:selected").attr("data-qu-factor"));
|
||||
if (sumValue == 0)
|
||||
{
|
||||
if (sumValue == 0) {
|
||||
$("#display_amount").parent().find(".invalid-feedback").text(__t('There are no units available at this location'));
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$("#location_id_to").on('change', function(e)
|
||||
{
|
||||
$("#location_id_to").on('change', function(e) {
|
||||
var locationId = $(e.target).val();
|
||||
|
||||
if (locationId == $("#location_id_from").val())
|
||||
{
|
||||
if (locationId == $("#location_id_from").val()) {
|
||||
$("#location_id_to").parent().find(".invalid-feedback").text(__t('This cannot be the same as the "From" location'));
|
||||
$("#location_id_to").val("");
|
||||
}
|
||||
});
|
||||
|
||||
$("#qu_id").on('change', function(e)
|
||||
{
|
||||
$("#qu_id").on('change', function(e) {
|
||||
$("#display_amount").attr("max", parseFloat($('#display_amount').attr("data-stock-amount")) * $("#qu_id option:selected").attr("data-qu-factor"));
|
||||
});
|
||||
|
||||
$('#display_amount').on('focus', function(e)
|
||||
{
|
||||
$('#display_amount').on('focus', function(e) {
|
||||
$(this).select();
|
||||
});
|
||||
|
||||
$('#transfer-form input').keyup(function(event)
|
||||
{
|
||||
$('#transfer-form input').keyup(function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('transfer-form');
|
||||
});
|
||||
|
||||
$('#transfer-form select').change(function(event)
|
||||
{
|
||||
$('#transfer-form select').change(function(event) {
|
||||
Grocy.FrontendHelpers.ValidateForm('transfer-form');
|
||||
});
|
||||
|
||||
$('#transfer-form input').keydown(function(event)
|
||||
{
|
||||
$('#transfer-form input').keydown(function(event) {
|
||||
if (event.keyCode === 13) //Enter
|
||||
{
|
||||
event.preventDefault();
|
||||
|
|
@ -379,58 +317,43 @@ $('#transfer-form input').keydown(function(event)
|
|||
if (document.getElementById('transfer-form').checkValidity() === false) //There is at least one validation error
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#save-transfer-button').click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#specific_stock_entry").on("change", function(e)
|
||||
{
|
||||
if ($(e.target).val() == "")
|
||||
{
|
||||
$("#specific_stock_entry").on("change", function(e) {
|
||||
if ($(e.target).val() == "") {
|
||||
var sumValue = 0;
|
||||
Grocy.Api.Get("stock/products/" + Grocy.Components.ProductPicker.GetValue() + '/entries',
|
||||
function(stockEntries)
|
||||
{
|
||||
stockEntries.forEach(stockEntry =>
|
||||
{
|
||||
if (stockEntry.location_id == $("#location_id_from").val() || stockEntry.location_id == "")
|
||||
{
|
||||
function(stockEntries) {
|
||||
stockEntries.forEach(stockEntry => {
|
||||
if (stockEntry.location_id == $("#location_id_from").val() || stockEntry.location_id == "") {
|
||||
sumValue = sumValue + parseFloat(stockEntry.amount);
|
||||
}
|
||||
});
|
||||
$("#display_amount").attr("max", sumValue * $("#qu_id option:selected").attr("data-qu-factor"));
|
||||
if (sumValue == 0)
|
||||
{
|
||||
if (sumValue == 0) {
|
||||
$("#display_amount").parent().find(".invalid-feedback").text(__t('There are no units available at this location'));
|
||||
}
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#display_amount").attr("max", $('option:selected', this).attr('amount'));
|
||||
}
|
||||
});
|
||||
|
||||
$("#use_specific_stock_entry").on("change", function()
|
||||
{
|
||||
$("#use_specific_stock_entry").on("change", function() {
|
||||
var value = $(this).is(":checked");
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (value) {
|
||||
$("#specific_stock_entry").removeAttr("disabled");
|
||||
$("#specific_stock_entry").attr("required", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$("#specific_stock_entry").attr("disabled", "");
|
||||
$("#specific_stock_entry").removeAttr("required");
|
||||
$("#specific_stock_entry").val("");
|
||||
|
|
@ -440,53 +363,43 @@ $("#use_specific_stock_entry").on("change", function()
|
|||
Grocy.FrontendHelpers.ValidateForm("transfer-form");
|
||||
});
|
||||
|
||||
function UndoStockBooking(bookingId)
|
||||
{
|
||||
function UndoStockBooking(bookingId) {
|
||||
Grocy.Api.Post('stock/bookings/' + bookingId.toString() + '/undo', {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
toastr.success(__t("Booking successfully undone"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function UndoStockTransaction(transactionId)
|
||||
{
|
||||
function UndoStockTransaction(transactionId) {
|
||||
Grocy.Api.Post('stock/transactions/' + transactionId.toString() + '/undo', {},
|
||||
function(result)
|
||||
{
|
||||
function(result) {
|
||||
toastr.success(__t("Transaction successfully undone"));
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
function(xhr) {
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined) {
|
||||
var locationId = GetUriParam('locationId');
|
||||
|
||||
if (typeof locationId === 'undefined')
|
||||
{
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (typeof locationId === 'undefined') {
|
||||
Grocy.Components.ProductPicker.Validate();
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
} else {
|
||||
|
||||
$("#location_id_from").val(locationId);
|
||||
$("#location_id_from").trigger('change');
|
||||
$("#use_specific_stock_entry").click();
|
||||
$("#use_specific_stock_entry").trigger('click');
|
||||
$("#use_specific_stock_entry").trigger('change');
|
||||
Grocy.Components.ProductPicker.GetPicker().trigger('change');
|
||||
Grocy.Components.ProductPicker.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
// Default input field
|
||||
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||
Grocy.Components.ProductPicker.Focus();
|
||||
|
|
|
|||
|
|
@ -5,26 +5,21 @@
|
|||
@section('viewJsName', 'batteries')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button permission-MASTER_DATA_EDIT m-1 mt-md-0 mb-md-0 float-right show-as-dialog-link"
|
||||
href="{{ $U('/battery/new?embedded') }}">
|
||||
{{ $__t('Add') }}
|
||||
|
|
@ -36,84 +31,69 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="form-check custom-control custom-checkbox">
|
||||
<input class="form-check-input custom-control-input"
|
||||
type="checkbox"
|
||||
id="show-disabled">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="show-disabled">
|
||||
<input class="form-check-input custom-control-input" type="checkbox" id="show-disabled">
|
||||
<label class="form-check-label custom-control-label" for="show-disabled">
|
||||
{{ $__t('Show disabled') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="batteries-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: batteries --}}
|
||||
<table id="batteries-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#batteries-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#batteries-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th>{{ $__t('Description') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Used in') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Charge cycle interval (days)') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($batteries as $battery)
|
||||
<tr class="@if($battery->active == 0) text-muted @endif">
|
||||
@foreach ($batteries as $battery)
|
||||
<tr class="@if ($battery->active == 0) text-muted @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm permission-MASTER_DATA_EDIT show-as-dialog-link"
|
||||
href="{{ $U('/battery/') }}{{ $battery->id }}?embedded"
|
||||
data-toggle="tooltip"
|
||||
href="{{ $U('/battery/') }}{{ $battery->id }}?embedded" data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm battery-delete-button permission-MASTER_DATA_EDIT"
|
||||
href="#"
|
||||
data-battery-id="{{ $battery->id }}"
|
||||
data-battery-name="{{ $battery->name }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<a class="btn btn-danger btn-sm battery-delete-button permission-MASTER_DATA_EDIT" href="#"
|
||||
data-battery-id="{{ $battery->id }}" data-battery-name="{{ $battery->name }}"
|
||||
data-toggle="tooltip" title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
|
|
@ -130,15 +110,19 @@
|
|||
{{ $battery->charge_interval_days }}
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $battery->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$battery->id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,33 +5,27 @@
|
|||
@section('viewJsName', 'batteriesjournal')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button" data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
|
|
@ -39,10 +33,10 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Battery') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="battery-filter">
|
||||
{{-- TODO: Select2: dynamic data: batteries --}}
|
||||
<select class="custom-control custom-select" id="battery-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($batteries as $battery)
|
||||
@foreach ($batteries as $battery)
|
||||
<option value="{{ $battery->id }}">{{ $battery->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -53,62 +47,53 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-clock"></i> {{ $__t('Date range') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="daterange-filter">
|
||||
<select class="custom-control custom-select" id="daterange-filter">
|
||||
<option value="1">{{ $__n(1, '%s month', '%s months') }}</option>
|
||||
<option value="6">{{ $__n(6, '%s month', '%s months') }}</option>
|
||||
<option value="12">{{ $__n(1, '%s year', '%s years') }}</option>
|
||||
<option value="24"
|
||||
selected>{{ $__n(2, '%s month', '%s years') }}</option>
|
||||
<option value="24" selected>{{ $__n(2, '%s month', '%s years') }}</option>
|
||||
<option value="9999">{{ $__t('All') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="row mt-2">
|
||||
<div class="col">
|
||||
<table id="batteries-journal-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: battery_charge_cycles --}}
|
||||
<table id="batteries-journal-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#batteries-journal-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#batteries-journal-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th class="allow-grouping">{{ $__t('Battery') }}</th>
|
||||
<th>{{ $__t('Tracked time') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($chargeCycles as $chargeCycleEntry)
|
||||
@foreach ($chargeCycles as $chargeCycleEntry)
|
||||
<tr id="charge-cycle-{{ $chargeCycleEntry->id }}-row"
|
||||
class="@if($chargeCycleEntry->undone == 1) text-muted @endif">
|
||||
class="@if ($chargeCycleEntry->undone == 1) text-muted @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-secondary btn-xs undo-battery-execution-button @if($chargeCycleEntry->undone == 1) disabled @endif permission-BATTERIES_UNDO_CHARGE_CYCLE"
|
||||
href="#"
|
||||
data-charge-cycle-id="{{ $chargeCycleEntry->id }}"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
title="{{ $__t('Undo charge cycle') }}">
|
||||
<a class="btn btn-secondary btn-xs undo-battery-execution-button @if ($chargeCycleEntry->undone == 1) disabled @endif permission-BATTERIES_UNDO_CHARGE_CYCLE"
|
||||
href="#" data-charge-cycle-id="{{ $chargeCycleEntry->id }}" data-toggle="tooltip"
|
||||
data-placement="left" title="{{ $__t('Undo charge cycle') }}">
|
||||
<i class="fas fa-undo"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="name-anchor @if($chargeCycleEntry->undone == 1) text-strike-through @endif">{{ FindObjectInArrayByPropertyValue($batteries, 'id', $chargeCycleEntry->battery_id)->name }}</span>
|
||||
@if($chargeCycleEntry->undone == 1)
|
||||
<span
|
||||
class="name-anchor @if ($chargeCycleEntry->undone == 1) text-strike-through @endif">{{ FindObjectInArrayByPropertyValue($batteries, 'id', $chargeCycleEntry->battery_id)->name }}</span>
|
||||
@if ($chargeCycleEntry->undone == 1)
|
||||
<br>
|
||||
{{ $__t('Undone on') . ' ' . $chargeCycleEntry->undone_timestamp }}
|
||||
<time class="timeago timeago-contextual"
|
||||
|
|
@ -125,5 +110,5 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,23 +5,19 @@
|
|||
@section('viewJsName', 'batteriesoverview')
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}" rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-outline-dark responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/batteriesjournal') }}">
|
||||
{{ $__t('Journal') }}
|
||||
|
|
@ -29,44 +25,33 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="border-top border-bottom my-2 py-1">
|
||||
<div id="info-overdue-batteries"
|
||||
data-status-filter="overdue"
|
||||
<div id="info-overdue-batteries" data-status-filter="overdue"
|
||||
class="error-message status-filter-message responsive-button mr-2"></div>
|
||||
<div id="info-due-today-batteries"
|
||||
data-status-filter="duetoday"
|
||||
<div id="info-due-today-batteries" data-status-filter="duetoday"
|
||||
class="normal-message status-filter-message responsive-button mr-2"></div>
|
||||
<div id="info-due-soon-batteries"
|
||||
data-status-filter="duesoon"
|
||||
data-next-x-days="{{ $nextXDays }}"
|
||||
class="warning-message status-filter-message responsive-button @if($nextXDays == 0) d-none @endif"></div>
|
||||
<div id="info-due-soon-batteries" data-status-filter="duesoon" data-next-x-days="{{ $nextXDays }}"
|
||||
class="warning-message status-filter-message responsive-button @if ($nextXDays == 0) d-none @endif">
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<a class="btn btn-sm btn-outline-info d-md-none mt-1"
|
||||
data-toggle="collapse"
|
||||
href="#table-filter-row"
|
||||
<a class="btn btn-sm btn-outline-info d-md-none mt-1" data-toggle="collapse" href="#table-filter-row"
|
||||
role="button">
|
||||
<i class="fas fa-filter"></i>
|
||||
</a>
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info mt-1"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info mt-1" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
|
|
@ -74,31 +59,27 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Status') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="status-filter">
|
||||
<select class="custom-control custom-select" id="status-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
<option value="overdue">{{ $__t('Overdue') }}</option>
|
||||
<option value="duetoday">{{ $__t('Due today') }}</option>
|
||||
@if($nextXDays > 0)
|
||||
@if ($nextXDays > 0)
|
||||
<option value="duesoon">{{ $__t('Due soon') }}</option>
|
||||
@endif
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="batteries-overview-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: batteries --}}
|
||||
<table id="batteries-overview-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#batteries-overview-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#batteries-overview-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Battery') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Used in') }}</th>
|
||||
|
|
@ -106,41 +87,36 @@
|
|||
<th>{{ $__t('Next planned charge cycle') }}</th>
|
||||
<th class="d-none">Hidden status</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($current as $currentBatteryEntry)
|
||||
@foreach ($current as $currentBatteryEntry)
|
||||
<tr id="battery-{{ $currentBatteryEntry->battery_id }}-row"
|
||||
class="@if($currentBatteryEntry->due_type == 'overdue') table-danger @elseif($currentBatteryEntry->due_type == 'duetoday') table-info @elseif($currentBatteryEntry->due_type == 'duesoon') table-warning @endif">
|
||||
class="@if ($currentBatteryEntry->due_type == 'overdue') table-danger @elseif($currentBatteryEntry->due_type == 'duetoday') table-info @elseif($currentBatteryEntry->due_type == 'duesoon') table-warning @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-success btn-sm track-charge-cycle-button permission-BATTERIES_TRACK_CHARGE_CYCLE"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
href="#" data-toggle="tooltip" data-placement="left"
|
||||
title="{{ $__t('Track charge cycle') }}"
|
||||
data-battery-id="{{ $currentBatteryEntry->battery_id }}"
|
||||
data-battery-name="{{ FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->name }}">
|
||||
<i class="fas fa-car-battery"></i>
|
||||
</a>
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
<button class="btn btn-sm btn-light text-secondary" type="button"
|
||||
data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="table-inline-menu dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item battery-name-cell"
|
||||
data-battery-id="{{ $currentBatteryEntry->battery_id }}"
|
||||
type="button"
|
||||
data-battery-id="{{ $currentBatteryEntry->battery_id }}" type="button"
|
||||
href="#">
|
||||
<span class="dropdown-item-text">{{ $__t('Battery overview') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/batteriesjournal?embedded&battery=') }}{{ $currentBatteryEntry->battery_id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Battery journal') }}</span>
|
||||
</a>
|
||||
|
|
@ -150,15 +126,13 @@
|
|||
<span class="dropdown-item-text">{{ $__t('Edit battery') }}</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
<a class="dropdown-item" type="button"
|
||||
href="{{ $U('/battery/' . $currentBatteryEntry->battery_id . '/grocycode?download=true') }}">
|
||||
{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Download %s grocycode', $__t('Battery'))) !!}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
@if (GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<a class="dropdown-item battery-grocycode-label-print"
|
||||
data-battery-id="{{ $currentBatteryEntry->battery_id }}"
|
||||
type="button"
|
||||
data-battery-id="{{ $currentBatteryEntry->battery_id }}" type="button"
|
||||
href="#">
|
||||
{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Print %s grocycode on label printer', $__t('Battery'))) !!}
|
||||
</a>
|
||||
|
|
@ -174,14 +148,16 @@
|
|||
{{ FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->used_in }}
|
||||
</td>
|
||||
<td>
|
||||
<span id="battery-{{ $currentBatteryEntry->battery_id }}-last-tracked-time">{{ $currentBatteryEntry->last_tracked_time }}</span>
|
||||
<span
|
||||
id="battery-{{ $currentBatteryEntry->battery_id }}-last-tracked-time">{{ $currentBatteryEntry->last_tracked_time }}</span>
|
||||
<time id="battery-{{ $currentBatteryEntry->battery_id }}-last-tracked-time-timeago"
|
||||
class="timeago timeago-contextual"
|
||||
datetime="{{ $currentBatteryEntry->last_tracked_time }}"></time>
|
||||
</td>
|
||||
<td>
|
||||
@if(FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->charge_interval_days > 0)
|
||||
<span id="battery-{{ $currentBatteryEntry->battery_id }}-next-charge-time">{{ $currentBatteryEntry->next_estimated_charge_time }}</span>
|
||||
@if (FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->charge_interval_days > 0)
|
||||
<span
|
||||
id="battery-{{ $currentBatteryEntry->battery_id }}-next-charge-time">{{ $currentBatteryEntry->next_estimated_charge_time }}</span>
|
||||
<time id="battery-{{ $currentBatteryEntry->battery_id }}-next-charge-time-timeago"
|
||||
class="timeago timeago-contextual"
|
||||
datetime="{{ $currentBatteryEntry->next_estimated_charge_time }}"></time>
|
||||
|
|
@ -191,37 +167,37 @@
|
|||
</td>
|
||||
<td class="d-none">
|
||||
{{ $currentBatteryEntry->due_type }}
|
||||
@if($currentBatteryEntry->due_type == 'duetoday')
|
||||
@if ($currentBatteryEntry->due_type == 'duetoday')
|
||||
duesoon
|
||||
@endif
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody',
|
||||
array( 'userfields'=> $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $currentBatteryEntry->battery_id)
|
||||
))
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$currentBatteryEntry->battery_id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="batteriesoverview-batterycard-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="batteriesoverview-batterycard-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content text-center">
|
||||
<div class="modal-body">
|
||||
@include('components.batterycard')
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -11,21 +11,15 @@
|
|||
|
||||
<hr class="my-2">
|
||||
|
||||
<form id="batterytracking-form"
|
||||
novalidate>
|
||||
<form id="batterytracking-form" novalidate>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="w-100"
|
||||
for="battery_id">
|
||||
<label class="w-100" for="battery_id">
|
||||
{{ $__t('Battery') }}
|
||||
<i id="barcode-lookup-hint"
|
||||
class="fas fa-barcode float-right mt-1"></i>
|
||||
<i id="barcode-lookup-hint" class="fas fa-barcode float-right mt-1"></i>
|
||||
</label>
|
||||
<select class="form-control combobox barcodescanner-input"
|
||||
id="battery_id"
|
||||
name="battery_id"
|
||||
required
|
||||
data-target="@batterypicker">
|
||||
{{-- TODO: Select2: dynamic data: batteries --}}
|
||||
<select class="form-control combobox barcodescanner-input" id="battery_id" name="battery_id" required data-target="@batterypicker">
|
||||
<option value=""></option>
|
||||
@foreach($batteries as $battery)
|
||||
<option value="{{ $battery->id }}">{{ $battery->name }}</option>
|
||||
|
|
@ -44,8 +38,7 @@
|
|||
'invalidFeedback' => $__t('This can only be before now')
|
||||
))
|
||||
|
||||
<button id="save-batterytracking-button"
|
||||
class="btn btn-success">{{ $__t('OK') }}</button>
|
||||
<button id="save-batterytracking-button" class="btn btn-success">{{ $__t('OK') }}</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@
|
|||
|
||||
@php $prefillById = ''; if($mode=='edit' && !empty($chore->product_id)) { $prefillById = $chore->product_id; } @endphp
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
'productsQuery' => 'order=name%3Acollate%20nocase',
|
||||
'nextInputSelector' => '#product_amount',
|
||||
'isRequired' => false,
|
||||
'disallowAllProductWorkflows' => true,
|
||||
|
|
|
|||
|
|
@ -5,26 +5,21 @@
|
|||
@section('viewJsName', 'chores')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/chore/new') }}">
|
||||
{{ $__t('Add') }}
|
||||
|
|
@ -36,97 +31,77 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="form-check custom-control custom-checkbox">
|
||||
<input class="form-check-input custom-control-input"
|
||||
type="checkbox"
|
||||
id="show-disabled">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="show-disabled">
|
||||
<input class="form-check-input custom-control-input" type="checkbox" id="show-disabled">
|
||||
<label class="form-check-label custom-control-label" for="show-disabled">
|
||||
{{ $__t('Show disabled') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="chores-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: chores --}}
|
||||
<table id="chores-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#chores-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#chores-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Period type') }}</th>
|
||||
<th>{{ $__t('Description') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($chores as $chore)
|
||||
<tr class="@if($chore->active == 0) text-muted @endif">
|
||||
@foreach ($chores as $chore)
|
||||
<tr class="@if ($chore->active == 0) text-muted @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm"
|
||||
href="{{ $U('/chore/') }}{{ $chore->id }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<a class="btn btn-info btn-sm" href="{{ $U('/chore/') }}{{ $chore->id }}"
|
||||
data-toggle="tooltip" title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm chore-delete-button"
|
||||
href="#"
|
||||
data-chore-id="{{ $chore->id }}"
|
||||
data-chore-name="{{ $chore->name }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<a class="btn btn-danger btn-sm chore-delete-button" href="#"
|
||||
data-chore-id="{{ $chore->id }}" data-chore-name="{{ $chore->name }}"
|
||||
data-toggle="tooltip" title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
<button class="btn btn-sm btn-light text-secondary" type="button"
|
||||
data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="table-inline-menu dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item merge-chores-button"
|
||||
data-chore-id="{{ $chore->id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
<a class="dropdown-item merge-chores-button" data-chore-id="{{ $chore->id }}"
|
||||
type="button" href="#">
|
||||
<span class="dropdown-item-text">{{ $__t('Merge') }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -142,21 +117,23 @@
|
|||
{{ $chore->description }}
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $chore->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$chore->id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="merge-chores-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="merge-chores-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content text-center">
|
||||
<div class="modal-header">
|
||||
|
|
@ -164,44 +141,38 @@
|
|||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="merge-chores-keep">{{ $__t('Chore to keep') }} <i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
<label for="merge-chores-keep">{{ $__t('Chore to keep') }} <i
|
||||
class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('After merging, this chore will be kept') }}"></i>
|
||||
</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="merge-chores-keep">
|
||||
{{-- TODO: Select2: dynamic data: chores --}}
|
||||
<select class="custom-control custom-select" id="merge-chores-keep">
|
||||
<option></option>
|
||||
@foreach($chores as $chore)
|
||||
@foreach ($chores as $chore)
|
||||
<option value="{{ $chore->id }}">{{ $chore->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="merge-chores-remove">{{ $__t('Chore to remove') }} <i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
<label for="merge-chores-remove">{{ $__t('Chore to remove') }} <i
|
||||
class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('After merging, all occurences of this chore will be replaced by the kept chore (means this chore will not exist anymore)') }}"></i>
|
||||
</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="merge-chores-remove">
|
||||
{{-- TODO: Select2: dynamic data: chores --}}
|
||||
<select class="custom-control custom-select" id="merge-chores-remove">
|
||||
<option></option>
|
||||
@foreach($chores as $chore)
|
||||
@foreach ($chores as $chore)
|
||||
<option value="{{ $chore->id }}">{{ $chore->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="merge-chores-save-button"
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="merge-chores-save-button" type="button" class="btn btn-primary"
|
||||
data-dismiss="modal">{{ $__t('OK') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,33 +5,27 @@
|
|||
@section('viewJsName', 'choresjournal')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button" data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
|
|
@ -39,10 +33,10 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Chore') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="chore-filter">
|
||||
{{-- TODO: Select2: dynamic data: chores --}}
|
||||
<select class="custom-control custom-select" id="chore-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($chores as $chore)
|
||||
@foreach ($chores as $chore)
|
||||
<option value="{{ $chore->id }}">{{ $chore->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -53,12 +47,10 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-clock"></i> {{ $__t('Date range') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="daterange-filter">
|
||||
<select class="custom-control custom-select" id="daterange-filter">
|
||||
<option value="1">{{ $__n(1, '%s month', '%s months') }}</option>
|
||||
<option value="6">{{ $__n(6, '%s month', '%s months') }}</option>
|
||||
<option value="12"
|
||||
selected>{{ $__n(1, '%s year', '%s years') }}</option>
|
||||
<option value="12" selected>{{ $__n(1, '%s year', '%s years') }}</option>
|
||||
<option value="24">{{ $__n(2, '%s month', '%s years') }}</option>
|
||||
<option value="9999">{{ $__t('All') }}</option>
|
||||
</select>
|
||||
|
|
@ -66,56 +58,49 @@
|
|||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="row mt-2">
|
||||
<div class="col">
|
||||
<table id="chores-journal-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: chores_log --}}
|
||||
<table id="chores-journal-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#chores-journal-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#chores-journal-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th class="allow-grouping">{{ $__t('Chore') }}</th>
|
||||
<th>{{ $__t('Tracked time') }}</th>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
@if (GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<th class="allow-grouping">{{ $__t('Done by') }}</th>
|
||||
@endif
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($choresLog as $choreLogEntry)
|
||||
@foreach ($choresLog as $choreLogEntry)
|
||||
<tr id="chore-execution-{{ $choreLogEntry->id }}-row"
|
||||
class="@if($choreLogEntry->undone == 1) text-muted @endif @if($choreLogEntry->skipped == 1) font-italic @endif">
|
||||
class="@if ($choreLogEntry->undone == 1) text-muted @endif @if ($choreLogEntry->skipped == 1) font-italic @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-secondary btn-xs undo-chore-execution-button permission-CHORE_UNDO_EXECUTION @if($choreLogEntry->undone == 1) disabled @endif"
|
||||
href="#"
|
||||
data-execution-id="{{ $choreLogEntry->id }}"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
title="{{ $__t('Undo chore execution') }}">
|
||||
<a class="btn btn-secondary btn-xs undo-chore-execution-button permission-CHORE_UNDO_EXECUTION @if ($choreLogEntry->undone == 1) disabled @endif"
|
||||
href="#" data-execution-id="{{ $choreLogEntry->id }}" data-toggle="tooltip"
|
||||
data-placement="left" title="{{ $__t('Undo chore execution') }}">
|
||||
<i class="fas fa-undo"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="name-anchor @if($choreLogEntry->undone == 1) text-strike-through @endif">{{ FindObjectInArrayByPropertyValue($chores, 'id', $choreLogEntry->chore_id)->name }}</span>
|
||||
@if($choreLogEntry->undone == 1)
|
||||
<span
|
||||
class="name-anchor @if ($choreLogEntry->undone == 1) text-strike-through @endif">{{ FindObjectInArrayByPropertyValue($chores, 'id', $choreLogEntry->chore_id)->name }}</span>
|
||||
@if ($choreLogEntry->undone == 1)
|
||||
<br>
|
||||
{{ $__t('Undone on') . ' ' . $choreLogEntry->undone_timestamp }}
|
||||
<time class="timeago timeago-contextual"
|
||||
|
|
@ -124,13 +109,14 @@
|
|||
</td>
|
||||
<td>
|
||||
<span>{{ $choreLogEntry->tracked_time }}</span>
|
||||
<time class="timeago timeago-contextual @if(FindObjectInArrayByPropertyValue($chores, 'id', $choreLogEntry->chore_id)->track_date_only == 1) timeago-date-only @endif"
|
||||
<time
|
||||
class="timeago timeago-contextual @if (FindObjectInArrayByPropertyValue($chores, 'id', $choreLogEntry->chore_id)->track_date_only == 1) timeago-date-only @endif"
|
||||
datetime="{{ $choreLogEntry->tracked_time }}"></time>
|
||||
@if($choreLogEntry->skipped == 1)
|
||||
@if ($choreLogEntry->skipped == 1)
|
||||
<span class="text-muted">{{ $__t('Skipped') }}</span>
|
||||
@endif
|
||||
</td>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
@if (GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<td>
|
||||
@if ($choreLogEntry->done_by_user_id !== null && !empty($choreLogEntry->done_by_user_id))
|
||||
{{ GetUserDisplayName(FindObjectInArrayByPropertyValue($users, 'id', $choreLogEntry->done_by_user_id)) }}
|
||||
|
|
@ -140,14 +126,18 @@
|
|||
</td>
|
||||
@endif
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $choreLogEntry->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$choreLogEntry->id
|
||||
),
|
||||
])
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,23 +5,19 @@
|
|||
@section('viewJsName', 'choresoverview')
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}" rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-outline-dark responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/choresjournal') }}">
|
||||
{{ $__t('Journal') }}
|
||||
|
|
@ -29,49 +25,37 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="border-top border-bottom my-2 py-1">
|
||||
<div id="info-overdue-chores"
|
||||
data-status-filter="overdue"
|
||||
<div id="info-overdue-chores" data-status-filter="overdue"
|
||||
class="error-message status-filter-message responsive-button mr-2"></div>
|
||||
<div id="info-due-today-chores"
|
||||
data-status-filter="duetoday"
|
||||
<div id="info-due-today-chores" data-status-filter="duetoday"
|
||||
class="normal-message status-filter-message responsive-button mr-2"></div>
|
||||
<div id="info-due-soon-chores"
|
||||
data-status-filter="duesoon"
|
||||
data-next-x-days="{{ $nextXDays }}"
|
||||
class="warning-message status-filter-message responsive-message mr-2 @if($nextXDays == 0) d-none @endif"></div>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<div id="info-assigned-to-me-chores"
|
||||
data-user-filter="xx{{ GROCY_USER_ID }}xx"
|
||||
<div id="info-due-soon-chores" data-status-filter="duesoon" data-next-x-days="{{ $nextXDays }}"
|
||||
class="warning-message status-filter-message responsive-message mr-2 @if ($nextXDays == 0) d-none @endif">
|
||||
</div>
|
||||
@if (GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<div id="info-assigned-to-me-chores" data-user-filter="xx{{ GROCY_USER_ID }}xx"
|
||||
class="secondary-message user-filter-message responsive-button"></div>
|
||||
@endif
|
||||
<div class="float-right">
|
||||
<a class="btn btn-sm btn-outline-info d-md-none mt-1"
|
||||
data-toggle="collapse"
|
||||
href="#table-filter-row"
|
||||
<a class="btn btn-sm btn-outline-info d-md-none mt-1" data-toggle="collapse" href="#table-filter-row"
|
||||
role="button">
|
||||
<i class="fas fa-filter"></i>
|
||||
</a>
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info mt-1"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info mt-1" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
|
|
@ -79,194 +63,180 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Status') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="status-filter">
|
||||
<select class="custom-control custom-select" id="status-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
<option value="overdue">{{ $__t('Overdue') }}</option>
|
||||
<option value="duetoday">{{ $__t('Due today') }}</option>
|
||||
@if($nextXDays > 0)
|
||||
@if ($nextXDays > 0)
|
||||
<option value="duesoon">{{ $__t('Due soon') }}</option>
|
||||
@endif
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
@if (GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Assignment') }}</span>
|
||||
<span class="input-group-text"><i
|
||||
class="fas fa-filter"></i> {{ $__t('Assignment') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="user-filter">
|
||||
{{-- TODO: Select2: dynamic data: users --}}
|
||||
<select class="custom-control custom-select" id="user-filter">
|
||||
<option></option>
|
||||
@foreach($users as $user)
|
||||
<option data-user-id="{{ $user->id }}"
|
||||
value="xx{{ $user->id }}xx">{{ $user->display_name }}</option>
|
||||
@foreach ($users as $user)
|
||||
<option data-user-id="{{ $user->id }}" value="xx{{ $user->id }}xx">
|
||||
{{ $user->display_name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="chores-overview-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: chores --}}
|
||||
<table id="chores-overview-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#chores-overview-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#chores-overview-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Chore') }}</th>
|
||||
<th>{{ $__t('Next estimated tracking') }}</th>
|
||||
<th>{{ $__t('Last tracked') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) d-none @endif allow-grouping">{{ $__t('Assigned to') }}</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) d-none @endif allow-grouping">
|
||||
{{ $__t('Assigned to') }}</th>
|
||||
<th class="d-none">Hidden status</th>
|
||||
<th class="d-none">Hidden assigned to user id</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($currentChores as $curentChoreEntry)
|
||||
@foreach ($currentChores as $curentChoreEntry)
|
||||
<tr id="chore-{{ $curentChoreEntry->chore_id }}-row"
|
||||
class="@if($curentChoreEntry->due_type == 'overdue') table-danger @elseif($curentChoreEntry->due_type == 'duetoday') table-info @elseif($curentChoreEntry->due_type == 'duesoon') table-warning @endif">
|
||||
class="@if ($curentChoreEntry->due_type == 'overdue') table-danger @elseif($curentChoreEntry->due_type == 'duetoday') table-info @elseif($curentChoreEntry->due_type == 'duesoon') table-warning @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-success btn-sm track-chore-button permission-CHORE_TRACK_EXECUTION"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
href="#" data-toggle="tooltip" data-placement="left"
|
||||
title="{{ $__t('Track chore execution') }}"
|
||||
data-chore-id="{{ $curentChoreEntry->chore_id }}"
|
||||
data-chore-name="{{ FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->name }}">
|
||||
<i class="fas fa-play"></i>
|
||||
</a>
|
||||
<a class="btn btn-secondary btn-sm track-chore-button skip permission-CHORE_TRACK_EXECUTION @if(FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->period_type == \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY) disabled @endif"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
<a class="btn btn-secondary btn-sm track-chore-button skip permission-CHORE_TRACK_EXECUTION @if (FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->period_type == \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY) disabled @endif"
|
||||
href="#" data-toggle="tooltip" data-placement="left"
|
||||
title="{{ $__t('Skip next chore schedule') }}"
|
||||
data-chore-id="{{ $curentChoreEntry->chore_id }}"
|
||||
data-chore-name="{{ FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->name }}">
|
||||
<i class="fas fa-forward"></i>
|
||||
</a>
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
<button class="btn btn-sm btn-light text-secondary" type="button"
|
||||
data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="table-inline-menu dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item chore-name-cell"
|
||||
data-chore-id="{{ $curentChoreEntry->chore_id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
data-chore-id="{{ $curentChoreEntry->chore_id }}" type="button" href="#">
|
||||
<span class="dropdown-item-text">{{ $__t('Chore overview') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/choresjournal?embedded&chore=') }}{{ $curentChoreEntry->chore_id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Chore journal') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item permission-MASTER_DATA_EDIT"
|
||||
type="button"
|
||||
<a class="dropdown-item permission-MASTER_DATA_EDIT" type="button"
|
||||
href="{{ $U('/chore/') }}{{ $curentChoreEntry->chore_id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Edit chore') }}</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
<a class="dropdown-item" type="button"
|
||||
href="{{ $U('/chore/' . $curentChoreEntry->chore_id . '/grocycode?download=true') }}">
|
||||
{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Download %s grocycode', $__t('Chore'))) !!}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
@if (GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<a class="dropdown-item chore-grocycode-label-print"
|
||||
data-chore-id="{{ $curentChoreEntry->chore_id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
data-chore-id="{{ $curentChoreEntry->chore_id }}" type="button" href="#">
|
||||
{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Print %s grocycode on label printer', $__t('Chore'))) !!}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="chore-name-cell cursor-link"
|
||||
data-chore-id="{{ $curentChoreEntry->chore_id }}">
|
||||
<td class="chore-name-cell cursor-link" data-chore-id="{{ $curentChoreEntry->chore_id }}">
|
||||
{{ FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->name }}
|
||||
</td>
|
||||
<td>
|
||||
@if(FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY)
|
||||
<span id="chore-{{ $curentChoreEntry->chore_id }}-next-execution-time">{{ $curentChoreEntry->next_estimated_execution_time }}</span>
|
||||
@if (FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY)
|
||||
<span
|
||||
id="chore-{{ $curentChoreEntry->chore_id }}-next-execution-time">{{ $curentChoreEntry->next_estimated_execution_time }}</span>
|
||||
<time id="chore-{{ $curentChoreEntry->chore_id }}-next-execution-time-timeago"
|
||||
class="timeago timeago-contextual @if($curentChoreEntry->track_date_only == 1) timeago-date-only @endif"
|
||||
class="timeago timeago-contextual @if ($curentChoreEntry->track_date_only == 1) timeago-date-only @endif"
|
||||
datetime="{{ $curentChoreEntry->next_estimated_execution_time }}"></time>
|
||||
@else
|
||||
<span>-</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<span id="chore-{{ $curentChoreEntry->chore_id }}-last-tracked-time">{{ $curentChoreEntry->last_tracked_time }}</span>
|
||||
<span
|
||||
id="chore-{{ $curentChoreEntry->chore_id }}-last-tracked-time">{{ $curentChoreEntry->last_tracked_time }}</span>
|
||||
<time id="chore-{{ $curentChoreEntry->chore_id }}-last-tracked-time-timeago"
|
||||
class="timeago timeago-contextual @if($curentChoreEntry->track_date_only == 1) timeago-date-only @endif"
|
||||
class="timeago timeago-contextual @if ($curentChoreEntry->track_date_only == 1) timeago-date-only @endif"
|
||||
datetime="{{ $curentChoreEntry->last_tracked_time }}"></time>
|
||||
</td>
|
||||
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) d-none @endif">
|
||||
<td class="@if (!GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS) d-none @endif">
|
||||
<span id="chore-{{ $curentChoreEntry->chore_id }}-next-execution-assigned-user">
|
||||
@if(!empty($curentChoreEntry->next_execution_assigned_to_user_id))
|
||||
@if (!empty($curentChoreEntry->next_execution_assigned_to_user_id))
|
||||
{{ FindObjectInArrayByPropertyValue($users, 'id', $curentChoreEntry->next_execution_assigned_to_user_id)->display_name }}
|
||||
@else
|
||||
<span>-</span>
|
||||
@endif
|
||||
</span>
|
||||
</td>
|
||||
<td id="chore-{{ $curentChoreEntry->chore_id }}-due-filter-column"
|
||||
class="d-none">
|
||||
<td id="chore-{{ $curentChoreEntry->chore_id }}-due-filter-column" class="d-none">
|
||||
{{ $curentChoreEntry->due_type }}
|
||||
@if($curentChoreEntry->due_type == 'duetoday')
|
||||
@if ($curentChoreEntry->due_type == 'duetoday')
|
||||
duesoon
|
||||
@endif
|
||||
</td>
|
||||
<td class="d-none">
|
||||
@if(!empty($curentChoreEntry->next_execution_assigned_to_user_id))
|
||||
@if (!empty($curentChoreEntry->next_execution_assigned_to_user_id))
|
||||
xx{{ $curentChoreEntry->next_execution_assigned_to_user_id }}xx
|
||||
</td>
|
||||
@endif
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $curentChoreEntry->chore_id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$curentChoreEntry->chore_id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="choresoverview-chorecard-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="choresoverview-chorecard-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content text-center">
|
||||
<div class="modal-body">
|
||||
@include('components.chorecard')
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -11,21 +11,15 @@
|
|||
|
||||
<hr class="my-2">
|
||||
|
||||
<form id="choretracking-form"
|
||||
novalidate>
|
||||
<form id="choretracking-form" novalidate>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="w-100"
|
||||
for="chore_id">
|
||||
<label class="w-100" for="chore_id">
|
||||
{{ $__t('Chore') }}
|
||||
<i id="barcode-lookup-hint"
|
||||
class="fas fa-barcode float-right mt-1"></i>
|
||||
<i id="barcode-lookup-hint" class="fas fa-barcode float-right mt-1"></i>
|
||||
</label>
|
||||
<select class="form-control combobox barcodescanner-input"
|
||||
id="chore_id"
|
||||
name="chore_id"
|
||||
required
|
||||
data-target="@chorepicker">
|
||||
{{-- TODO: Select2: dynamic data: chores --}}
|
||||
<select class="form-control combobox barcodescanner-input" id="chore_id" name="chore_id" required data-target="@chorepicker">
|
||||
<option value=""></option>
|
||||
@foreach($chores as $chore)
|
||||
<option value="{{ $chore->id }}">{{ $chore->name }}</option>
|
||||
|
|
@ -52,10 +46,7 @@
|
|||
'prefillByUserId' => GROCY_USER_ID
|
||||
))
|
||||
@else
|
||||
<input type="hidden"
|
||||
id="user_id"
|
||||
name="user_id"
|
||||
value="{{ GROCY_USER_ID }}">
|
||||
<input type="hidden" id="user_id" name="user_id" value="{{ GROCY_USER_ID }}">
|
||||
@endif
|
||||
|
||||
@include('components.userfieldsform', array(
|
||||
|
|
|
|||
|
|
@ -26,6 +26,17 @@
|
|||
margin-right: 36px !important;
|
||||
}
|
||||
|
||||
.input-group-prepend #barcodescanner-start-button {
|
||||
position: static;
|
||||
right: unset;
|
||||
margin: unset;
|
||||
}
|
||||
|
||||
.input-group>#barcodescanner-start-button-container+.select2-hidden-accessible+.select2-container--bootstrap>.selection>.select2-selection, .input-group>#barcodescanner-start-button-container+.select2-hidden-accessible+.select2-container--bootstrap>.selection>.select2-selection.form-control {
|
||||
border-top-right-radius: .25rem;
|
||||
border-bottom-right-radius: .25rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +1,48 @@
|
|||
@once
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/locationpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/locationpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
@endonce
|
||||
|
||||
@php if(empty($prefillByName)) { $prefillByName = ''; } @endphp
|
||||
@php if(empty($prefillById)) { $prefillById = ''; } @endphp
|
||||
@php if(!isset($isRequired)) { $isRequired = true; } @endphp
|
||||
@php if(empty($hint)) { $hint = ''; } @endphp
|
||||
@php if(empty($nextInputSelector)) { $nextInputSelector = ''; } @endphp
|
||||
@php
|
||||
if (empty($prefillByName)) {
|
||||
$prefillByName = '';
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (empty($prefillById)) {
|
||||
$prefillById = '';
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (!isset($isRequired)) {
|
||||
$isRequired = true;
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (empty($hint)) {
|
||||
$hint = '';
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (empty($nextInputSelector)) {
|
||||
$nextInputSelector = '';
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div class="form-group"
|
||||
data-next-input-selector="{{ $nextInputSelector }}"
|
||||
data-prefill-by-name="{{ $prefillByName }}"
|
||||
data-prefill-by-id="{{ $prefillById }}">
|
||||
<div class="form-group" data-next-input-selector="{{ $nextInputSelector }}"
|
||||
data-prefill-by-name="{{ $prefillByName }}" data-prefill-by-id="{{ $prefillById }}">
|
||||
<label for="location_id">{{ $__t('Location') }}
|
||||
@if(!empty($hint))
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
@if (!empty($hint))
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $hint }}"></i>
|
||||
@endif
|
||||
</label>
|
||||
<select class="form-control location-combobox"
|
||||
id="location_id"
|
||||
name="location_id"
|
||||
@if($isRequired)
|
||||
required
|
||||
@endif>
|
||||
{{-- TODO: Select2: dynamic data: locations --}}
|
||||
<select class="form-control location-combobox" id="location_id" name="location_id"
|
||||
@if ($isRequired) required @endif>
|
||||
<option value=""></option>
|
||||
@foreach($locations as $location)
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->id }}">{{ $location->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -1,72 +1,74 @@
|
|||
@once
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/productpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/productpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
@endonce
|
||||
|
||||
@php if(empty($disallowAddProductWorkflows)) { $disallowAddProductWorkflows = false; } @endphp
|
||||
@php if(empty($disallowAllProductWorkflows)) { $disallowAllProductWorkflows = false; } @endphp
|
||||
@php if(empty($prefillByName)) { $prefillByName = ''; } @endphp
|
||||
@php if(empty($prefillById)) { $prefillById = ''; } @endphp
|
||||
@php if(!isset($isRequired)) { $isRequired = true; } @endphp
|
||||
@php if(!isset($label)) { $label = 'Product'; } @endphp
|
||||
@php if(!isset($disabled)) { $disabled = false; } @endphp
|
||||
@php if(empty($hint)) { $hint = ''; } @endphp
|
||||
@php if(empty($nextInputSelector)) { $nextInputSelector = ''; } @endphp
|
||||
@php if(empty($validationMessage)) { $validationMessage = 'You have to select a product'; } @endphp
|
||||
@php
|
||||
if (empty($disallowAddProductWorkflows)) {
|
||||
$disallowAddProductWorkflows = false;
|
||||
}
|
||||
if (empty($disallowAllProductWorkflows)) {
|
||||
$disallowAllProductWorkflows = false;
|
||||
}
|
||||
if (empty($prefillByName)) {
|
||||
$prefillByName = '';
|
||||
}
|
||||
if (empty($prefillById)) {
|
||||
$prefillById = '';
|
||||
}
|
||||
if (!isset($isRequired)) {
|
||||
$isRequired = true;
|
||||
}
|
||||
if (!isset($label)) {
|
||||
$label = 'Product';
|
||||
}
|
||||
if (!isset($disabled)) {
|
||||
$disabled = false;
|
||||
}
|
||||
if (empty($hint)) {
|
||||
$hint = '';
|
||||
}
|
||||
if (empty($nextInputSelector)) {
|
||||
$nextInputSelector = '';
|
||||
}
|
||||
if (empty($validationMessage)) {
|
||||
$validationMessage = 'You have to select a product';
|
||||
}
|
||||
if (empty($productsQuery)) {
|
||||
$productsQuery = '';
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div class="form-group"
|
||||
data-next-input-selector="{{ $nextInputSelector }}"
|
||||
<div class="form-group" data-next-input-selector="{{ $nextInputSelector }}"
|
||||
data-disallow-add-product-workflows="{{ BoolToString($disallowAddProductWorkflows) }}"
|
||||
data-disallow-all-product-workflows="{{ BoolToString($disallowAllProductWorkflows) }}"
|
||||
data-prefill-by-name="{{ $prefillByName }}"
|
||||
data-prefill-by-id="{{ $prefillById }}">
|
||||
<label class="w-100"
|
||||
for="product_id">
|
||||
data-prefill-by-name="{{ $prefillByName }}" data-prefill-by-id="{{ $prefillById }}"
|
||||
data-products-query="{{ $productsQuery }}">
|
||||
<label class="w-100" for="product_id">
|
||||
{{ $__t($label) }}
|
||||
@if(!$disallowAllProductWorkflows)
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
@if (!$disallowAllProductWorkflows)
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('Type a new product name or barcode and hit TAB or ENTER to start a workflow') }}"></i>
|
||||
@endif
|
||||
@if(!empty($hint))
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
@if (!empty($hint))
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $hint }}"></i>
|
||||
@endif
|
||||
<span id="barcode-lookup-disabled-hint"
|
||||
class="small text-muted d-none float-right"> {{ $__t('Barcode lookup is disabled') }}</span>
|
||||
<i id="barcode-lookup-hint"
|
||||
class="fas fa-barcode float-right mt-1"></i>
|
||||
<span id="barcode-lookup-disabled-hint" class="small text-muted d-none float-right">
|
||||
{{ $__t('Barcode lookup is disabled') }}</span>
|
||||
<i id="barcode-lookup-hint" class="fas fa-barcode float-right mt-1"></i>
|
||||
</label>
|
||||
<select class="form-control product-combobox barcodescanner-input"
|
||||
id="product_id"
|
||||
name="product_id"
|
||||
@if($isRequired)
|
||||
required
|
||||
@endif
|
||||
@if($disabled)
|
||||
disabled
|
||||
@endif
|
||||
data-target="@productpicker">
|
||||
<option value=""></option>
|
||||
@foreach($products as $product)
|
||||
@php $bc = null;
|
||||
if(isset($barcodes)) {
|
||||
$bc = FindObjectInArrayByPropertyValue($barcodes, 'product_id', $product->id);
|
||||
}
|
||||
@endphp
|
||||
<option data-additional-searchdata="@if(isset($bc)){{ $bc->barcodes }}@endif,"
|
||||
value="{{ $product->id }}">{{ $product->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="input-group">
|
||||
<select class="select2 custom-control custom-select barcodescanner-input" id="product_id" name="product_id"
|
||||
@if ($isRequired) required @endif @if ($disabled) disabled @endif
|
||||
data-target="@productpicker"></select>
|
||||
<div class="invalid-feedback">{{ $__t($validationMessage) }}</div>
|
||||
<div id="custom-productpicker-error"
|
||||
class="form-text text-danger d-none"></div>
|
||||
<div id="flow-info-InplaceAddBarcodeToExistingProduct"
|
||||
class="form-text text-info small d-none"><strong><span id="InplaceAddBarcodeToExistingProduct"></span></strong> {{ $__t('will be added to the list of barcodes for the selected product on submit') }}</div>
|
||||
</div>
|
||||
<div id="custom-productpicker-error" class="form-text text-danger d-none"></div>
|
||||
<div id="flow-info-InplaceAddBarcodeToExistingProduct" class="form-text text-info small d-none"><strong><span
|
||||
id="InplaceAddBarcodeToExistingProduct"></span></strong>
|
||||
{{ $__t('will be added to the list of barcodes for the selected product on submit') }}</div>
|
||||
</div>
|
||||
|
||||
@include('components.barcodescanner')
|
||||
|
|
|
|||
|
|
@ -1,38 +1,49 @@
|
|||
@once
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/recipepicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/recipepicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
@endonce
|
||||
|
||||
@php if(empty($prefillByName)) { $prefillByName = ''; } @endphp
|
||||
@php if(empty($prefillById)) { $prefillById = ''; } @endphp
|
||||
@php if(!isset($isRequired)) { $isRequired = true; } @endphp
|
||||
@php if(empty($hint)) { $hint = ''; } @endphp
|
||||
@php if(empty($nextInputSelector)) { $nextInputSelector = ''; } @endphp
|
||||
@php
|
||||
if (empty($prefillByName)) {
|
||||
$prefillByName = '';
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (empty($prefillById)) {
|
||||
$prefillById = '';
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (!isset($isRequired)) {
|
||||
$isRequired = true;
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (empty($hint)) {
|
||||
$hint = '';
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (empty($nextInputSelector)) {
|
||||
$nextInputSelector = '';
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div class="form-group"
|
||||
data-next-input-selector="{{ $nextInputSelector }}"
|
||||
data-prefill-by-name="{{ $prefillByName }}"
|
||||
data-prefill-by-id="{{ $prefillById }}">
|
||||
<label class="w-100"
|
||||
for="recipe_id">{{ $__t('Recipe') }}
|
||||
@if(!empty($hint))
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
<div class="form-group" data-next-input-selector="{{ $nextInputSelector }}"
|
||||
data-prefill-by-name="{{ $prefillByName }}" data-prefill-by-id="{{ $prefillById }}">
|
||||
<label class="w-100" for="recipe_id">{{ $__t('Recipe') }}
|
||||
@if (!empty($hint))
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $hint }}"></i>
|
||||
@endif
|
||||
<i class="fas fa-barcode float-right mt-1"></i>
|
||||
</label>
|
||||
<select class="form-control recipe-combobox barcodescanner-input"
|
||||
id="recipe_id"
|
||||
name="recipe_id"
|
||||
data-target="@recipepicker"
|
||||
@if($isRequired)
|
||||
required
|
||||
@endif>
|
||||
{{-- TODO: Select2: dynamic data: recipes --}}
|
||||
<select class="form-control recipe-combobox barcodescanner-input" id="recipe_id" name="recipe_id"
|
||||
data-target="@recipepicker" @if ($isRequired) required @endif>
|
||||
<option value=""></option>
|
||||
@foreach($recipes as $recipe)
|
||||
@foreach ($recipes as $recipe)
|
||||
<option value="{{ $recipe->id }}">{{ $recipe->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -1,30 +1,45 @@
|
|||
@once
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/shoppinglocationpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/shoppinglocationpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
@endonce
|
||||
|
||||
@php if(empty($prefillByName)) { $prefillByName = ''; } @endphp
|
||||
@php if(empty($prefillById)) { $prefillById = ''; } @endphp
|
||||
@php if(!isset($isRequired)) { $isRequired = false; } @endphp
|
||||
@php if(empty($hint)) { $hint = ''; } @endphp
|
||||
@php if(empty($nextInputSelector)) { $nextInputSelector = ''; } @endphp
|
||||
@php
|
||||
if (empty($prefillByName)) {
|
||||
$prefillByName = '';
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (empty($prefillById)) {
|
||||
$prefillById = '';
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (!isset($isRequired)) {
|
||||
$isRequired = false;
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (empty($hint)) {
|
||||
$hint = '';
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (empty($nextInputSelector)) {
|
||||
$nextInputSelector = '';
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div class="form-group"
|
||||
data-next-input-selector="{{ $nextInputSelector }}"
|
||||
data-prefill-by-name="{{ $prefillByName }}"
|
||||
data-prefill-by-id="{{ $prefillById }}">
|
||||
<label for="shopping_location_id">{{ $__t($label) }} <span @if(!empty($hintId))id="{{ $hintId }}"
|
||||
@endif
|
||||
<div class="form-group" data-next-input-selector="{{ $nextInputSelector }}"
|
||||
data-prefill-by-name="{{ $prefillByName }}" data-prefill-by-id="{{ $prefillById }}">
|
||||
<label for="shopping_location_id">{{ $__t($label) }} <span
|
||||
@if (!empty($hintId)) id="{{ $hintId }}" @endif
|
||||
class="small text-muted">{{ $hint }}</span></label>
|
||||
<select class="form-control shopping-location-combobox"
|
||||
id="shopping_location_id"
|
||||
name="shopping_location_id"
|
||||
@if($isRequired)
|
||||
required
|
||||
@endif>
|
||||
{{-- TODO: Select2: dynamic data: shopping_locations --}}
|
||||
<select class="form-control shopping-location-combobox" id="shopping_location_id" name="shopping_location_id"
|
||||
@if ($isRequired) required @endif>
|
||||
<option value=""></option>
|
||||
@foreach($shoppinglocations as $shoppinglocation)
|
||||
@foreach ($shoppinglocations as $shoppinglocation)
|
||||
<option value="{{ $shoppinglocation->id }}">{{ $shoppinglocation->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -1,25 +1,34 @@
|
|||
@once
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/userpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/userpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
@endonce
|
||||
|
||||
@php if(empty($prefillByUsername)) { $prefillByUsername = ''; } @endphp
|
||||
@php if(empty($prefillByUserId)) { $prefillByUserId = ''; } @endphp
|
||||
@php if(!isset($nextInputSelector)) { $nextInputSelector = ''; } @endphp
|
||||
@php
|
||||
if (empty($prefillByUsername)) {
|
||||
$prefillByUsername = '';
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (empty($prefillByUserId)) {
|
||||
$prefillByUserId = '';
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if (!isset($nextInputSelector)) {
|
||||
$nextInputSelector = '';
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div class="form-group"
|
||||
data-next-input-selector="{{ $nextInputSelector }}"
|
||||
data-prefill-by-username="{{ $prefillByUsername }}"
|
||||
data-prefill-by-user-id="{{ $prefillByUserId }}">
|
||||
<div class="form-group" data-next-input-selector="{{ $nextInputSelector }}"
|
||||
data-prefill-by-username="{{ $prefillByUsername }}" data-prefill-by-user-id="{{ $prefillByUserId }}">
|
||||
<label for="user_id">{{ $__t($label) }}</label>
|
||||
<select class="form-control user-combobox"
|
||||
id="user_id"
|
||||
name="user_id">
|
||||
{{-- TODO: Select2: dynamic data: users --}}
|
||||
<select class="form-control user-combobox" id="user_id" name="user_id">
|
||||
<option value=""></option>
|
||||
@foreach($users as $user)
|
||||
<option data-additional-searchdata="{{ $user->username }}"
|
||||
value="{{ $user->id }}">{{ GetUserDisplayName($user) }}</option>
|
||||
@foreach ($users as $user)
|
||||
<option data-additional-searchdata="{{ $user->username }}" value="{{ $user->id }}">
|
||||
{{ GetUserDisplayName($user) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,40 +5,39 @@
|
|||
@section('viewJsName', 'consume')
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/js/grocy_uisound.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/js/grocy_uisound.js?v=', true) }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<script>
|
||||
<script>
|
||||
Grocy.QuantityUnits = {!! json_encode($quantityUnits) !!};
|
||||
Grocy.QuantityUnitConversionsResolved = {!! json_encode($quantityUnitConversionsResolved) !!};
|
||||
Grocy.DefaultMinAmount = '{{$DEFAULT_MIN_AMOUNT}}';
|
||||
</script>
|
||||
Grocy.DefaultMinAmount = '{{ $DEFAULT_MIN_AMOUNT }}';
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 col-xl-4 pb-3">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3 hide-when-embedded"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
type="button" data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
@if(!$embedded)
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
@if (!$embedded)
|
||||
<button id="scan-mode-button"
|
||||
class="btn @if(boolval($userSettings['scan_mode_consume_enabled'])) btn-success @else btn-danger @endif m-1 mt-md-0 mb-md-0 float-right"
|
||||
class="btn @if (boolval($userSettings['scan_mode_consume_enabled'])) btn-success @else btn-danger @endif m-1 mt-md-0 mb-md-0 float-right"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('When enabled, after changing/scanning a product and if all fields could be automatically populated (by product and/or barcode defaults), the transaction is automatically submitted') }}">{{ $__t('Scan mode') }} <span id="scan-mode-status">@if(boolval($userSettings['scan_mode_consume_enabled'])) {{ $__t('on') }} @else {{ $__t('off') }} @endif</span></button>
|
||||
<input id="scan-mode"
|
||||
type="checkbox"
|
||||
class="d-none user-setting-control"
|
||||
data-setting-key="scan_mode_consume_enabled"
|
||||
@if(boolval($userSettings['scan_mode_consume_enabled']))
|
||||
checked
|
||||
@endif>
|
||||
title="{{ $__t('When enabled, after changing/scanning a product and if all fields could be automatically populated (by product and/or barcode defaults), the transaction is automatically submitted') }}">{{ $__t('Scan mode') }}
|
||||
<span id="scan-mode-status">
|
||||
@if (boolval($userSettings['scan_mode_consume_enabled']))
|
||||
{{ $__t('on') }}
|
||||
@else
|
||||
{{ $__t('off') }}
|
||||
@endif
|
||||
</span></button>
|
||||
<input id="scan-mode" type="checkbox" class="d-none user-setting-control"
|
||||
data-setting-key="scan_mode_consume_enabled" @if (boolval($userSettings['scan_mode_consume_enabled'])) checked @endif>
|
||||
@else
|
||||
<script>
|
||||
Grocy.UserSettings.scan_mode_consume_enabled = false;
|
||||
|
|
@ -49,44 +48,41 @@
|
|||
|
||||
<hr class="my-2">
|
||||
|
||||
<form id="consume-form"
|
||||
novalidate>
|
||||
<form id="consume-form" novalidate>
|
||||
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
'barcodes' => $barcodes,
|
||||
@include('components.productpicker', [
|
||||
'productsQuery' => 'query%5B%5D=active%3D1&only_in_stock=1&order=name',
|
||||
'nextInputSelector' => '#amount',
|
||||
'disallowAddProductWorkflows' => true
|
||||
))
|
||||
'disallowAddProductWorkflows' => true,
|
||||
])
|
||||
|
||||
<div id="consume-exact-amount-group"
|
||||
class="form-group d-none">
|
||||
<div id="consume-exact-amount-group" class="form-group d-none">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="form-check-input custom-control-input"
|
||||
type="checkbox"
|
||||
id="consume-exact-amount"
|
||||
name="consume-exact-amount"
|
||||
value="1">
|
||||
<input class="form-check-input custom-control-input" type="checkbox" id="consume-exact-amount"
|
||||
name="consume-exact-amount" value="1">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="consume-exact-amount">{{ $__t('Consume exact amount') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('components.productamountpicker', array(
|
||||
@include('components.productamountpicker', [
|
||||
'value' => 1,
|
||||
'additionalHtmlContextHelp' => '<div id="tare-weight-handling-info"
|
||||
class="text-info font-italic d-none">' . $__t('Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated') . '</div>'
|
||||
))
|
||||
'additionalHtmlContextHelp' =>
|
||||
'<div id="tare-weight-handling-info" class="text-info font-italic d-none">' .
|
||||
$__t(
|
||||
'Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated'
|
||||
) .
|
||||
'</div>',
|
||||
])
|
||||
|
||||
<div class="form-group @if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif">
|
||||
<div class="form-group @if (!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif">
|
||||
<label for="location_id">{{ $__t('Location') }}</label>
|
||||
<select required
|
||||
class="custom-control custom-select location-combobox"
|
||||
id="location_id"
|
||||
{{-- TODO: Select2: dynamic data: locations --}}
|
||||
<select required class="custom-control custom-select location-combobox" id="location_id"
|
||||
name="location_id">
|
||||
<option></option>
|
||||
@foreach($locations as $location)
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->id }}">{{ $location->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -95,52 +91,41 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="form-check-input custom-control-input"
|
||||
type="checkbox"
|
||||
id="spoiled"
|
||||
name="spoiled"
|
||||
<input class="form-check-input custom-control-input" type="checkbox" id="spoiled" name="spoiled"
|
||||
value="1">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="spoiled">{{ $__t('Spoiled') }}
|
||||
<label class="form-check-label custom-control-label" for="spoiled">{{ $__t('Spoiled') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="form-check-input custom-control-input"
|
||||
type="checkbox"
|
||||
id="use_specific_stock_entry"
|
||||
name="use_specific_stock_entry"
|
||||
value="1">
|
||||
<input class="form-check-input custom-control-input" type="checkbox" id="use_specific_stock_entry"
|
||||
name="use_specific_stock_entry" value="1">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="use_specific_stock_entry">{{ $__t('Use a specific stock item') }}
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
title="{{ $__t('The first item in this list would be picked by the default rule which is "Opened first, then first due first, then first in first out"') }}"></i>
|
||||
</label>
|
||||
</div>
|
||||
<select disabled
|
||||
class="custom-control custom-select mt-2"
|
||||
id="specific_stock_entry"
|
||||
<select disabled class="custom-control custom-select mt-2" id="specific_stock_entry"
|
||||
name="specific_stock_entry">
|
||||
<option></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@if (GROCY_FEATURE_FLAG_RECIPES)
|
||||
@include('components.recipepicker', array(
|
||||
@include('components.recipepicker', [
|
||||
'recipes' => $recipes,
|
||||
'isRequired' => false,
|
||||
'hint' => $__t('This is for statistical purposes only')
|
||||
))
|
||||
'hint' => $__t('This is for statistical purposes only'),
|
||||
])
|
||||
@endif
|
||||
|
||||
<button id="save-consume-button"
|
||||
class="btn btn-success">{{ $__t('OK') }}</button>
|
||||
<button id="save-consume-button" class="btn btn-success">{{ $__t('OK') }}</button>
|
||||
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
|
||||
<button id="save-mark-as-open-button"
|
||||
class="btn btn-secondary permission-STOCK_OPEN">{{ $__t('Mark as opened') }}</button>
|
||||
@endif
|
||||
|
|
@ -151,5 +136,5 @@
|
|||
<div class="col-12 col-md-6 col-xl-4 hide-when-embedded">
|
||||
@include('components.productcard')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,26 +5,21 @@
|
|||
@section('viewJsName', 'equipment')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4 pb-3">
|
||||
<div class="title-related-links border-bottom mb-2 py-1">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/equipment/new') }}">
|
||||
{{ $__t('Add') }}
|
||||
|
|
@ -32,69 +27,57 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id="equipment-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: equipment --}}
|
||||
<table id="equipment-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#equipment-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#equipment-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($equipment as $equipmentItem)
|
||||
@foreach ($equipment as $equipmentItem)
|
||||
<tr data-equipment-id="{{ $equipmentItem->id }}">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm hide-when-embedded hide-on-fullscreen-card"
|
||||
href="{{ $U('/equipment/') }}{{ $equipmentItem->id }}"
|
||||
data-toggle="tooltip"
|
||||
href="{{ $U('/equipment/') }}{{ $equipmentItem->id }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
<button class="btn btn-sm btn-light text-secondary" type="button"
|
||||
data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="table-inline-menu dropdown-menu dropdown-menu-right hide-on-fullscreen-card hide-when-embedded">
|
||||
<a class="dropdown-item equipment-delete-button"
|
||||
type="button"
|
||||
href="#"
|
||||
<div
|
||||
class="table-inline-menu dropdown-menu dropdown-menu-right hide-on-fullscreen-card hide-when-embedded">
|
||||
<a class="dropdown-item equipment-delete-button" type="button" href="#"
|
||||
data-equipment-id="{{ $equipmentItem->id }}"
|
||||
data-equipment-name="{{ $equipmentItem->name }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Delete this item') }}</span>
|
||||
|
|
@ -106,10 +89,14 @@
|
|||
{{ $equipmentItem->name }}
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $equipmentItem->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$equipmentItem->id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
|
|
@ -120,70 +107,54 @@
|
|||
<div class="col-12 col-md-8">
|
||||
<ul class="nav nav-tabs grocy-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active"
|
||||
data-toggle="tab"
|
||||
<a class="nav-link active" data-toggle="tab"
|
||||
href="#instruction-manual-tab">{{ $__t('Instruction manual') }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
data-toggle="tab"
|
||||
href="#description-tab">{{ $__t('Notes') }}</a>
|
||||
<a class="nav-link" data-toggle="tab" href="#description-tab">{{ $__t('Notes') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content grocy-tabs">
|
||||
<div class="tab-pane fade show active"
|
||||
id="instruction-manual-tab">
|
||||
<div id="selectedEquipmentInstructionManualCard"
|
||||
class="card">
|
||||
<div class="tab-pane fade show active" id="instruction-manual-tab">
|
||||
<div id="selectedEquipmentInstructionManualCard" class="card">
|
||||
<div class="card-header card-header-fullscreen">
|
||||
<span class="selected-equipment-name"></span>
|
||||
<a id="selectedEquipmentInstructionManualToggleFullscreenButton"
|
||||
class="btn btn-sm btn-outline-secondary py-0 float-right mr-1"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Expand to fullscreen') }}">
|
||||
class="btn btn-sm btn-outline-secondary py-0 float-right mr-1" href="#"
|
||||
data-toggle="tooltip" title="{{ $__t('Expand to fullscreen') }}">
|
||||
<i class="fas fa-expand-arrows-alt"></i>
|
||||
</a>
|
||||
<a id="selectedEquipmentInstructionManualDownloadButton"
|
||||
class="btn btn-sm btn-outline-secondary py-0 float-right mr-1"
|
||||
href="#"
|
||||
target="_blank"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Download file') }}">
|
||||
class="btn btn-sm btn-outline-secondary py-0 float-right mr-1" href="#" target="_blank"
|
||||
data-toggle="tooltip" title="{{ $__t('Download file') }}">
|
||||
<i class="fas fa-file-download"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body py-0 px-0">
|
||||
<p id="selected-equipment-has-no-instruction-manual-hint"
|
||||
class="text-muted font-italic d-none pt-3 pl-3">{{ $__t('The selected equipment has no instruction manual') }}</p>
|
||||
<embed id="selected-equipment-instruction-manual"
|
||||
class="embed-responsive embed-responsive-4by3"
|
||||
src=""
|
||||
type="application/pdf">
|
||||
class="text-muted font-italic d-none pt-3 pl-3">
|
||||
{{ $__t('The selected equipment has no instruction manual') }}</p>
|
||||
<embed id="selected-equipment-instruction-manual" class="embed-responsive embed-responsive-4by3"
|
||||
src="" type="application/pdf">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade"
|
||||
id="description-tab">
|
||||
<div id="selectedEquipmentDescriptionCard"
|
||||
class="card">
|
||||
<div class="tab-pane fade" id="description-tab">
|
||||
<div id="selectedEquipmentDescriptionCard" class="card">
|
||||
<div class="card-header card-header-fullscreen">
|
||||
<span class="selected-equipment-name"></span>
|
||||
<a id="selectedEquipmentDescriptionToggleFullscreenButton"
|
||||
class="btn btn-sm btn-outline-secondary py-0 float-right"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
class="btn btn-sm btn-outline-secondary py-0 float-right" href="#" data-toggle="tooltip"
|
||||
title="{{ $__t('Expand to fullscreen') }}">
|
||||
<i class="fas fa-expand-arrows-alt"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="description-tab-content"
|
||||
class="mb-0"></div>
|
||||
<div id="description-tab-content" class="mb-0"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@
|
|||
novalidate>
|
||||
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
'barcodes' => $barcodes,
|
||||
'productsQuery' => 'query%5B%5D=active%3D1&order=name%3Acollate%20nocase',
|
||||
'nextInputSelector' => '#new_amount'
|
||||
))
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,10 @@
|
|||
rel="stylesheet">
|
||||
<link href="{{ $U('/node_modules/@fontsource/noto-sans/latin.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/node_modules/select2/dist/css/select2.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/node_modules/select2-theme-bootstrap4/dist/select2-bootstrap.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/css/grocy.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/css/grocy_night_mode.css?v=', true) }}{{ $version }}"
|
||||
|
|
@ -706,6 +710,7 @@
|
|||
@if(!empty($__t('bootstrap-select_locale') && $__t('bootstrap-select_locale') != 'x'))<script src="{{ $U('/node_modules', true) }}/bootstrap-select/dist/js/i18n/defaults-{{ $__t('bootstrap-select_locale') }}.js?v={{ $version }}"></script>@endif
|
||||
<script src="{{ $U('/node_modules/jquery-lazy/jquery.lazy.min.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/node_modules/nosleep.js/dist/NoSleep.min.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/node_modules/select2/dist/js/select2.min.js?v=', true) }}{{ $version }}"></script>
|
||||
|
||||
<script src="{{ $U('/js/extensions.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/js/grocy.js?v=', true) }}{{ $version }}"></script>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
@section('viewJsName', 'locationcontentsheet')
|
||||
|
||||
@push('pageStyles')
|
||||
<style>
|
||||
<style>
|
||||
@media print {
|
||||
.page:not(:last-child) {
|
||||
page-break-after: always !important;
|
||||
|
|
@ -24,46 +24,39 @@
|
|||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="title-related-links d-print-none">
|
||||
<div class="title-related-links d-print-none">
|
||||
<h2 class="title">
|
||||
@yield('title')
|
||||
<i class="fas fa-question-circle text-muted small"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
<i class="fas fa-question-circle text-muted small" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('Here you can print a page per location with the current stock, maybe to hang it there and note the consumed things on it') }}"></i>
|
||||
</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button" data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-outline-dark responsive-button m-1 mt-md-0 mb-md-0 float-right print-all-locations-button"
|
||||
href="#">
|
||||
{{ $__t('Print') . ' (' . $__t('all locations') . ')' }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2 d-print-none">
|
||||
<hr class="my-2 d-print-none">
|
||||
|
||||
@foreach($locations as $location)
|
||||
<div class="page">
|
||||
@foreach ($locations as $location)
|
||||
<div class="page">
|
||||
<h1 class="pt-4 text-center">
|
||||
<img src="{{ $U('/img/grocy_logo.svg?v=', true) }}{{ $version }}"
|
||||
height="30"
|
||||
<img src="{{ $U('/img/grocy_logo.svg?v=', true) }}{{ $version }}" height="30"
|
||||
class="d-none d-print-flex mx-auto">
|
||||
{{ $location->name }}
|
||||
<a class="btn btn-outline-dark btn-sm responsive-button print-single-location-button d-print-none"
|
||||
href="#">
|
||||
<a class="btn btn-outline-dark btn-sm responsive-button print-single-location-button d-print-none" href="#">
|
||||
{{ $__t('Print') . ' (' . $__t('this location') . ')' }}
|
||||
</a>
|
||||
</h1>
|
||||
|
|
@ -73,6 +66,7 @@
|
|||
</h6>
|
||||
<div class="row w-75">
|
||||
<div class="col">
|
||||
{{-- TODO: DataTables: dynamic data: stock --}}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -83,14 +77,21 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
@php $currentStockEntriesForLocation = FindAllObjectsInArrayByPropertyValue($currentStockLocationContent, 'location_id', $location->id); @endphp
|
||||
@foreach($currentStockEntriesForLocation as $currentStockEntry)
|
||||
@foreach ($currentStockEntriesForLocation as $currentStockEntry)
|
||||
<tr>
|
||||
<td>
|
||||
{{ FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name }}
|
||||
</td>
|
||||
<td>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->amount }}</span> <span id="product-{{ $currentStockEntry->product_id }}-qu-name">{{ $__n($currentStockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural, true) }}</span>
|
||||
<span class="small font-italic">@if($currentStockEntry->amount_opened > 0){{ $__t('%s opened', $currentStockEntry->amount_opened) }}@endif</span>
|
||||
<span
|
||||
class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->amount }}</span>
|
||||
<span
|
||||
id="product-{{ $currentStockEntry->product_id }}-qu-name">{{ $__n($currentStockEntry->amount,FindObjectInArrayByPropertyValue($quantityunits,'id',FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name,FindObjectInArrayByPropertyValue($quantityunits,'id',FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural,true) }}</span>
|
||||
<span class="small font-italic">
|
||||
@if ($currentStockEntry->amount_opened > 0)
|
||||
{{ $__t('%s opened', $currentStockEntry->amount_opened) }}
|
||||
@endif
|
||||
</span>
|
||||
</td>
|
||||
<td class=""></td>
|
||||
</tr>
|
||||
|
|
@ -99,6 +100,6 @@
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,26 +5,21 @@
|
|||
@section('viewJsName', 'locations')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button m-1 mt-md-0 mb-md-0 float-right show-as-dialog-link"
|
||||
href="{{ $U('/location/new?embedded') }}">
|
||||
{{ $__t('Add') }}
|
||||
|
|
@ -36,72 +31,59 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="locations-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: locations --}}
|
||||
<table id="locations-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#locations-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#locations-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th>{{ $__t('Description') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($locations as $location)
|
||||
@foreach ($locations as $location)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm show-as-dialog-link"
|
||||
href="{{ $U('/location/') }}{{ $location->id }}?embedded"
|
||||
data-toggle="tooltip"
|
||||
href="{{ $U('/location/') }}{{ $location->id }}?embedded" data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm location-delete-button"
|
||||
href="#"
|
||||
data-location-id="{{ $location->id }}"
|
||||
data-location-name="{{ $location->name }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<a class="btn btn-danger btn-sm location-delete-button" href="#"
|
||||
data-location-id="{{ $location->id }}" data-location-name="{{ $location->name }}"
|
||||
data-toggle="tooltip" title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
|
|
@ -112,15 +94,19 @@
|
|||
{{ $location->description }}
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $location->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$location->id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,35 +5,29 @@
|
|||
@section('viewJsName', 'manageapikeys')
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/node_modules/bwip-js/dist/bwip-js-min.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/node_modules/bwip-js/dist/bwip-js-min.js?v=', true) }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}" rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/manageapikeys/new') }}">
|
||||
{{ $__t('Add') }}
|
||||
|
|
@ -41,46 +35,37 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="apikeys-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: API keys --}}
|
||||
<table id="apikeys-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#apikeys-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#apikeys-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('API key') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('User') }}</th>
|
||||
|
|
@ -91,22 +76,17 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($apiKeys as $apiKey)
|
||||
@foreach ($apiKeys as $apiKey)
|
||||
<tr id="apiKeyRow_{{ $apiKey->id }}">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-danger btn-sm apikey-delete-button"
|
||||
href="#"
|
||||
data-apikey-id="{{ $apiKey->id }}"
|
||||
data-apikey-apikey="{{ $apiKey->api_key }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<a class="btn btn-danger btn-sm apikey-delete-button" href="#"
|
||||
data-apikey-id="{{ $apiKey->id }}" data-apikey-apikey="{{ $apiKey->api_key }}"
|
||||
data-toggle="tooltip" title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<a class="btn btn-info btn-sm apikey-show-qr-button"
|
||||
href="#"
|
||||
<a class="btn btn-info btn-sm apikey-show-qr-button" href="#"
|
||||
data-apikey-key="{{ $apiKey->api_key }}"
|
||||
data-apikey-type="{{ $apiKey->key_type }}"
|
||||
data-toggle="tooltip"
|
||||
data-apikey-type="{{ $apiKey->key_type }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Show a QR-Code for this API key') }}">
|
||||
<i class="fas fa-qrcode"></i>
|
||||
</a>
|
||||
|
|
@ -119,13 +99,13 @@
|
|||
</td>
|
||||
<td>
|
||||
{{ $apiKey->expires }}
|
||||
<time class="timeago timeago-contextual"
|
||||
datetime="{{ $apiKey->expires }}"></time>
|
||||
<time class="timeago timeago-contextual" datetime="{{ $apiKey->expires }}"></time>
|
||||
</td>
|
||||
<td>
|
||||
@if(empty($apiKey->last_used)){{ $__t('never') }}@else{{ $apiKey->last_used }}@endif
|
||||
<time class="timeago timeago-contextual"
|
||||
datetime="{{ $apiKey->last_used }}"></time>
|
||||
@if (empty($apiKey->last_used))
|
||||
{{ $__t('never') }}@else{{ $apiKey->last_used }}
|
||||
@endif
|
||||
<time class="timeago timeago-contextual" datetime="{{ $apiKey->last_used }}"></time>
|
||||
</td>
|
||||
<td>
|
||||
{{ $apiKey->row_created_timestamp }}
|
||||
|
|
@ -140,5 +120,5 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,15 +5,19 @@
|
|||
@section('viewJsName', 'mealplan')
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/node_modules/fullcalendar/dist/fullcalendar.min.js?v=', true) }}{{ $version }}"></script>
|
||||
@if(!empty($__t('fullcalendar_locale') && $__t('fullcalendar_locale') != 'x'))<script src="{{ $U('/node_modules', true) }}/fullcalendar/dist/locale/{{ $__t('fullcalendar_locale') }}.js?v={{ $version }}"></script>@endif
|
||||
<script src="{{ $U('/node_modules/fullcalendar/dist/fullcalendar.min.js?v=', true) }}{{ $version }}"></script>
|
||||
@if (!empty($__t('fullcalendar_locale') && $__t('fullcalendar_locale') != 'x'))
|
||||
<script
|
||||
src="{{ $U('/node_modules', true) }}/fullcalendar/dist/locale/{{ $__t('fullcalendar_locale') }}.js?v={{ $version }}">
|
||||
</script>
|
||||
@endif
|
||||
@endpush
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/fullcalendar/dist/fullcalendar.min.css?v=', true) }}{{ $version }}"
|
||||
<link href="{{ $U('/node_modules/fullcalendar/dist/fullcalendar.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
|
||||
<style>
|
||||
<style>
|
||||
.fc-event-container {
|
||||
border-bottom: 1px solid !important;
|
||||
border-color: #d6d6d6 !important;
|
||||
|
|
@ -71,12 +75,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<script>
|
||||
var fullcalendarEventSources = {!! json_encode(array($fullcalendarEventSources)) !!}
|
||||
<script>
|
||||
var fullcalendarEventSources = {!! json_encode([$fullcalendarEventSources]) !!}
|
||||
var internalRecipes = {!! json_encode($internalRecipes) !!}
|
||||
var recipesResolved = {!! json_encode($recipesResolved) !!}
|
||||
|
||||
|
|
@ -84,24 +88,20 @@
|
|||
Grocy.QuantityUnitConversionsResolved = {!! json_encode($quantityUnitConversionsResolved) !!};
|
||||
|
||||
Grocy.MealPlanFirstDayOfWeek = '{{ GROCY_MEAL_PLAN_FIRST_DAY_OF_WEEK }}';
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right d-print-none">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100 d-print-none"
|
||||
id="related-links">
|
||||
<a id="print-meal-plan-button"
|
||||
class="btn btn-outline-dark m-1 mt-md-0 mb-md-0 float-right">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100 d-print-none" id="related-links">
|
||||
<a id="print-meal-plan-button" class="btn btn-outline-dark m-1 mt-md-0 mb-md-0 float-right">
|
||||
{{ $__t('Print') }}
|
||||
</a>
|
||||
<a class="btn btn-outline-secondary m-1 mt-md-0 mb-md-0 float-right"
|
||||
|
|
@ -111,220 +111,173 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
@foreach($usedMealplanSections as $mealplanSection)
|
||||
<div class="row">
|
||||
@foreach ($usedMealplanSections as $mealplanSection)
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="calendar"
|
||||
data-section-id="{{ $mealplanSection->id }}"
|
||||
<div class="calendar" data-section-id="{{ $mealplanSection->id }}"
|
||||
data-section-name="{{ $mealplanSection->name }}<br><span class='small text-muted'>{{ $mealplanSection->time_info }}</span>"
|
||||
data-primary-section="{{ BoolToString($loop->first) }}"
|
||||
{{--
|
||||
$loop->last doesn't work however, is always null...
|
||||
--}}
|
||||
data-primary-section="{{ BoolToString($loop->first) }}" {{-- $loop->last doesn't work however, is always null... --}}
|
||||
data-last-section="{{ BoolToString(array_values(array_slice($usedMealplanSections->fetchAll(), -1))[0]->id == $mealplanSection->id) }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
{{-- Default empty calendar/section when no single meal plan entry is in the given date range --}}
|
||||
@if($usedMealplanSections->count() === 0)
|
||||
<div class="row">
|
||||
{{-- Default empty calendar/section when no single meal plan entry is in the given date range --}}
|
||||
@if ($usedMealplanSections->count() === 0)
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="calendar"
|
||||
data-section-id="-1"
|
||||
data-section-name=""
|
||||
data-primary-section="true"
|
||||
<div class="calendar" data-section-id="-1" data-section-name="" data-primary-section="true"
|
||||
data-last-section="true">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="modal fade"
|
||||
id="add-recipe-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="add-recipe-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 id="add-recipe-modal-title"
|
||||
class="modal-title w-100"></h4>
|
||||
<h4 id="add-recipe-modal-title" class="modal-title w-100"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="add-recipe-form"
|
||||
novalidate>
|
||||
<form id="add-recipe-form" novalidate>
|
||||
|
||||
@include('components.recipepicker', array(
|
||||
@include('components.recipepicker', [
|
||||
'recipes' => $recipes,
|
||||
'isRequired' => true,
|
||||
'nextInputSelector' => '#recipe_servings'
|
||||
))
|
||||
'nextInputSelector' => '#recipe_servings',
|
||||
])
|
||||
|
||||
@include('components.numberpicker', array(
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'recipe_servings',
|
||||
'label' => 'Servings',
|
||||
'min' => $DEFAULT_MIN_AMOUNT,
|
||||
'decimals' => $userSettings['stock_decimal_places_amounts'],
|
||||
'value' => '1',
|
||||
'additionalCssClasses' => 'locale-number-input locale-number-quantity-amount'
|
||||
))
|
||||
'additionalCssClasses' => 'locale-number-input locale-number-quantity-amount',
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<label for="period_type">{{ $__t('Section') }}</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="section_id_recipe"
|
||||
name="section_id_recipe"
|
||||
{{-- TODO: Select2: dynamic data: meal_plan_sections --}}
|
||||
<select class="custom-control custom-select" id="section_id_recipe" name="section_id_recipe"
|
||||
required>
|
||||
@foreach($mealplanSections as $mealplanSection)
|
||||
@foreach ($mealplanSections as $mealplanSection)
|
||||
<option value="{{ $mealplanSection->id }}">{{ $mealplanSection->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<input type="hidden"
|
||||
id="day"
|
||||
name="day"
|
||||
value="">
|
||||
<input type="hidden"
|
||||
name="type"
|
||||
value="recipe">
|
||||
<input type="hidden" id="day" name="day" value="">
|
||||
<input type="hidden" name="type" value="recipe">
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="save-add-recipe-button"
|
||||
data-dismiss="modal"
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="save-add-recipe-button" data-dismiss="modal"
|
||||
class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="add-note-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="add-note-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 id="add-note-modal-title"
|
||||
class="modal-title w-100"></h4>
|
||||
<h4 id="add-note-modal-title" class="modal-title w-100"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="add-note-form"
|
||||
novalidate>
|
||||
<form id="add-note-form" novalidate>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="note">{{ $__t('Note') }}</label>
|
||||
<textarea class="form-control"
|
||||
rows="2"
|
||||
id="note"
|
||||
name="note"></textarea>
|
||||
<textarea class="form-control" rows="2" id="note" name="note"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="period_type">{{ $__t('Section') }}</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="section_id_note"
|
||||
name="section_id_note"
|
||||
{{-- TODO: Select2: dynamic data: meal_plan_sections --}}
|
||||
<select class="custom-control custom-select" id="section_id_note" name="section_id_note"
|
||||
required>
|
||||
@foreach($mealplanSections as $mealplanSection)
|
||||
@foreach ($mealplanSections as $mealplanSection)
|
||||
<option value="{{ $mealplanSection->id }}">{{ $mealplanSection->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<input type="hidden"
|
||||
name="type"
|
||||
value="note">
|
||||
<input type="hidden" name="type" value="note">
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="save-add-note-button"
|
||||
data-dismiss="modal"
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="save-add-note-button" data-dismiss="modal"
|
||||
class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="add-product-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="add-product-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 id="add-product-modal-title"
|
||||
class="modal-title w-100"></h4>
|
||||
<h4 id="add-product-modal-title" class="modal-title w-100"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="add-product-form"
|
||||
novalidate>
|
||||
<form id="add-product-form" novalidate>
|
||||
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
'nextInputSelector' => '#amount'
|
||||
))
|
||||
@include('components.productpicker', [
|
||||
'productsQuery' => 'order=name%3Acollate%20nocase',
|
||||
'nextInputSelector' => '#amount',
|
||||
])
|
||||
|
||||
@include('components.productamountpicker', array(
|
||||
@include('components.productamountpicker', [
|
||||
'value' => 1,
|
||||
'additionalGroupCssClasses' => 'mb-0'
|
||||
))
|
||||
'additionalGroupCssClasses' => 'mb-0',
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<label for="period_type">{{ $__t('Section') }}</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="section_id_product"
|
||||
name="section_id_product"
|
||||
{{-- TODO: Select2: dynamic data: meal_plan_sections --}}
|
||||
<select class="custom-control custom-select" id="section_id_product" name="section_id_product"
|
||||
required>
|
||||
@foreach($mealplanSections as $mealplanSection)
|
||||
@foreach ($mealplanSections as $mealplanSection)
|
||||
<option value="{{ $mealplanSection->id }}">{{ $mealplanSection->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<input type="hidden"
|
||||
name="type"
|
||||
value="product">
|
||||
<input type="hidden" name="type" value="product">
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="save-add-product-button"
|
||||
data-dismiss="modal"
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="save-add-product-button" data-dismiss="modal"
|
||||
class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="copy-day-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="copy-day-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 id="copy-day-modal-title"
|
||||
class="modal-title w-100"></h4>
|
||||
<h4 id="copy-day-modal-title" class="modal-title w-100"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="copy-day-form"
|
||||
novalidate>
|
||||
<form id="copy-day-form" novalidate>
|
||||
|
||||
@include('components.datetimepicker', array(
|
||||
@include('components.datetimepicker', [
|
||||
'id' => 'copy_to_date',
|
||||
'label' => 'Day',
|
||||
'format' => 'YYYY-MM-DD',
|
||||
|
|
@ -333,37 +286,30 @@
|
|||
'limitStartToNow' => false,
|
||||
'isRequired' => true,
|
||||
'additionalCssClasses' => 'date-only-datetimepicker',
|
||||
'invalidFeedback' => $__t('A date is required')
|
||||
))
|
||||
'invalidFeedback' => $__t('A date is required'),
|
||||
])
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="save-copy-day-button"
|
||||
data-dismiss="modal"
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="save-copy-day-button" data-dismiss="modal"
|
||||
class="btn btn-primary">{{ $__t('Copy') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="mealplan-productcard-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="mealplan-productcard-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content text-center">
|
||||
<div class="modal-body">
|
||||
@include('components.productcard')
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,26 +5,21 @@
|
|||
@section('viewJsName', 'mealplansections')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button m-1 mt-md-0 mb-md-0 float-right show-as-dialog-link"
|
||||
href="{{ $U('/mealplansection/new?embedded') }}">
|
||||
{{ $__t('Add') }}
|
||||
|
|
@ -32,46 +27,37 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="mealplansections-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: meal_plan_sections --}}
|
||||
<table id="mealplansections-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#mealplansections-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#mealplansections-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th>{{ $__t('Sort number') }}</th>
|
||||
|
|
@ -79,20 +65,17 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($mealplanSections as $mealplanSection)
|
||||
@foreach ($mealplanSections as $mealplanSection)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm show-as-dialog-link"
|
||||
href="{{ $U('/mealplansection/') }}{{ $mealplanSection->id }}?embedded"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
data-toggle="tooltip" title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm mealplansection-delete-button"
|
||||
href="#"
|
||||
<a class="btn btn-danger btn-sm mealplansection-delete-button" href="#"
|
||||
data-mealplansection-id="{{ $mealplanSection->id }}"
|
||||
data-mealplansection-name="{{ $mealplanSection->name }}"
|
||||
data-toggle="tooltip"
|
||||
data-mealplansection-name="{{ $mealplanSection->name }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
|
|
@ -111,5 +94,5 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -1,33 +1,34 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@if($mode == 'edit')
|
||||
@section('title', $__t('Edit Barcode'))
|
||||
@if ($mode == 'edit')
|
||||
@section('title', $__t('Edit Barcode'))
|
||||
@else
|
||||
@section('title', $__t('Create Barcode'))
|
||||
@section('title', $__t('Create Barcode'))
|
||||
@endif
|
||||
|
||||
@section('viewJsName', 'productbarcodeform')
|
||||
|
||||
@section('content')
|
||||
<script>
|
||||
<script>
|
||||
Grocy.QuantityUnits = {!! json_encode($quantityUnits) !!};
|
||||
Grocy.QuantityUnitConversionsResolved = {!! json_encode($quantityUnitConversionsResolved) !!};
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<h2>
|
||||
<span class="text-muted small">{{ $__t('Barcode for product') }} <strong>{{ $product->name }}</strong></span>
|
||||
<span class="text-muted small">{{ $__t('Barcode for product') }}
|
||||
<strong>{{ $product->name }}</strong></span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-12">
|
||||
|
||||
<script>
|
||||
|
|
@ -35,79 +36,69 @@
|
|||
Grocy.EditObjectProduct = {!! json_encode($product) !!};
|
||||
</script>
|
||||
|
||||
@if($mode == 'edit')
|
||||
@if ($mode == 'edit')
|
||||
<script>
|
||||
Grocy.EditObjectId = {{ $barcode->id }};
|
||||
Grocy.EditObject = {!! json_encode($barcode) !!};
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<form id="barcode-form"
|
||||
novalidate>
|
||||
<form id="barcode-form" novalidate>
|
||||
|
||||
<input type="hidden"
|
||||
name="product_id"
|
||||
value="{{ $product->id }}">
|
||||
<input type="hidden" name="product_id" value="{{ $product->id }}">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">{{ $__t('Barcode') }} <i class="fas fa-barcode"></i></label>
|
||||
<div class="input-group">
|
||||
<input type="text"
|
||||
class="form-control barcodescanner-input"
|
||||
required
|
||||
id="barcode"
|
||||
name="barcode"
|
||||
value="@if($mode == 'edit'){{ $barcode->barcode }}@endif"
|
||||
<input type="text" class="form-control barcodescanner-input" required id="barcode" name="barcode"
|
||||
value="@if ($mode == 'edit') {{ $barcode->barcode }} @endif"
|
||||
data-target="#barcode">
|
||||
@include('components.barcodescanner')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@php if($mode == 'edit') { $value = $barcode->amount; } else { $value = ''; } @endphp
|
||||
@include('components.productamountpicker', array(
|
||||
@php
|
||||
if ($mode == 'edit') {
|
||||
$value = $barcode->amount;
|
||||
} else {
|
||||
$value = '';
|
||||
}
|
||||
@endphp
|
||||
@include('components.productamountpicker', [
|
||||
'value' => $value,
|
||||
'isRequired' => false
|
||||
))
|
||||
'isRequired' => false,
|
||||
])
|
||||
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
<div class="form-group">
|
||||
<label for="shopping_location_id_id">{{ $__t('Store') }}</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="shopping_location_id"
|
||||
name="shopping_location_id">
|
||||
{{-- TODO: Select2: dynamic data: shopping_locations --}}
|
||||
<select class="custom-control custom-select" id="shopping_location_id" name="shopping_location_id">
|
||||
<option></option>
|
||||
@foreach($shoppinglocations as $store)
|
||||
<option @if($mode=='edit'
|
||||
&&
|
||||
$store->id == $barcode->shopping_location_id) selected="selected" @endif value="{{ $store->id }}">{{ $store->name }}</option>
|
||||
@foreach ($shoppinglocations as $store)
|
||||
<option @if ($mode == 'edit' && $store->id == $barcode->shopping_location_id) selected="selected" @endif
|
||||
value="{{ $store->id }}">{{ $store->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@else
|
||||
<input type="hidden"
|
||||
name="shopping_location_id"
|
||||
id="shopping_location_id"
|
||||
value="1">
|
||||
<input type="hidden" name="shopping_location_id" id="shopping_location_id" value="1">
|
||||
@endif
|
||||
|
||||
<div class="form-group">
|
||||
<label for="note">{{ $__t('Note') }}</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="note"
|
||||
name="note"
|
||||
value="@if($mode == 'edit'){{ $barcode->note }}@endif">
|
||||
<input type="text" class="form-control" id="note" name="note"
|
||||
value="@if ($mode == 'edit') {{ $barcode->note }} @endif">
|
||||
</div>
|
||||
|
||||
@include('components.userfieldsform', array(
|
||||
@include('components.userfieldsform', [
|
||||
'userfields' => $userfields,
|
||||
'entity' => 'product_barcodes'
|
||||
))
|
||||
'entity' => 'product_barcodes',
|
||||
])
|
||||
|
||||
<button id="save-barcode-button"
|
||||
class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
<button id="save-barcode-button" class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,26 +5,21 @@
|
|||
@section('viewJsName', 'productgroups')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button show-as-dialog-link m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/productgroup/new?embedded') }}">
|
||||
{{ $__t('Add') }}
|
||||
|
|
@ -36,71 +31,59 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="productgroups-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: product_groups --}}
|
||||
<table id="productgroups-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#productgroups-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#productgroups-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th>{{ $__t('Description') }}</th>
|
||||
<th>{{ $__t('Product count') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($productGroups as $productGroup)
|
||||
@foreach ($productGroups as $productGroup)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm show-as-dialog-link"
|
||||
href="{{ $U('/productgroup/') }}{{ $productGroup->id }}?embedded"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
data-toggle="tooltip" title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm product-group-delete-button"
|
||||
href="#"
|
||||
<a class="btn btn-danger btn-sm product-group-delete-button" href="#"
|
||||
data-group-id="{{ $productGroup->id }}"
|
||||
data-group-name="{{ $productGroup->name }}"
|
||||
data-toggle="tooltip"
|
||||
data-group-name="{{ $productGroup->name }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
|
|
@ -119,15 +102,19 @@
|
|||
</a>
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $productGroup->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$productGroup->id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,26 +5,21 @@
|
|||
@section('viewJsName', 'products')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/product/new') }}">
|
||||
{{ $__t('Add') }}
|
||||
|
|
@ -40,21 +35,17 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex mb-3" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
|
|
@ -62,10 +53,10 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Product group') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="product-group-filter">
|
||||
{{-- TODO: Select2: dynamic data: product_groups --}}
|
||||
<select class="custom-control custom-select" id="product-group-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($productGroups as $productGroup)
|
||||
@foreach ($productGroups as $productGroup)
|
||||
<option value="{{ $productGroup->id }}">{{ $productGroup->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -73,155 +64,58 @@
|
|||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-2">
|
||||
<div class="form-check custom-control custom-checkbox">
|
||||
<input class="form-check-input custom-control-input"
|
||||
type="checkbox"
|
||||
id="show-disabled">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="show-disabled">
|
||||
<input class="form-check-input custom-control-input" type="checkbox" id="show-disabled">
|
||||
<label class="form-check-label custom-control-label" for="show-disabled">
|
||||
{{ $__t('Show disabled') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-2">
|
||||
<div class="form-check custom-control custom-checkbox">
|
||||
<input class="form-check-input custom-control-input"
|
||||
type="checkbox"
|
||||
id="show-only-in-stock">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="show-only-in-stock">
|
||||
<input class="form-check-input custom-control-input" type="checkbox" id="show-only-in-stock">
|
||||
<label class="form-check-label custom-control-label" for="show-only-in-stock">
|
||||
{{ $__t('Show only in-stock products') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="products-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
<table id="products-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#products-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#products-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif allow-grouping">{{ $__t('Location') }}</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif allow-grouping">{{ $__t('Location') }}
|
||||
</th>
|
||||
<th class="allow-grouping">{{ $__t('Min. stock amount') }}</th>
|
||||
<th class="">{{ $__t('Default quantity unit purchase') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Quantity unit stock') }}</th>
|
||||
<th class="">{{ $__t('Product group') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif allow-grouping">{{ $__t('Default store') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif allow-grouping">
|
||||
{{ $__t('Default store') }}</th>
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($products as $product)
|
||||
<tr class="@if($product->active == 0) text-muted @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm"
|
||||
href="{{ $U('/product/') }}{{ $product->id }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm product-delete-button"
|
||||
href="#"
|
||||
data-product-id="{{ $product->id }}"
|
||||
data-product-name="{{ $product->name }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="table-inline-menu dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
href="{{ $U('/product/new?copy-of=') }}{{ $product->id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Copy') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item merge-products-button"
|
||||
data-product-id="{{ $product->id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
<span class="dropdown-item-text">{{ $__t('Merge') }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ $product->name }}
|
||||
@if(!empty($product->picture_file_name))
|
||||
<i class="fas fa-image text-muted"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('This product has a picture') }}"></i>
|
||||
@endif
|
||||
</td>
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif">
|
||||
@php
|
||||
$location = FindObjectInArrayByPropertyValue($locations, 'id', $product->location_id);
|
||||
@endphp
|
||||
@if($location != null)
|
||||
{{ $location->name }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $product->min_stock_amount }}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_purchase)->name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_stock)->name }}
|
||||
</td>
|
||||
<td>
|
||||
@if(!empty($product->product_group_id)) {{ FindObjectInArrayByPropertyValue($productGroups, 'id', $product->product_group_id)->name }} @endif
|
||||
</td>
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">
|
||||
@php
|
||||
$store = FindObjectInArrayByPropertyValue($shoppingLocations, 'id', $product->shopping_location_id);
|
||||
@endphp
|
||||
@if($store != null)
|
||||
{{ $store->name }}
|
||||
@endif
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $product->id)
|
||||
))
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="merge-products-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="merge-products-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content text-center">
|
||||
<div class="modal-header">
|
||||
|
|
@ -229,44 +123,30 @@
|
|||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="merge-products-keep">{{ $__t('Product to keep') }} <i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
<label for="merge-products-keep">{{ $__t('Product to keep') }} <i
|
||||
class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('After merging, this product will be kept') }}"></i>
|
||||
</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="merge-products-keep">
|
||||
<option></option>
|
||||
@foreach($products as $product)
|
||||
<option value="{{ $product->id }}">{{ $product->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<select class="select2 custom-control custom-select" id="merge-products-keep"></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="merge-products-remove">{{ $__t('Product to remove') }} <i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
<label for="merge-products-remove">{{ $__t('Product to remove') }} <i
|
||||
class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('After merging, all occurences of this product will be replaced by "Product to keep" (means this product will not exist anymore)') }}"></i>
|
||||
</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="merge-products-remove">
|
||||
<option></option>
|
||||
@foreach($products as $product)
|
||||
<option value="{{ $product->id }}">{{ $product->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<select class="select2 custom-control custom-select" id="merge-products-remove"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="merge-products-save-button"
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="merge-products-save-button" type="button" class="btn btn-primary"
|
||||
data-dismiss="modal">{{ $__t('OK') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var userfields = {!! json_encode($userfields) !!};
|
||||
</script>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -53,8 +53,7 @@
|
|||
novalidate>
|
||||
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
'barcodes' => $barcodes,
|
||||
'productsQuery' => 'query%5B%5D=active%3D1&order=name%3Acollate%20nocase',
|
||||
'nextInputSelector' => '#display_amount'
|
||||
))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,65 +1,61 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@if($mode == 'edit')
|
||||
@section('title', $__t('Edit QU conversion'))
|
||||
@if ($mode == 'edit')
|
||||
@section('title', $__t('Edit QU conversion'))
|
||||
@else
|
||||
@section('title', $__t('Create QU conversion'))
|
||||
@section('title', $__t('Create QU conversion'))
|
||||
@endif
|
||||
|
||||
@section('viewJsName', 'quantityunitconversionform')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<h2>
|
||||
@if($product != null)
|
||||
<span class="text-muted small">{{ $__t('Override for product') }} <strong>{{ $product->name }}</strong></span>
|
||||
@if ($product != null)
|
||||
<span class="text-muted small">{{ $__t('Override for product') }}
|
||||
<strong>{{ $product->name }}</strong></span>
|
||||
@else
|
||||
<span class="text-muted small">{{ $__t('Default for QU') }} <strong>{{ $defaultQuUnit->name }}</strong></span>
|
||||
<span class="text-muted small">{{ $__t('Default for QU') }}
|
||||
<strong>{{ $defaultQuUnit->name }}</strong></span>
|
||||
@endif
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-12">
|
||||
|
||||
<script>
|
||||
Grocy.EditMode = '{{ $mode }}';
|
||||
</script>
|
||||
|
||||
@if($mode == 'edit')
|
||||
@if ($mode == 'edit')
|
||||
<script>
|
||||
Grocy.EditObjectId = {{ $quConversion->id }};
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<form id="quconversion-form"
|
||||
novalidate>
|
||||
<form id="quconversion-form" novalidate>
|
||||
|
||||
@if($product != null)
|
||||
<input type="hidden"
|
||||
name="product_id"
|
||||
value="{{ $product->id }}">
|
||||
@if ($product != null)
|
||||
<input type="hidden" name="product_id" value="{{ $product->id }}">
|
||||
@endif
|
||||
|
||||
<div class="form-group">
|
||||
<label for="from_qu_id">{{ $__t('Quantity unit from') }}</label>
|
||||
<select required
|
||||
class="custom-control custom-select input-group-qu"
|
||||
id="from_qu_id"
|
||||
name="from_qu_id">
|
||||
{{-- TODO: Select2: dynamic data: quantity_units --}}
|
||||
<select required class="custom-control custom-select input-group-qu" id="from_qu_id" name="from_qu_id">
|
||||
<option></option>
|
||||
@foreach($quantityunits as $quantityunit)
|
||||
<option @if(($product
|
||||
!=null
|
||||
&&
|
||||
$quantityunit->id == $product->qu_id_stock) || ($defaultQuUnit != null && $quantityunit->id == $defaultQuUnit->id))) selected="selected" @endif value="{{ $quantityunit->id }}" data-plural-form="{{ $quantityunit->name_plural }}">{{ $quantityunit->name }}</option>
|
||||
@foreach ($quantityunits as $quantityunit)
|
||||
<option @if (($product != null && $quantityunit->id == $product->qu_id_stock) || ($defaultQuUnit != null && $quantityunit->id == $defaultQuUnit->id)) ) selected="selected" @endif
|
||||
value="{{ $quantityunit->id }}" data-plural-form="{{ $quantityunit->name_plural }}">
|
||||
{{ $quantityunit->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
|
||||
|
|
@ -67,22 +63,26 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label for="to_qu_id">{{ $__t('Quantity unit to') }}</label>
|
||||
<select required
|
||||
class="custom-control custom-select input-group-qu"
|
||||
id="to_qu_id"
|
||||
name="to_qu_id">
|
||||
{{-- TODO: Select2: dynamic data: quantity_units --}}
|
||||
<select required class="custom-control custom-select input-group-qu" id="to_qu_id" name="to_qu_id">
|
||||
<option></option>
|
||||
@foreach($quantityunits as $quantityunit)
|
||||
<option @if($mode=='edit'
|
||||
&&
|
||||
$quantityunit->id == $quConversion->to_qu_id) selected="selected" @endif value="{{ $quantityunit->id }}" data-plural-form="{{ $quantityunit->name_plural }}">{{ $quantityunit->name }}</option>
|
||||
@foreach ($quantityunits as $quantityunit)
|
||||
<option @if ($mode == 'edit' && $quantityunit->id == $quConversion->to_qu_id) selected="selected" @endif
|
||||
value="{{ $quantityunit->id }}" data-plural-form="{{ $quantityunit->name_plural }}">
|
||||
{{ $quantityunit->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
|
||||
</div>
|
||||
|
||||
@php if($mode == 'edit') { $value = $quConversion->factor; } else { $value = 1; } @endphp
|
||||
@include('components.numberpicker', array(
|
||||
@php
|
||||
if ($mode == 'edit') {
|
||||
$value = $quConversion->factor;
|
||||
} else {
|
||||
$value = 1;
|
||||
}
|
||||
@endphp
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'factor',
|
||||
'label' => 'Factor',
|
||||
'min' => $DEFAULT_MIN_AMOUNT,
|
||||
|
|
@ -90,33 +90,27 @@
|
|||
'value' => $value,
|
||||
'additionalHtmlElements' => '<p id="qu-conversion-info"
|
||||
class="form-text text-info d-none"></p>',
|
||||
'additionalCssClasses' => 'input-group-qu locale-number-input locale-number-quantity-amount'
|
||||
))
|
||||
'additionalCssClasses' => 'input-group-qu locale-number-input locale-number-quantity-amount',
|
||||
])
|
||||
|
||||
<div class="form-group @if($mode == 'edit') d-none @endif">
|
||||
<div class="form-group @if ($mode == 'edit') d-none @endif">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input checked
|
||||
class="form-check-input custom-control-input"
|
||||
type="checkbox"
|
||||
id="create_inverse"
|
||||
name="create_inverse:skip"
|
||||
value="1">
|
||||
<input checked class="form-check-input custom-control-input" type="checkbox" id="create_inverse"
|
||||
name="create_inverse:skip" value="1">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="create_inverse">{{ $__t('Create inverse QU conversion') }}</label>
|
||||
</div>
|
||||
<span id="qu-conversion-inverse-info"
|
||||
class="form-text text-info d-none"></span>
|
||||
<span id="qu-conversion-inverse-info" class="form-text text-info d-none"></span>
|
||||
</div>
|
||||
|
||||
@include('components.userfieldsform', array(
|
||||
@include('components.userfieldsform', [
|
||||
'userfields' => $userfields,
|
||||
'entity' => 'quantity_unit_conversions'
|
||||
))
|
||||
'entity' => 'quantity_unit_conversions',
|
||||
])
|
||||
|
||||
<button id="save-quconversion-button"
|
||||
class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
<button id="save-quconversion-button" class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -1,58 +1,52 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@if($mode == 'edit')
|
||||
@section('title', $__t('Edit quantity unit'))
|
||||
@if ($mode == 'edit')
|
||||
@section('title', $__t('Edit quantity unit'))
|
||||
@else
|
||||
@section('title', $__t('Create quantity unit'))
|
||||
@section('title', $__t('Create quantity unit'))
|
||||
@endif
|
||||
|
||||
@section('viewJsName', 'quantityunitform')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-12">
|
||||
<script>
|
||||
Grocy.EditMode = '{{ $mode }}';
|
||||
</script>
|
||||
|
||||
@if($mode == 'edit')
|
||||
@if ($mode == 'edit')
|
||||
<script>
|
||||
Grocy.EditObjectId = {{ $quantityUnit->id }};
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<form id="quantityunit-form"
|
||||
novalidate>
|
||||
<form id="quantityunit-form" novalidate>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">{{ $__t('Name') }} <span class="small text-muted">{{ $__t('in singular form') }}</span></label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
required
|
||||
id="name"
|
||||
name="name"
|
||||
value="@if($mode == 'edit'){{ $quantityUnit->name }}@endif">
|
||||
<label for="name">{{ $__t('Name') }} <span
|
||||
class="small text-muted">{{ $__t('in singular form') }}</span></label>
|
||||
<input type="text" class="form-control" required id="name" name="name"
|
||||
value="@if ($mode == 'edit') {{ $quantityUnit->name }} @endif">
|
||||
<div class="invalid-feedback">{{ $__t('A name is required') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name_plural">{{ $__t('Name') }} <span class="small text-muted">{{ $__t('in plural form') }}</span></label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="name_plural"
|
||||
name="name_plural"
|
||||
value="@if($mode == 'edit'){{ $quantityUnit->name_plural }}@endif">
|
||||
<label for="name_plural">{{ $__t('Name') }} <span
|
||||
class="small text-muted">{{ $__t('in plural form') }}</span></label>
|
||||
<input type="text" class="form-control" id="name_plural" name="name_plural"
|
||||
value="@if ($mode == 'edit') {{ $quantityUnit->name_plural }} @endif">
|
||||
</div>
|
||||
|
||||
@if($pluralCount > 2)
|
||||
@if ($pluralCount > 2)
|
||||
<div class="form-group">
|
||||
<label for="plural_forms">
|
||||
{{ $__t('Plural forms') }}<br>
|
||||
|
|
@ -62,34 +56,37 @@
|
|||
{{ $__t('Plural rule') }}: {{ $pluralRule }}
|
||||
</span>
|
||||
</label>
|
||||
<textarea class="form-control"
|
||||
rows="3"
|
||||
id="plural_forms"
|
||||
name="plural_forms">@if($mode == 'edit'){{ $quantityUnit->plural_forms }}@endif</textarea>
|
||||
<textarea class="form-control" rows="3" id="plural_forms" name="plural_forms">
|
||||
@if ($mode == 'edit')
|
||||
{{ $quantityUnit->plural_forms }}
|
||||
@endif
|
||||
</textarea>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">{{ $__t('Description') }}</label>
|
||||
<textarea class="form-control"
|
||||
rows="2"
|
||||
id="description"
|
||||
name="description">@if($mode == 'edit'){{ $quantityUnit->description }}@endif</textarea>
|
||||
<textarea class="form-control" rows="2" id="description" name="description">
|
||||
@if ($mode == 'edit')
|
||||
{{ $quantityUnit->description }}
|
||||
@endif
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
@include('components.userfieldsform', array(
|
||||
@include('components.userfieldsform', [
|
||||
'userfields' => $userfields,
|
||||
'entity' => 'quantity_units'
|
||||
))
|
||||
'entity' => 'quantity_units',
|
||||
])
|
||||
|
||||
<small class="my-2 form-text text-muted @if($mode == 'edit') d-none @endif">{{ $__t('Save & continue to add conversions') }}</small>
|
||||
<small
|
||||
class="my-2 form-text text-muted @if ($mode == 'edit') d-none @endif">{{ $__t('Save & continue to add conversions') }}</small>
|
||||
|
||||
<button class="save-quantityunit-button btn btn-success mb-2"
|
||||
data-location="continue">{{ $__t('Save & continue') }}</button>
|
||||
<button class="save-quantityunit-button btn btn-info mb-2"
|
||||
data-location="return">{{ $__t('Save & return to quantity units') }}</button>
|
||||
|
||||
@if(intval($pluralCount) > 2)
|
||||
@if (intval($pluralCount) > 2)
|
||||
<button id="test-quantityunit-plural-forms-button"
|
||||
class="btn btn-secondary">{{ $__t('Test plural forms') }}</button>
|
||||
@endif
|
||||
|
|
@ -97,63 +94,57 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6 col-12 @if($mode == 'create') d-none @endif">
|
||||
<div class="col-lg-6 col-12 @if ($mode == 'create') d-none @endif">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h4>
|
||||
{{ $__t('Default conversions') }}
|
||||
<small id="qu-conversion-headline-info"
|
||||
class="text-muted font-italic"></small>
|
||||
<small id="qu-conversion-headline-info" class="text-muted font-italic"></small>
|
||||
</h4>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-outline-primary btn-sm m-1 mt-md-0 mb-md-0 float-right show-as-dialog-link"
|
||||
href="{{ $U('/quantityunitconversion/new?embedded&qu-unit=' . $quantityUnit->id ) }}">
|
||||
href="{{ $U('/quantityunitconversion/new?embedded&qu-unit=' . $quantityUnit->id) }}">
|
||||
{{ $__t('Add') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id="qu-conversions-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: quantity_unit_conversions --}}
|
||||
<table id="qu-conversions-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#qu-conversions-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#qu-conversions-table" href="#"><i
|
||||
class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Factor') }}</th>
|
||||
<th>{{ $__t('Unit') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@if($mode == "edit")
|
||||
@foreach($defaultQuConversions as $defaultQuConversion)
|
||||
@if ($mode == 'edit')
|
||||
@foreach ($defaultQuConversions as $defaultQuConversion)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-sm btn-info show-as-dialog-link"
|
||||
href="{{ $U('/quantityunitconversion/' . $defaultQuConversion->id . '?embedded&qu-unit=' . $quantityUnit->id ) }}"
|
||||
href="{{ $U('/quantityunitconversion/' . $defaultQuConversion->id . '?embedded&qu-unit=' . $quantityUnit->id) }}"
|
||||
data-qu-conversion-id="{{ $defaultQuConversion->id }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-danger qu-conversion-delete-button"
|
||||
href="#"
|
||||
<a class="btn btn-sm btn-danger qu-conversion-delete-button" href="#"
|
||||
data-qu-conversion-id="{{ $defaultQuConversion->id }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $defaultQuConversion->factor }}</span>
|
||||
<span
|
||||
class="locale-number locale-number-quantity-amount">{{ $defaultQuConversion->factor }}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ FindObjectInArrayByPropertyValue($quantityUnits, 'id', $defaultQuConversion->to_qu_id)->name }}
|
||||
|
|
@ -166,5 +157,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,51 +5,47 @@
|
|||
@section('viewJsName', 'quantityunitpluraltesting')
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}" rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-12">
|
||||
<form id="quantityunitpluraltesting-form"
|
||||
novalidate>
|
||||
<form id="quantityunitpluraltesting-form" novalidate>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="qu_id">{{ $__t('Quantity unit') }}</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="qu_id"
|
||||
name="qu_id">
|
||||
{{-- TODO: Select2: dynamic data: quantity_units --}}
|
||||
<select class="custom-control custom-select" id="qu_id" name="qu_id">
|
||||
<option></option>
|
||||
@foreach($quantityUnits as $quantityUnit)
|
||||
<option value="{{ $quantityUnit->id }}"
|
||||
data-singular-form="{{ $quantityUnit->name }}"
|
||||
@foreach ($quantityUnits as $quantityUnit)
|
||||
<option value="{{ $quantityUnit->id }}" data-singular-form="{{ $quantityUnit->name }}"
|
||||
data-plural-form="{{ $quantityUnit->name_plural }}">{{ $quantityUnit->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@include('components.numberpicker', array(
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'amount',
|
||||
'label' => 'Amount',
|
||||
'min' => 0,
|
||||
'decimals' => $userSettings['stock_decimal_places_amounts'],
|
||||
'isRequired' => false,
|
||||
'value' => 1,
|
||||
'additionalCssClasses' => 'locale-number-input locale-number-quantity-amount'
|
||||
))
|
||||
'additionalCssClasses' => 'locale-number-input locale-number-quantity-amount',
|
||||
])
|
||||
|
||||
</form>
|
||||
|
||||
<h2><strong>{{ $__t('Result') }}:</strong> <span id="result"></span></h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,26 +5,21 @@
|
|||
@section('viewJsName', 'quantityunits')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/quantityunit/new') }}">
|
||||
{{ $__t('Add') }}
|
||||
|
|
@ -36,70 +31,57 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="quantityunits-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: quantity_units --}}
|
||||
<table id="quantityunits-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#quantityunits-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#quantityunits-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th>{{ $__t('Description') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($quantityunits as $quantityunit)
|
||||
@foreach ($quantityunits as $quantityunit)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm"
|
||||
href="{{ $U('/quantityunit/') }}{{ $quantityunit->id }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<a class="btn btn-info btn-sm" href="{{ $U('/quantityunit/') }}{{ $quantityunit->id }}"
|
||||
data-toggle="tooltip" title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm quantityunit-delete-button"
|
||||
href="#"
|
||||
<a class="btn btn-danger btn-sm quantityunit-delete-button" href="#"
|
||||
data-quantityunit-id="{{ $quantityunit->id }}"
|
||||
data-quantityunit-name="{{ $quantityunit->name }}"
|
||||
data-toggle="tooltip"
|
||||
data-quantityunit-name="{{ $quantityunit->name }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
|
|
@ -111,15 +93,19 @@
|
|||
{{ $quantityunit->description }}
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $quantityunit->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$quantityunit->id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@if($mode == 'edit')
|
||||
@section('title', $__t('Edit recipe'))
|
||||
@if ($mode == 'edit')
|
||||
@section('title', $__t('Edit recipe'))
|
||||
@else
|
||||
@section('title', $__t('Create recipe'))
|
||||
@section('title', $__t('Create recipe'))
|
||||
@endif
|
||||
|
||||
@section('viewJsName', 'recipeform')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
|
||||
|
|
@ -19,87 +19,90 @@
|
|||
Grocy.QuantityUnitConversionsResolved = {!! json_encode($quantityUnitConversionsResolved) !!};
|
||||
</script>
|
||||
|
||||
@if($mode == 'edit')
|
||||
@if ($mode == 'edit')
|
||||
<script>
|
||||
Grocy.EditObjectId = {{ $recipe->id }};
|
||||
</script>
|
||||
|
||||
@if(!empty($recipe->picture_file_name))
|
||||
@if (!empty($recipe->picture_file_name))
|
||||
<script>
|
||||
Grocy.RecipePictureFileName = '{{ $recipe->picture_file_name }}';
|
||||
</script>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-7 pb-3">
|
||||
<form id="recipe-form"
|
||||
novalidate>
|
||||
<form id="recipe-form" novalidate>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">{{ $__t('Name') }}</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
required
|
||||
id="name"
|
||||
name="name"
|
||||
value="@if($mode == 'edit'){{ $recipe->name }}@endif">
|
||||
<input type="text" class="form-control" required id="name" name="name"
|
||||
value="@if ($mode == 'edit') {{ $recipe->name }} @endif">
|
||||
<div class="invalid-feedback">{{ $__t('A name is required') }}</div>
|
||||
</div>
|
||||
|
||||
@php if($mode == 'edit') { $value = $recipe->base_servings; } else { $value = 1; } @endphp
|
||||
@include('components.numberpicker', array(
|
||||
@php
|
||||
if ($mode == 'edit') {
|
||||
$value = $recipe->base_servings;
|
||||
} else {
|
||||
$value = 1;
|
||||
}
|
||||
@endphp
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'base_servings',
|
||||
'label' => 'Servings',
|
||||
'min' => $DEFAULT_MIN_AMOUNT,
|
||||
'decimals' => $userSettings['stock_decimal_places_amounts'],
|
||||
'value' => $value,
|
||||
'hint' => $__t('The ingredients listed here result in this amount of servings'),
|
||||
'additionalCssClasses' => 'locale-number-input locale-number-quantity-amount'
|
||||
))
|
||||
'additionalCssClasses' => 'locale-number-input locale-number-quantity-amount',
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input @if($mode=='edit'
|
||||
&&
|
||||
$recipe->not_check_shoppinglist == 1) checked @endif class="form-check-input custom-control-input" type="checkbox" id="not_check_shoppinglist" name="not_check_shoppinglist" value="1">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="not_check_shoppinglist">
|
||||
<input @if ($mode == 'edit' && $recipe->not_check_shoppinglist == 1) checked @endif
|
||||
class="form-check-input custom-control-input" type="checkbox" id="not_check_shoppinglist"
|
||||
name="not_check_shoppinglist" value="1">
|
||||
<label class="form-check-label custom-control-label" for="not_check_shoppinglist">
|
||||
{{ $__t('Do not check against the shopping list when adding missing items to it') }}
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('By default the amount to be added to the shopping list is "needed amount - stock amount - shopping list amount" - when this is enabled, it is only checked against the stock amount, not against what is already on the shopping list') }}"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
@include('components.productpicker', [
|
||||
'productsQuery' => 'order=name%3Acollate%20nocase',
|
||||
'isRequired' => false,
|
||||
'label' => 'Produces product',
|
||||
'prefillById' => $mode == 'edit' ? $recipe->product_id : '',
|
||||
'hint' => $__t('When a product is selected, one unit (per serving in stock quantity unit) will be added to stock on consuming this recipe'),
|
||||
'hint' => $__t(
|
||||
'When a product is selected, one unit (per serving in stock quantity unit) will be added to stock on consuming this recipe'
|
||||
),
|
||||
'disallowAllProductWorkflows' => true,
|
||||
))
|
||||
])
|
||||
|
||||
@include('components.userfieldsform', array(
|
||||
@include('components.userfieldsform', [
|
||||
'userfields' => $userfields,
|
||||
'entity' => 'recipes'
|
||||
))
|
||||
'entity' => 'recipes',
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">{{ $__t('Preparation') }}</label>
|
||||
<textarea id="description"
|
||||
class="form-control wysiwyg-editor"
|
||||
name="description">@if($mode == 'edit'){{ $recipe->description }}@endif</textarea>
|
||||
<textarea id="description" class="form-control wysiwyg-editor" name="description">
|
||||
@if ($mode == 'edit')
|
||||
{{ $recipe->description }}
|
||||
@endif
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<small class="my-2 form-text text-muted @if($mode == 'edit') d-none @endif">{{ $__t('Save & continue to add ingredients and included recipes') }}</small>
|
||||
<small
|
||||
class="my-2 form-text text-muted @if ($mode == 'edit') d-none @endif">{{ $__t('Save & continue to add ingredients and included recipes') }}</small>
|
||||
|
||||
<button class="save-recipe btn btn-success mb-2"
|
||||
data-location="continue">{{ $__t('Save & continue') }}</button>
|
||||
|
|
@ -109,40 +112,33 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-5 pb-3 @if($mode == 'create') d-none @endif">
|
||||
<div class="col-12 col-md-5 pb-3 @if ($mode == 'create') d-none @endif">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h4>
|
||||
{{ $__t('Ingredients list') }}
|
||||
</h4>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a id="recipe-pos-add-button"
|
||||
class="btn btn-outline-primary btn-sm recipe-pos-add-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
type="button"
|
||||
href="#">
|
||||
type="button" href="#">
|
||||
{{ $__t('Add') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id="recipes-pos-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: recipes_pos --}}
|
||||
<table id="recipes-pos-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#recipes-pos-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#recipes-pos-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Product') }}</th>
|
||||
<th>{{ $__t('Amount') }}</th>
|
||||
|
|
@ -151,19 +147,16 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@if($mode == "edit")
|
||||
@foreach($recipePositions as $recipePosition)
|
||||
@if ($mode == 'edit')
|
||||
@foreach ($recipePositions as $recipePosition)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-sm btn-info recipe-pos-edit-button"
|
||||
type="button"
|
||||
href="#"
|
||||
<a class="btn btn-sm btn-info recipe-pos-edit-button" type="button" href="#"
|
||||
data-recipe-pos-id="{{ $recipePosition->id }}"
|
||||
data-product-id="{{ $recipePosition->product_id }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-danger recipe-pos-delete-button"
|
||||
href="#"
|
||||
<a class="btn btn-sm btn-danger recipe-pos-delete-button" href="#"
|
||||
data-recipe-pos-id="{{ $recipePosition->id }}"
|
||||
data-recipe-pos-name="{{ FindObjectInArrayByPropertyValue($products, 'id', $recipePosition->product_id)->name }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
|
|
@ -178,8 +171,7 @@
|
|||
// but some users decide to edit the database manually and
|
||||
// enter something like "4 or 5" in the amount column (brilliant)
|
||||
// => So at least don't crash this view by just assuming 0 if that's the case
|
||||
if (!is_numeric($recipePosition->amount))
|
||||
{
|
||||
if (!is_numeric($recipePosition->amount)) {
|
||||
$recipePosition->amount = 0;
|
||||
}
|
||||
|
||||
|
|
@ -187,27 +179,29 @@
|
|||
$productQuConversions = FindAllObjectsInArrayByPropertyValue($quantityUnitConversionsResolved, 'product_id', $product->id);
|
||||
$productQuConversions = FindAllObjectsInArrayByPropertyValue($productQuConversions, 'from_qu_id', $product->qu_id_stock);
|
||||
$productQuConversion = FindObjectInArrayByPropertyValue($productQuConversions, 'to_qu_id', $recipePosition->qu_id);
|
||||
if ($productQuConversion && $recipePosition->only_check_single_unit_in_stock == 0)
|
||||
{
|
||||
if ($productQuConversion && $recipePosition->only_check_single_unit_in_stock == 0) {
|
||||
$recipePosition->amount = $recipePosition->amount * $productQuConversion->factor;
|
||||
}
|
||||
@endphp
|
||||
@if(!empty($recipePosition->variable_amount))
|
||||
@if (!empty($recipePosition->variable_amount))
|
||||
{{ $recipePosition->variable_amount }}
|
||||
@else
|
||||
<span class="locale-number locale-number-quantity-amount">@if($recipePosition->amount == round($recipePosition->amount)){{ round($recipePosition->amount) }}@else{{ $recipePosition->amount }}@endif</span>
|
||||
<span class="locale-number locale-number-quantity-amount">
|
||||
@if ($recipePosition->amount == round($recipePosition->amount))
|
||||
{{ round($recipePosition->amount) }}@else{{ $recipePosition->amount }}
|
||||
@endif
|
||||
{{ $__n($recipePosition->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name_plural, true) }}
|
||||
</span>
|
||||
@endif
|
||||
{{ $__n($recipePosition->amount,FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name,FindObjectInArrayByPropertyValue($quantityunits, 'id', $recipePosition->qu_id)->name_plural,true) }}
|
||||
|
||||
@if(!empty($recipePosition->variable_amount))
|
||||
<div class="small text-muted font-italic">{{ $__t('Variable amount') }}</div>
|
||||
@if (!empty($recipePosition->variable_amount))
|
||||
<div class="small text-muted font-italic">{{ $__t('Variable amount') }}
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
<td class="fit-content">
|
||||
<a class="btn btn-sm btn-info recipe-pos-show-note-button @if(empty($recipePosition->note)) disabled @endif"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top"
|
||||
<a class="btn btn-sm btn-info recipe-pos-show-note-button @if (empty($recipePosition->note)) disabled @endif"
|
||||
href="#" data-toggle="tooltip" data-placement="top"
|
||||
title="{{ $__t('Show notes') }}"
|
||||
data-recipe-pos-note="{{ $recipePosition->note }}">
|
||||
<i class="fas fa-eye"></i>
|
||||
|
|
@ -230,50 +224,42 @@
|
|||
<h4>
|
||||
{{ $__t('Included recipes') }}
|
||||
</h4>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a id="recipe-include-add-button"
|
||||
class="btn btn-outline-primary btn-sm m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="#">
|
||||
class="btn btn-outline-primary btn-sm m-1 mt-md-0 mb-md-0 float-right" href="#">
|
||||
{{ $__t('Add') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<table id="recipes-includes-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: recipes_nestings_resolved --}}
|
||||
<table id="recipes-includes-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#recipes-includes-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#recipes-includes-table" href="#"><i
|
||||
class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Recipe') }}</th>
|
||||
<th>{{ $__t('Servings') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@if($mode == "edit")
|
||||
@foreach($recipeNestings as $recipeNesting)
|
||||
@if ($mode == 'edit')
|
||||
@foreach ($recipeNestings as $recipeNesting)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-sm btn-info recipe-include-edit-button"
|
||||
href="#"
|
||||
<a class="btn btn-sm btn-info recipe-include-edit-button" href="#"
|
||||
data-recipe-include-id="{{ $recipeNesting->id }}"
|
||||
data-recipe-included-recipe-id="{{ $recipeNesting->includes_recipe_id }}"
|
||||
data-recipe-included-recipe-servings="{{ $recipeNesting->servings }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-danger recipe-include-delete-button"
|
||||
href="#"
|
||||
<a class="btn btn-sm btn-danger recipe-include-delete-button" href="#"
|
||||
data-recipe-include-id="{{ $recipeNesting->id }}"
|
||||
data-recipe-include-name="{{ FindObjectInArrayByPropertyValue($recipes, 'id', $recipeNesting->includes_recipe_id)->name }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
|
|
@ -302,17 +288,14 @@
|
|||
<div class="form-group w-75 m-0">
|
||||
<div class="input-group">
|
||||
<div class="custom-file">
|
||||
<input type="file"
|
||||
class="custom-file-input"
|
||||
id="recipe-picture"
|
||||
accept="image/*">
|
||||
<input type="file" class="custom-file-input" id="recipe-picture" accept="image/*">
|
||||
<label id="recipe-picture-label"
|
||||
class="custom-file-label @if(empty($recipe->picture_file_name)) d-none @endif"
|
||||
class="custom-file-label @if (empty($recipe->picture_file_name)) d-none @endif"
|
||||
for="recipe-picture">
|
||||
{{ $recipe->picture_file_name }}
|
||||
</label>
|
||||
<label id="recipe-picture-label-none"
|
||||
class="custom-file-label @if(!empty($recipe->picture_file_name)) d-none @endif"
|
||||
class="custom-file-label @if (!empty($recipe->picture_file_name)) d-none @endif"
|
||||
for="recipe-picture">
|
||||
{{ $__t('No file selected') }}
|
||||
</label>
|
||||
|
|
@ -324,15 +307,16 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if(!empty($recipe->picture_file_name))
|
||||
@if (!empty($recipe->picture_file_name))
|
||||
<img id="current-recipe-picture"
|
||||
data-src="{{ $U('/api/files/recipepictures/' . base64_encode($recipe->picture_file_name) . '?force_serve_as=picture&best_fit_width=400') }}"
|
||||
data-src="{{ $U('/api/files/recipepictures/' .base64_encode($recipe->picture_file_name) .'?force_serve_as=picture&best_fit_width=400') }}"
|
||||
class="img-fluid img-thumbnail mt-2 lazy mb-5">
|
||||
<p id="delete-current-recipe-picture-on-save-hint"
|
||||
class="form-text text-muted font-italic d-none mb-5">{{ $__t('The current picture will be deleted on save') }}</p>
|
||||
class="form-text text-muted font-italic d-none mb-5">
|
||||
{{ $__t('The current picture will be deleted on save') }}</p>
|
||||
@else
|
||||
<p id="no-current-recipe-picture-hint"
|
||||
class="form-text text-muted font-italic mb-5">{{ $__t('No picture available') }}</p>
|
||||
<p id="no-current-recipe-picture-hint" class="form-text text-muted font-italic mb-5">
|
||||
{{ $__t('No picture available') }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -342,13 +326,11 @@
|
|||
<div class="title-related-links">
|
||||
<h4>
|
||||
<span class="ls-n1">{{ $__t('grocycode') }}</span>
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
title="{{ $__t('grocycode is a unique referer to this %s in your grocy instance - print it onto a label and scan it like any other barcode', $__t('Recipe')) }}"></i>
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('grocycode is a unique referer to this %s in your grocy instance - print it onto a label and scan it like any other barcode',$__t('Recipe')) }}"></i>
|
||||
</h4>
|
||||
<p>
|
||||
@if($mode == 'edit')
|
||||
@if ($mode == 'edit')
|
||||
<img src="{{ $U('/recipe/' . $recipe->id . '/grocycode?size=60') }}"
|
||||
class="float-lg-left">
|
||||
@endif
|
||||
|
|
@ -356,10 +338,9 @@
|
|||
<p>
|
||||
<a class="btn btn-outline-primary btn-sm"
|
||||
href="{{ $U('/recipe/' . $recipe->id . '/grocycode?download=true') }}">{{ $__t('Download') }}</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
@if (GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<a class="btn btn-outline-primary btn-sm recipe-grocycode-label-print"
|
||||
data-recipe-id="{{ $recipe->id }}"
|
||||
href="#">
|
||||
data-recipe-id="{{ $recipe->id }}" href="#">
|
||||
{{ $__t('Print on label printer') }}
|
||||
</a>
|
||||
@endif
|
||||
|
|
@ -369,46 +350,39 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="recipe-include-editform-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="recipe-include-editform-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content text-center">
|
||||
<div class="modal-header">
|
||||
<h4 id="recipe-include-editform-title"
|
||||
class="modal-title w-100"></h4>
|
||||
<h4 id="recipe-include-editform-title" class="modal-title w-100"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="recipe-include-form"
|
||||
novalidate>
|
||||
<form id="recipe-include-form" novalidate>
|
||||
|
||||
@include('components.recipepicker', array(
|
||||
@include('components.recipepicker', [
|
||||
'recipes' => $recipes,
|
||||
'isRequired' => true
|
||||
))
|
||||
'isRequired' => true,
|
||||
])
|
||||
|
||||
@include('components.numberpicker', array(
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'includes_servings',
|
||||
'label' => 'Servings',
|
||||
'min' => $DEFAULT_MIN_AMOUNT,
|
||||
'decimals' => $userSettings['stock_decimal_places_amounts'],
|
||||
'value' => '1',
|
||||
'additionalCssClasses' => 'locale-number-input locale-number-quantity-amount'
|
||||
))
|
||||
'additionalCssClasses' => 'locale-number-input locale-number-quantity-amount',
|
||||
])
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="save-recipe-include-button"
|
||||
data-dismiss="modal"
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="save-recipe-include-button" data-dismiss="modal"
|
||||
class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
novalidate>
|
||||
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
'productsQuery' => ($recipePosId === 'new' ? 'query%5B%5D=active%3D1&' : '') . 'order=name%3Acollate%20nocase',
|
||||
'nextInputSelector' => '#amount'
|
||||
))
|
||||
|
||||
|
|
|
|||
|
|
@ -5,41 +5,36 @@
|
|||
@section('viewJsName', 'recipes')
|
||||
|
||||
@push('pageStyles')
|
||||
<style>
|
||||
<style>
|
||||
.card-img-top {
|
||||
max-height: 250px !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<script>
|
||||
<script>
|
||||
Grocy.QuantityUnits = {!! json_encode($quantityUnits) !!};
|
||||
Grocy.QuantityUnitConversionsResolved = {!! json_encode($quantityUnitConversionsResolved) !!};
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="@if(boolval($userSettings['recipes_show_list_side_by_side']) || $embedded) col-12 col-md-6 @else col @endif d-print-none">
|
||||
<div class="row">
|
||||
<div class="@if (boolval($userSettings['recipes_show_list_side_by_side']) || $embedded) col-12 col-md-6 @else col @endif d-print-none">
|
||||
<div class="title-related-links border-bottom mb-2 py-1">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/recipe/new') }}">
|
||||
{{ $__t('Add') }}
|
||||
|
|
@ -47,17 +42,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-5 col-xl-5">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -66,11 +57,11 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Status') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="status-filter">
|
||||
<select class="custom-control custom-select" id="status-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
<option value="Xenoughinstock">{{ $__t('Enough in stock') }}</option>
|
||||
<option value="enoughinstockwithshoppinglist">{{ $__t('Not enough in stock, but already on the shopping list') }}</option>
|
||||
<option value="enoughinstockwithshoppinglist">
|
||||
{{ $__t('Not enough in stock, but already on the shopping list') }}</option>
|
||||
<option value="notenoughinstock">{{ $__t('Not enough in stock') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -78,9 +69,7 @@
|
|||
|
||||
<div class="col">
|
||||
<div class="float-right mt-1">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -89,90 +78,74 @@
|
|||
|
||||
<ul class="nav nav-tabs grocy-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active"
|
||||
id="list-tab"
|
||||
data-toggle="tab"
|
||||
href="#list">{{ $__t('List') }}</a>
|
||||
<a class="nav-link active" id="list-tab" data-toggle="tab" href="#list">{{ $__t('List') }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
id="gallery-tab"
|
||||
data-toggle="tab"
|
||||
<a class="nav-link" id="gallery-tab" data-toggle="tab"
|
||||
href="#gallery">{{ $__t('Gallery') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content grocy-tabs">
|
||||
<div class="tab-pane show active"
|
||||
id="list">
|
||||
<table id="recipes-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
<div class="tab-pane show active" id="list">
|
||||
{{-- TODO: DataTables: dynamic data: recipes --}}
|
||||
<table id="recipes-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#recipes-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#recipes-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Desired servings') }}</th>
|
||||
<th data-shadow-rowgroup-column="7"
|
||||
class="@if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif allow-grouping">{{ $__t('Requirements fulfilled') }}</th>
|
||||
class="@if (!GROCY_FEATURE_FLAG_STOCK) d-none @endif allow-grouping">
|
||||
{{ $__t('Requirements fulfilled') }}</th>
|
||||
<th class="d-none">Hidden status for sorting of "Requirements fulfilled" column</th>
|
||||
<th class="d-none">Hidden status for filtering by status</th>
|
||||
<th class="d-none">Hidden recipe ingredient product names</th>
|
||||
<th class="d-none">Hidden status for grouping by status</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($recipes as $recipe)
|
||||
<tr id="recipe-row-{{ $recipe->id }}"
|
||||
data-recipe-id="{{ $recipe->id }}">
|
||||
@foreach ($recipes as $recipe)
|
||||
<tr id="recipe-row-{{ $recipe->id }}" data-recipe-id="{{ $recipe->id }}">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm hide-when-embedded hide-on-fullscreen-card"
|
||||
href="{{ $U('/recipe/') }}{{ $recipe->id }}"
|
||||
data-toggle="tooltip"
|
||||
href="{{ $U('/recipe/') }}{{ $recipe->id }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
<button class="btn btn-sm btn-light text-secondary" type="button"
|
||||
data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="table-inline-menu dropdown-menu dropdown-menu-right hide-on-fullscreen-card hide-when-embedded">
|
||||
<a class="dropdown-item recipe-delete"
|
||||
type="button"
|
||||
href="#"
|
||||
<div
|
||||
class="table-inline-menu dropdown-menu dropdown-menu-right hide-on-fullscreen-card hide-when-embedded">
|
||||
<a class="dropdown-item recipe-delete" type="button" href="#"
|
||||
data-recipe-id="{{ $recipe->id }}"
|
||||
data-recipe-name="{{ $recipe->name }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Delete this item') }}</span>
|
||||
<span
|
||||
class="dropdown-item-text">{{ $__t('Delete this item') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item recipe-copy"
|
||||
type="button"
|
||||
href="#"
|
||||
<a class="dropdown-item recipe-copy" type="button" href="#"
|
||||
data-recipe-id="{{ $recipe->id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Copy recipe') }}</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
<a class="dropdown-item" type="button"
|
||||
href="{{ $U('/recipe/' . $recipe->id . '/grocycode?download=true') }}">
|
||||
<span class="dropdown-item-text">{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Download %s grocycode', $__t('Recipe'))) !!}</span>
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
@if (GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<a class="dropdown-item recipe-grocycode-label-print"
|
||||
data-recipe-id="{{ $recipe->id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
data-recipe-id="{{ $recipe->id }}" type="button" href="#">
|
||||
<span class="dropdown-item-text">{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Print %s grocycode on label printer', $__t('Recipe'))) !!}</span>
|
||||
</a>
|
||||
@endif
|
||||
|
|
@ -185,29 +158,56 @@
|
|||
<td>
|
||||
{{ $recipe->desired_servings }}
|
||||
</td>
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif">
|
||||
@if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
|
||||
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1){{ $__t('Enough in stock') }}@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1){{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count, 'Not enough in stock, %s ingredient missing but already on the shopping list', 'Not enough in stock, %s ingredients missing but already on the shopping list') }}@else{{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count, 'Not enough in stock, %s ingredient missing', 'Not enough in stock, %s ingredients missing') }}@endif</span>
|
||||
<td class="@if (!GROCY_FEATURE_FLAG_STOCK) d-none @endif">
|
||||
@if (FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1)
|
||||
<i class="fas fa-check text-success"></i>
|
||||
@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)
|
||||
<i class="fas fa-exclamation text-warning"></i>@else<i
|
||||
class="fas fa-times text-danger"></i>
|
||||
@endif
|
||||
<span class="timeago-contextual">
|
||||
@if (FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1)
|
||||
{{ $__t('Enough in stock') }}
|
||||
@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)
|
||||
{{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count,'Not enough in stock, %s ingredient missing but already on the shopping list','Not enough in stock, %s ingredients missing but already on the shopping list') }}@else{{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count,'Not enough in stock, %s ingredient missing','Not enough in stock, %s ingredients missing') }}
|
||||
@endif
|
||||
</span>
|
||||
</td>
|
||||
<td class="d-none">
|
||||
{{ FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count }}
|
||||
</td>
|
||||
<td class="d-none">
|
||||
@if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1) Xenoughinstock @elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1) enoughinstockwithshoppinglist @else notenoughinstock @endif
|
||||
@if (FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1)
|
||||
Xenoughinstock
|
||||
@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)
|
||||
enoughinstockwithshoppinglist
|
||||
@else
|
||||
notenoughinstock
|
||||
@endif
|
||||
</td>
|
||||
<td class="d-none">
|
||||
@foreach(FindAllObjectsInArrayByPropertyValue($recipePositionsResolved, 'recipe_id', $recipe->id) as $recipePos)
|
||||
@foreach (FindAllObjectsInArrayByPropertyValue($recipePositionsResolved, 'recipe_id', $recipe->id) as $recipePos)
|
||||
{{ FindObjectInArrayByPropertyValue($products, 'id', $recipePos->product_id)->name . ' ' }}
|
||||
@endforeach
|
||||
</td>
|
||||
<td class="d-none">
|
||||
@if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1) {{ $__t('Enough in stock') }} @elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1) {{ $__t('Not enough in stock, but already on the shopping list') }} @else {{ $__t('Not enough in stock') }} @endif
|
||||
@if (FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1)
|
||||
{{ $__t('Enough in stock') }}
|
||||
@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)
|
||||
{{ $__t('Not enough in stock, but already on the shopping list') }}
|
||||
@else
|
||||
{{ $__t('Not enough in stock') }}
|
||||
@endif
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $recipe->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$recipe->id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
|
|
@ -215,37 +215,42 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane show"
|
||||
id="gallery">
|
||||
<div class="tab-pane show" id="gallery">
|
||||
<div class="card-columns no-gutters">
|
||||
@foreach($recipes as $recipe)
|
||||
<a class="discrete-link recipe-gallery-item @if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1) recipe-enoughinstock @elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1) recipe-enoughinstockwithshoppinglist @else recipe-notenoughinstock @endif"
|
||||
data-recipe-id="{{ $recipe->id }}"
|
||||
href="#">
|
||||
<div id="RecipeGalleryCard-{{ $recipe->id }}"
|
||||
class="card recipe-card">
|
||||
@if(!empty($recipe->picture_file_name))
|
||||
<img data-src="{{ $U('/api/files/recipepictures/' . base64_encode($recipe->picture_file_name) . '?force_serve_as=picture&best_fit_width=400') }}"
|
||||
@foreach ($recipes as $recipe)
|
||||
<a class="discrete-link recipe-gallery-item @if (FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1) recipe-enoughinstock @elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1) recipe-enoughinstockwithshoppinglist @else recipe-notenoughinstock @endif"
|
||||
data-recipe-id="{{ $recipe->id }}" href="#">
|
||||
<div id="RecipeGalleryCard-{{ $recipe->id }}" class="card recipe-card">
|
||||
@if (!empty($recipe->picture_file_name))
|
||||
<img data-src="{{ $U('/api/files/recipepictures/' .base64_encode($recipe->picture_file_name) .'?force_serve_as=picture&best_fit_width=400') }}"
|
||||
class="card-img-top lazy">
|
||||
@endif
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title mb-1">{{ $recipe->name }}</h5>
|
||||
<p class="card-text">
|
||||
@if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
|
||||
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1){{ $__t('Enough in stock') }}@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1){{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count, 'Not enough in stock, %s ingredient missing but already on the shopping list', 'Not enough in stock, %s ingredients missing but already on the shopping list') }}@else{{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count, 'Not enough in stock, %s ingredient missing', 'Not enough in stock, %s ingredients missing') }}@endif</span>
|
||||
@if (FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1)
|
||||
<i class="fas fa-check text-success"></i>
|
||||
@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)
|
||||
<i class="fas fa-exclamation text-warning"></i>@else<i
|
||||
class="fas fa-times text-danger"></i>
|
||||
@endif
|
||||
<span class="timeago-contextual">
|
||||
@if (FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 1)
|
||||
{{ $__t('Enough in stock') }}
|
||||
@elseif(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)
|
||||
{{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count,'Not enough in stock, %s ingredient missing but already on the shopping list','Not enough in stock, %s ingredients missing but already on the shopping list') }}@else{{ $__n(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->missing_products_count,'Not enough in stock, %s ingredient missing','Not enough in stock, %s ingredients missing') }}
|
||||
@endif
|
||||
</span>
|
||||
</p>
|
||||
<p class="card-text mt-2">
|
||||
<a class="btn btn-xs btn-outline-danger hide-when-embedded hide-on-fullscreen-card recipe-delete"
|
||||
href="#"
|
||||
data-recipe-id="{{ $recipe->id }}"
|
||||
data-recipe-name="{{ $recipe->name }}"
|
||||
data-toggle="tooltip"
|
||||
href="#" data-recipe-id="{{ $recipe->id }}"
|
||||
data-recipe-name="{{ $recipe->name }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<a class="btn btn-outline-info btn-xs hide-when-embedded hide-on-fullscreen-card"
|
||||
href="{{ $U('/recipe/') }}{{ $recipe->id }}"
|
||||
data-toggle="tooltip"
|
||||
href="{{ $U('/recipe/') }}{{ $recipe->id }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
|
|
@ -259,22 +264,20 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
@if($selectedRecipe !== null && (boolval($userSettings['recipes_show_list_side_by_side']) || $embedded))
|
||||
@if ($selectedRecipe !== null && (boolval($userSettings['recipes_show_list_side_by_side']) || $embedded))
|
||||
@php
|
||||
$allRecipes = $selectedRecipeSubRecipes;
|
||||
array_unshift($allRecipes, $selectedRecipe);
|
||||
@endphp
|
||||
<div class="col-12 col-md-6 print-view">
|
||||
<div id="selectedRecipeCard"
|
||||
class="card grocy-card">
|
||||
@if(count($allRecipes) > 1)
|
||||
<div id="selectedRecipeCard" class="card grocy-card">
|
||||
@if (count($allRecipes) > 1)
|
||||
<div class="card-header card-header-fullscreen d-print-none">
|
||||
<ul class="nav nav-tabs grocy-tabs card-header-tabs">
|
||||
@foreach($allRecipes as $index=>$recipe)
|
||||
@foreach ($allRecipes as $index => $recipe)
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if($index == 0) active @endif"
|
||||
data-toggle="tab"
|
||||
href="#recipe-{{ $index + 1 }}">{{ $recipe->name }}</a>
|
||||
<a class="nav-link @if ($index == 0) active @endif"
|
||||
data-toggle="tab" href="#recipe-{{ $index + 1 }}">{{ $recipe->name }}</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
|
@ -282,45 +285,39 @@
|
|||
@endif
|
||||
|
||||
<div class="tab-content grocy-tabs print break">
|
||||
@foreach($allRecipes as $index=>$recipe)
|
||||
<div class="tab-pane @if($index == 0) active @endif"
|
||||
id="recipe-{{ $index + 1 }}"
|
||||
role="tabpanel">
|
||||
@if(!empty($recipe->picture_file_name))
|
||||
@foreach ($allRecipes as $index => $recipe)
|
||||
<div class="tab-pane @if ($index == 0) active @endif"
|
||||
id="recipe-{{ $index + 1 }}" role="tabpanel">
|
||||
@if (!empty($recipe->picture_file_name))
|
||||
<img class="card-img-top lazy"
|
||||
src="{{ $U('/api/files/recipepictures/' . base64_encode($recipe->picture_file_name) . '?force_serve_as=picture') }}">
|
||||
@endif
|
||||
<div class="card-body">
|
||||
<div class="shadow p-4 mb-5 bg-white rounded mt-n5 d-print-none @if(empty($recipe->picture_file_name)) d-none @endif">
|
||||
<div
|
||||
class="shadow p-4 mb-5 bg-white rounded mt-n5 d-print-none @if (empty($recipe->picture_file_name)) d-none @endif">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h3 class="card-title mb-0">{{ $recipe->name }}</h3>
|
||||
<div class="card-icons d-flex flex-wrap justify-content-end flex-shrink-1">
|
||||
<a class="btn @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif recipe-consume @if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 0) disabled @endif"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
<a class="btn @if (!GROCY_FEATURE_FLAG_STOCK) d-none @endif recipe-consume @if (FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 0) disabled @endif"
|
||||
href="#" data-toggle="tooltip"
|
||||
title="{{ $__t('Consume all ingredients needed by this recipe') }}"
|
||||
data-recipe-id="{{ $recipe->id }}"
|
||||
data-recipe-name="{{ $recipe->name }}">
|
||||
<i class="fas fa-utensils"></i>
|
||||
</a>
|
||||
<a class="btn @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif recipe-shopping-list @if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1) disabled @endif"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
<a class="btn @if (!GROCY_FEATURE_FLAG_STOCK) d-none @endif recipe-shopping-list @if (FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1) disabled @endif"
|
||||
href="#" data-toggle="tooltip"
|
||||
title="{{ $__t('Put missing products on shopping list') }}"
|
||||
data-recipe-id="{{ $recipe->id }}"
|
||||
data-recipe-name="{{ $recipe->name }}">
|
||||
<i class="fas fa-cart-plus"></i>
|
||||
</a>
|
||||
<a class="btn recipe-fullscreen hide-when-embedded"
|
||||
id="selectedRecipeToggleFullscreenButton"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
id="selectedRecipeToggleFullscreenButton" href="#" data-toggle="tooltip"
|
||||
title="{{ $__t('Expand to fullscreen') }}">
|
||||
<i class="fas fa-expand-arrows-alt"></i>
|
||||
</a>
|
||||
<a class="btn recipe-print"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
<a class="btn recipe-print" href="#" data-toggle="tooltip"
|
||||
title="{{ $__t('Print') }}">
|
||||
<i class="fas fa-print"></i>
|
||||
</a>
|
||||
|
|
@ -328,34 +325,30 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4 @if(!empty($recipe->picture_file_name)) d-none @else d-flex @endif d-print-block justify-content-between align-items-center">
|
||||
<div
|
||||
class="mb-4 @if (!empty($recipe->picture_file_name)) d-none @else d-flex @endif d-print-block justify-content-between align-items-center">
|
||||
<h1 class="card-title mb-0">{{ $recipe->name }}</h1>
|
||||
<div class="card-icons d-flex flex-wrap justify-content-end flex-shrink-1 d-print-none">
|
||||
<a class="btn recipe-consume @if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 0) disabled @endif"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
<div
|
||||
class="card-icons d-flex flex-wrap justify-content-end flex-shrink-1 d-print-none">
|
||||
<a class="btn recipe-consume @if (FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled == 0) disabled @endif"
|
||||
href="#" data-toggle="tooltip"
|
||||
title="{{ $__t('Consume all ingredients needed by this recipe') }}"
|
||||
data-recipe-id="{{ $recipe->id }}"
|
||||
data-recipe-name="{{ $recipe->name }}">
|
||||
<i class="fas fa-utensils"></i>
|
||||
</a>
|
||||
<a class="btn recipe-shopping-list @if(FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1) disabled @endif"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
<a class="btn recipe-shopping-list @if (FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1) disabled @endif"
|
||||
href="#" data-toggle="tooltip"
|
||||
title="{{ $__t('Put missing products on shopping list') }}"
|
||||
data-recipe-id="{{ $recipe->id }}"
|
||||
data-recipe-name="{{ $recipe->name }}">
|
||||
<i class="fas fa-cart-plus"></i>
|
||||
</a>
|
||||
<a class=" btnrecipe-fullscreen hide-when-embedded"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Expand to fullscreen') }}">
|
||||
<a class=" btnrecipe-fullscreen hide-when-embedded" href="#"
|
||||
data-toggle="tooltip" title="{{ $__t('Expand to fullscreen') }}">
|
||||
<i class="fas fa-expand-arrows-alt"></i>
|
||||
</a>
|
||||
<a class="btn recipe-print PrintRecipe"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
<a class="btn recipe-print PrintRecipe" href="#" data-toggle="tooltip"
|
||||
title="{{ $__t('Print') }}">
|
||||
<i class="fas fa-print"></i>
|
||||
</a>
|
||||
|
|
@ -368,31 +361,31 @@
|
|||
@endphp
|
||||
|
||||
<div class="row ml-1">
|
||||
@if(!empty($calories) && intval($calories) > 0)
|
||||
@if (!empty($calories) && intval($calories) > 0)
|
||||
<div class="col-4">
|
||||
<label>{{ $__t('Energy (kcal)') }}</label>
|
||||
<i class="fas fa-question-circle text-muted d-print-none"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('per serving') }}"></i>
|
||||
<h3 class="locale-number locale-number-generic pt-0">{{ $calories }}</h3>
|
||||
<h3 class="locale-number locale-number-generic pt-0">{{ $calories }}
|
||||
</h3>
|
||||
</div>
|
||||
@endif
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
<div class="col-4">
|
||||
<label>{{ $__t('Costs') }}
|
||||
<i class="fas fa-question-circle text-muted d-print-none"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('Based on the prices of the default consume rule (Opened first, then first due first, then first in first out) for in-stock ingredients and on the last price for missing ones') }}"></i>
|
||||
</label>
|
||||
<h3 class="locale-number locale-number-currency pt-0">{{ $costs }}</h3>
|
||||
<h3 class="locale-number locale-number-currency pt-0">{{ $costs }}
|
||||
</h3>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($index == 0)
|
||||
@if ($index == 0)
|
||||
<div class="col-4 d-print-none">
|
||||
@include('components.numberpicker', array(
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'servings-scale',
|
||||
'label' => 'Desired servings',
|
||||
'min' => $DEFAULT_MIN_AMOUNT,
|
||||
|
|
@ -400,8 +393,9 @@
|
|||
'value' => $recipe->desired_servings,
|
||||
'additionalAttributes' => 'data-recipe-id="' . $recipe->id . '"',
|
||||
'hint' => $__t('Base: %s', $recipe->base_servings),
|
||||
'additionalCssClasses' => 'locale-number-input locale-number-quantity-amount'
|
||||
))
|
||||
'additionalCssClasses' =>
|
||||
'locale-number-input locale-number-quantity-amount',
|
||||
])
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
|
@ -410,30 +404,26 @@
|
|||
$recipePositionsFiltered = FindAllObjectsInArrayByPropertyValue($allRecipePositions[$recipe->id], 'recipe_id', $recipe->id);
|
||||
@endphp
|
||||
|
||||
<ul class="nav nav-tabs grocy-tabs mb-3 d-print-none"
|
||||
role="tablist">
|
||||
@if(count($recipePositionsFiltered) > 0)
|
||||
<ul class="nav nav-tabs grocy-tabs mb-3 d-print-none" role="tablist">
|
||||
@if (count($recipePositionsFiltered) > 0)
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active"
|
||||
data-toggle="tab"
|
||||
<a class="nav-link active" data-toggle="tab"
|
||||
href="#ingredients-{{ $index }}"
|
||||
role="tab">{{ $__t('Ingredients') }}</a>
|
||||
</li>
|
||||
@endif
|
||||
@if(!empty($recipe->description))
|
||||
@if (!empty($recipe->description))
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if(count($recipePositionsFiltered) == 0) active @endif"
|
||||
data-toggle="tab"
|
||||
href="#prep-{{ $index }}"
|
||||
<a class="nav-link @if (count($recipePositionsFiltered) == 0) active @endif"
|
||||
data-toggle="tab" href="#prep-{{ $index }}"
|
||||
role="tab">{{ $__t('Preparation') }}</a>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
|
||||
<div class="tab-content grocy-tabs p-2 print">
|
||||
@if(count($recipePositionsFiltered) > 0)
|
||||
<div class="tab-pane active"
|
||||
id="ingredients-{{ $index }}"
|
||||
@if (count($recipePositionsFiltered) > 0)
|
||||
<div class="tab-pane active" id="ingredients-{{ $index }}"
|
||||
role="tabpanel">
|
||||
<div class="mb-2 mt-3 d-none d-print-block">
|
||||
<h3 class="mb-0">{{ $__t('Ingredients') }}</h3>
|
||||
|
|
@ -445,48 +435,77 @@
|
|||
$hasIngredientGroups = false;
|
||||
$hasProductGroups = false;
|
||||
@endphp
|
||||
@foreach($recipePositionsFiltered as $selectedRecipePosition)
|
||||
@if($lastIngredientGroup != $selectedRecipePosition->ingredient_group && !empty($selectedRecipePosition->ingredient_group))
|
||||
@foreach ($recipePositionsFiltered as $selectedRecipePosition)
|
||||
@if ($lastIngredientGroup != $selectedRecipePosition->ingredient_group && !empty($selectedRecipePosition->ingredient_group))
|
||||
@php $hasIngredientGroups = true; @endphp
|
||||
<h5 class="mb-2 mt-2 ml-1"><strong>{{ $selectedRecipePosition->ingredient_group }}</strong></h5>
|
||||
<h5 class="mb-2 mt-2 ml-1">
|
||||
<strong>{{ $selectedRecipePosition->ingredient_group }}</strong>
|
||||
</h5>
|
||||
@endif
|
||||
@if(boolval($userSettings['recipe_ingredients_group_by_product_group']) && $lastProductGroup != $selectedRecipePosition->product_group && !empty($selectedRecipePosition->product_group))
|
||||
@if (boolval($userSettings['recipe_ingredients_group_by_product_group']) && $lastProductGroup != $selectedRecipePosition->product_group && !empty($selectedRecipePosition->product_group))
|
||||
@php $hasProductGroups = true; @endphp
|
||||
<h6 class="mb-2 mt-2 @if($hasIngredientGroups) ml-3 @else ml-1 @endif"><strong>{{ $selectedRecipePosition->product_group }}</strong></h6>
|
||||
<h6
|
||||
class="mb-2 mt-2 @if ($hasIngredientGroups) ml-3 @else ml-1 @endif">
|
||||
<strong>{{ $selectedRecipePosition->product_group }}</strong>
|
||||
</h6>
|
||||
@endif
|
||||
<li class="list-group-item px-0 @if($hasIngredientGroups && $hasProductGroups) ml-4 @elseif($hasIngredientGroups || $hasProductGroups) ml-2 @else ml-0 @endif">
|
||||
@if($selectedRecipePosition->product_active == 0)
|
||||
<div class="small text-muted font-italic">{{ $__t('Disabled') }}</div>
|
||||
<li
|
||||
class="list-group-item px-0 @if ($hasIngredientGroups && $hasProductGroups) ml-4 @elseif($hasIngredientGroups || $hasProductGroups) ml-2 @else ml-0 @endif">
|
||||
@if ($selectedRecipePosition->product_active == 0)
|
||||
<div class="small text-muted font-italic">
|
||||
{{ $__t('Disabled') }}</div>
|
||||
@endif
|
||||
@php
|
||||
$product = FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id);
|
||||
$productQuConversions = FindAllObjectsInArrayByPropertyValue($quantityUnitConversionsResolved, 'product_id', $product->id);
|
||||
$productQuConversions = FindAllObjectsInArrayByPropertyValue($productQuConversions, 'from_qu_id', $product->qu_id_stock);
|
||||
$productQuConversion = FindObjectInArrayByPropertyValue($productQuConversions, 'to_qu_id', $selectedRecipePosition->qu_id);
|
||||
if ($productQuConversion && $selectedRecipePosition->only_check_single_unit_in_stock == 0)
|
||||
{
|
||||
if ($productQuConversion && $selectedRecipePosition->only_check_single_unit_in_stock == 0) {
|
||||
$selectedRecipePosition->recipe_amount = $selectedRecipePosition->recipe_amount * $productQuConversion->factor;
|
||||
}
|
||||
@endphp
|
||||
@if(!empty($selectedRecipePosition->recipe_variable_amount))
|
||||
@if (!empty($selectedRecipePosition->recipe_variable_amount))
|
||||
{{ $selectedRecipePosition->recipe_variable_amount }}
|
||||
@else
|
||||
<span class="locale-number locale-number-quantity-amount">@if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif</span>
|
||||
<span class="locale-number locale-number-quantity-amount">
|
||||
@if ($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2))
|
||||
{{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}
|
||||
@endif
|
||||
{{ $__n($selectedRecipePosition->recipe_amount, FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }}
|
||||
@if(GROCY_FEATURE_FLAG_STOCK)
|
||||
<span class="d-print-none">
|
||||
@if($selectedRecipePosition->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif($selectedRecipePosition->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
|
||||
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->need_fulfilled == 1) {{ $__t('Enough in stock') }} @else {{ $__t('Not enough in stock, %1$s missing, %2$s already on shopping list', round($selectedRecipePosition->missing_amount, 2), round($selectedRecipePosition->amount_on_shopping_list, 2)) }} @endif</span>
|
||||
</span>
|
||||
@endif
|
||||
@if($selectedRecipePosition->need_fulfilled == 1 && GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) <span class="float-right font-italic ml-2 locale-number locale-number-currency">{{ $selectedRecipePosition->costs }}</span> @endif
|
||||
<span class="float-right font-italic"><span class="locale-number locale-number-generic">{{ $selectedRecipePosition->calories }}</span> {{ $__t('Calories') }}</span>
|
||||
@if(!empty($selectedRecipePosition->recipe_variable_amount))
|
||||
<div class="small text-muted font-italic">{{ $__t('Variable amount') }}</div>
|
||||
{{ $__n($selectedRecipePosition->recipe_amount,FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name,FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name_plural) }}
|
||||
{{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }}
|
||||
@if (GROCY_FEATURE_FLAG_STOCK)
|
||||
<span class="d-print-none">
|
||||
@if ($selectedRecipePosition->need_fulfilled == 1)
|
||||
<i class="fas fa-check text-success"></i>
|
||||
@elseif($selectedRecipePosition->need_fulfilled_with_shopping_list == 1)
|
||||
<i
|
||||
class="fas fa-exclamation text-warning"></i>@else<i
|
||||
class="fas fa-times text-danger"></i>
|
||||
@endif
|
||||
<span class="timeago-contextual">
|
||||
@if (FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->need_fulfilled == 1)
|
||||
{{ $__t('Enough in stock') }}
|
||||
@else
|
||||
{{ $__t('Not enough in stock, %1$s missing, %2$s already on shopping list',round($selectedRecipePosition->missing_amount, 2),round($selectedRecipePosition->amount_on_shopping_list, 2)) }}
|
||||
@endif
|
||||
</span>
|
||||
</span>
|
||||
@endif
|
||||
@if ($selectedRecipePosition->need_fulfilled == 1 && GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
<span
|
||||
class="float-right font-italic ml-2 locale-number locale-number-currency">{{ $selectedRecipePosition->costs }}</span>
|
||||
@endif
|
||||
<span class="float-right font-italic"><span
|
||||
class="locale-number locale-number-generic">{{ $selectedRecipePosition->calories }}</span>
|
||||
{{ $__t('Calories') }}</span>
|
||||
@if (!empty($selectedRecipePosition->recipe_variable_amount))
|
||||
<div class="small text-muted font-italic">
|
||||
{{ $__t('Variable amount') }}</div>
|
||||
@endif
|
||||
|
||||
@if(!empty($selectedRecipePosition->note))
|
||||
@if (!empty($selectedRecipePosition->note))
|
||||
<div class="text-muted">{!! nl2br($selectedRecipePosition->note) !!}</div>
|
||||
@endif
|
||||
</li>
|
||||
|
|
@ -496,13 +515,12 @@
|
|||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
<div class="tab-pane @if(count($recipePositionsFiltered) == 0) active @endif"
|
||||
id="prep-{{ $index }}"
|
||||
role="tabpanel">
|
||||
<div class="tab-pane @if (count($recipePositionsFiltered) == 0) active @endif"
|
||||
id="prep-{{ $index }}" role="tabpanel">
|
||||
<div class="mb-2 d-none d-print-block">
|
||||
<h3 class="mb-0">{{ $__t('Preparation') }}</h3>
|
||||
</div>
|
||||
@if(!empty($recipe->description))
|
||||
@if (!empty($recipe->description))
|
||||
{!! $recipe->description !!}
|
||||
@endif
|
||||
</div>
|
||||
|
|
@ -510,16 +528,14 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="missing-recipe-pos-list"
|
||||
class="list-group d-none mt-3">
|
||||
@foreach($recipePositionsResolved as $recipePos)
|
||||
@if(in_array($recipePos->recipe_id, $includedRecipeIdsAbsolute) && $recipePos->missing_amount > 0)
|
||||
<div id="missing-recipe-pos-list" class="list-group d-none mt-3">
|
||||
@foreach ($recipePositionsResolved as $recipePos)
|
||||
@if (in_array($recipePos->recipe_id, $includedRecipeIdsAbsolute) && $recipePos->missing_amount > 0)
|
||||
<a href="#"
|
||||
class="list-group-item list-group-item-action list-group-item-primary missing-recipe-pos-select-button">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input missing-recipe-pos-product-checkbox"
|
||||
type="checkbox"
|
||||
data-product-id="{{ $recipePos->product_id }}"
|
||||
type="checkbox" data-product-id="{{ $recipePos->product_id }}"
|
||||
checked>
|
||||
</div>
|
||||
{{ FindObjectInArrayByPropertyValue($products, 'id', $recipePos->product_id)->name }}
|
||||
|
|
@ -532,5 +548,5 @@
|
|||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,23 +5,23 @@
|
|||
@section('viewJsName', 'shoppinglist')
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/node_modules/bwip-js/dist/bwip-js-min.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/viewjs/purchase.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/node_modules/bwip-js/dist/bwip-js-min.js?v=', true) }}{{ $version }}">
|
||||
</script>
|
||||
<script src="{{ $U('/viewjs/purchase.js?v=', true) }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}" rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row d-print-none hide-on-fullscreen-card">
|
||||
<div class="row d-print-none hide-on-fullscreen-card">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title mr-2 order-0">
|
||||
@yield('title')
|
||||
</h2>
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
<h2 class="mb-0 mr-auto order-3 order-md-1 width-xs-sm-100">
|
||||
<span class="text-muted small">{!! $__t('%s total value', '<span class="locale-number locale-number-currency">' . SumArrayValue($listItems, 'last_price_total') . '</span>') !!}</span>
|
||||
</h2>
|
||||
|
|
@ -31,27 +31,23 @@
|
|||
href="{{ $U('/shoppinglistitem/new?embedded&list=' . $selectedShoppingListId) }}">
|
||||
{{ $__t('Add item') }}
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
@if(GROCY_FEATURE_FLAG_SHOPPINGLIST_MULTIPLE_LISTS)
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
@if (GROCY_FEATURE_FLAG_SHOPPINGLIST_MULTIPLE_LISTS)
|
||||
<div class="my-auto float-right">
|
||||
<select class="custom-control custom-select custom-select-sm"
|
||||
id="selected-shopping-list">
|
||||
@foreach($shoppingLists as $shoppingList)
|
||||
<option @if($shoppingList->id == $selectedShoppingListId) selected="selected" @endif value="{{ $shoppingList->id }}">{{ $shoppingList->name }}</option>
|
||||
{{-- TODO: Select2: dynamic data: shopping_lists --}}
|
||||
<select class="custom-control custom-select custom-select-sm" id="selected-shopping-list">
|
||||
@foreach ($shoppingLists as $shoppingList)
|
||||
<option @if ($shoppingList->id == $selectedShoppingListId) selected="selected" @endif
|
||||
value="{{ $shoppingList->id }}">{{ $shoppingList->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -64,70 +60,63 @@
|
|||
{{ $__t('Edit shopping list') }}
|
||||
</a>
|
||||
<a id="delete-selected-shopping-list"
|
||||
class="btn btn-outline-danger responsive-button m-1 mt-md-0 mb-md-0 float-right @if($selectedShoppingListId == 1) disabled @endif"
|
||||
class="btn btn-outline-danger responsive-button m-1 mt-md-0 mb-md-0 float-right @if ($selectedShoppingListId == 1) disabled @endif"
|
||||
href="#">
|
||||
{{ $__t('Delete shopping list') }}
|
||||
</a>
|
||||
@else
|
||||
<input type="hidden"
|
||||
name="selected-shopping-list"
|
||||
id="selected-shopping-list"
|
||||
value="1">
|
||||
<input type="hidden" name="selected-shopping-list" id="selected-shopping-list" value="1">
|
||||
@endif
|
||||
<a id="print-shopping-list-button"
|
||||
class="btn btn-outline-dark responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="#">
|
||||
class="btn btn-outline-dark responsive-button m-1 mt-md-0 mb-md-0 float-right" href="#">
|
||||
{{ $__t('Print') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="filter-container"
|
||||
class="border-top border-bottom my-2 py-1">
|
||||
<div id="table-filter-row"
|
||||
data-status-filter="belowminstockamount"
|
||||
class="collapse normal-message status-filter-message responsive-button @if(!GROCY_FEATURE_FLAG_STOCK) d-none @else d-md-inline-block @endif"><span class="d-block d-md-none">{{count($missingProducts)}} <i class="fas fa-exclamation-circle"></i></span><span class="d-none d-md-block">{{ $__n(count($missingProducts), '%s product is below defined min. stock amount', '%s products are below defined min. stock amount') }}</span></div>
|
||||
<div id="related-links"
|
||||
class="float-right mt-1 collapse d-md-block">
|
||||
<div id="filter-container" class="border-top border-bottom my-2 py-1">
|
||||
<div id="table-filter-row" data-status-filter="belowminstockamount"
|
||||
class="collapse normal-message status-filter-message responsive-button @if (!GROCY_FEATURE_FLAG_STOCK) d-none @else d-md-inline-block @endif">
|
||||
<span class="d-block d-md-none">{{ count($missingProducts) }} <i
|
||||
class="fas fa-exclamation-circle"></i></span><span
|
||||
class="d-none d-md-block">{{ $__n(count($missingProducts),'%s product is below defined min. stock amount','%s products are below defined min. stock amount') }}</span>
|
||||
</div>
|
||||
<div id="related-links" class="float-right mt-1 collapse d-md-block">
|
||||
<a class="btn btn-primary responsive-button btn-sm mb-1 show-as-dialog-link d-none d-md-inline-block"
|
||||
href="{{ $U('/shoppinglistitem/new?embedded&list=' . $selectedShoppingListId) }}">
|
||||
{{ $__t('Add item') }}
|
||||
</a>
|
||||
<a id="clear-shopping-list"
|
||||
class="btn btn-outline-danger btn-sm mb-1 responsive-button @if($listItems->count() == 0) disabled @endif"
|
||||
class="btn btn-outline-danger btn-sm mb-1 responsive-button @if ($listItems->count() == 0) disabled @endif"
|
||||
href="#">
|
||||
{{ $__t('Clear list') }}
|
||||
</a>
|
||||
<a id="add-all-items-to-stock-button"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button @if (!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
|
||||
href="#">
|
||||
{{ $__t('Add all list items to stock') }}
|
||||
</a>
|
||||
<a id="add-products-below-min-stock-amount"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button @if (!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
|
||||
href="#">
|
||||
{{ $__t('Add products that are below defined min. stock amount') }}
|
||||
</a>
|
||||
<a id="add-overdue-expired-products"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
|
||||
class="btn btn-outline-primary btn-sm mb-1 responsive-button @if (!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
|
||||
href="#">
|
||||
{{ $__t('Add overdue/expired products') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row collapse d-md-flex d-print-none hide-on-fullscreen-card"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex d-print-none hide-on-fullscreen-card" id="table-filter-row">
|
||||
<div class="col-12 col-md-5">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 col-lg-5">
|
||||
|
|
@ -135,11 +124,10 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Status') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="status-filter">
|
||||
<select class="custom-control custom-select" id="status-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
<option class="@if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif"
|
||||
value="belowminstockamount">{{ $__t('Below min. stock amount') }}</option>
|
||||
<option class="@if (!GROCY_FEATURE_FLAG_STOCK) d-none @endif" value="belowminstockamount">
|
||||
{{ $__t('Below min. stock amount') }}</option>
|
||||
<option value="xxDONExx">{{ $__t('Only done items') }}</option>
|
||||
<option value="xxUNDONExx">{{ $__t('Only undone items') }}</option>
|
||||
</select>
|
||||
|
|
@ -147,137 +135,147 @@
|
|||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="shoppinglist-main"
|
||||
class="row d-print-none">
|
||||
<div class="@if(boolval($userSettings['shopping_list_show_calendar'])) col-12 col-md-8 @else col-12 @endif pb-3">
|
||||
<table id="shoppinglist-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
<div id="shoppinglist-main" class="row d-print-none">
|
||||
<div class="@if (boolval($userSettings['shopping_list_show_calendar'])) col-12 col-md-8 @else col-12 @endif pb-3">
|
||||
{{-- TODO: DataTables: dynamic data: shopping_lists --}}
|
||||
<table id="shoppinglist-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#shoppinglist-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#shoppinglist-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th class="allow-grouping">{{ $__t('Product') }} / <em>{{ $__t('Note') }}</em></th>
|
||||
<th>{{ $__t('Amount') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Product group') }}</th>
|
||||
<th class="d-none">Hidden status</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">{{ $__t('Last price (Unit)') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">{{ $__t('Last price (Total)') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif allow-grouping">{{ $__t('Default store') }}</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">{{ $__t('Last price (Unit)') }}</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">{{ $__t('Last price (Total)') }}
|
||||
</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif allow-grouping">
|
||||
{{ $__t('Default store') }}</th>
|
||||
<th>{{ $__t('Barcodes') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $productUserfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $productUserfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($listItems as $listItem)
|
||||
@foreach ($listItems as $listItem)
|
||||
<tr id="shoppinglistitem-{{ $listItem->id }}-row"
|
||||
class="@if(FindObjectInArrayByPropertyValue($missingProducts, 'id', $listItem->product_id) !== null) table-info @endif @if($listItem->done == 1) text-muted text-strike-through @endif">
|
||||
class="@if (FindObjectInArrayByPropertyValue($missingProducts, 'id', $listItem->product_id) !== null) table-info @endif @if ($listItem->done == 1) text-muted text-strike-through @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-success btn-sm order-listitem-button"
|
||||
href="#"
|
||||
data-item-id="{{ $listItem->id }}"
|
||||
data-item-done="{{ $listItem->done }}"
|
||||
data-toggle="tooltip"
|
||||
data-placement="right"
|
||||
<a class="btn btn-success btn-sm order-listitem-button" href="#"
|
||||
data-item-id="{{ $listItem->id }}" data-item-done="{{ $listItem->done }}"
|
||||
data-toggle="tooltip" data-placement="right"
|
||||
title="{{ $__t('Mark this item as done') }}">
|
||||
<i class="fas fa-check"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-info show-as-dialog-link"
|
||||
href="{{ $U('/shoppinglistitem/' . $listItem->id . '?embedded&list=' . $selectedShoppingListId ) }}"
|
||||
data-toggle="tooltip"
|
||||
data-placement="right"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
href="{{ $U('/shoppinglistitem/' . $listItem->id . '?embedded&list=' . $selectedShoppingListId) }}"
|
||||
data-toggle="tooltip" data-placement="right" title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-danger shoppinglist-delete-button"
|
||||
href="#"
|
||||
data-shoppinglist-id="{{ $listItem->id }}"
|
||||
data-toggle="tooltip"
|
||||
data-placement="right"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<a class="btn btn-sm btn-danger shoppinglist-delete-button" href="#"
|
||||
data-shoppinglist-id="{{ $listItem->id }}" data-toggle="tooltip"
|
||||
data-placement="right" title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-primary @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif @if(empty($listItem->product_id)) disabled @else shopping-list-stock-add-workflow-list-item-button @endif"
|
||||
<a class="btn btn-sm btn-primary @if (!GROCY_FEATURE_FLAG_STOCK) d-none @endif @if (empty($listItem->product_id)) disabled @else shopping-list-stock-add-workflow-list-item-button @endif"
|
||||
href="{{ $U('/purchase?embedded&flow=shoppinglistitemtostock&product=') }}{{ $listItem->product_id }}&amount={{ $listItem->amount }}&listitemid={{ $listItem->id }}&quId={{ $listItem->qu_id }}"
|
||||
@if(!empty($listItem->product_id)) data-toggle="tooltip" title="{{ $__t('Add this item to stock') }}" @endif>
|
||||
@if (!empty($listItem->product_id)) data-toggle="tooltip" title="{{ $__t('Add this item to stock') }}" @endif>
|
||||
<i class="fas fa-box"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td class="product-name-cell cursor-link"
|
||||
data-product-id="{{ $listItem->product_id }}">
|
||||
@if(!empty($listItem->product_id)) {{ $listItem->product_name }}<br>@endif<em>{!! nl2br($listItem->note) !!}</em>
|
||||
<td class="product-name-cell cursor-link" data-product-id="{{ $listItem->product_id }}">
|
||||
@if (!empty($listItem->product_id))
|
||||
{{ $listItem->product_name }}<br>
|
||||
@endif
|
||||
<em>
|
||||
{!! nl2br($listItem->note) !!}</em>
|
||||
</td>
|
||||
@if(!empty($listItem->product_id))
|
||||
@if (!empty($listItem->product_id))
|
||||
@php
|
||||
$listItem->amount_origin_qu = $listItem->amount;
|
||||
$product = FindObjectInArrayByPropertyValue($products, 'id', $listItem->product_id);
|
||||
$productQuConversions = FindAllObjectsInArrayByPropertyValue($quantityUnitConversionsResolved, 'product_id', $product->id);
|
||||
$productQuConversions = FindAllObjectsInArrayByPropertyValue($productQuConversions, 'from_qu_id', $product->qu_id_stock);
|
||||
$productQuConversion = FindObjectInArrayByPropertyValue($productQuConversions, 'to_qu_id', $listItem->qu_id);
|
||||
if ($productQuConversion)
|
||||
{
|
||||
if ($productQuConversion) {
|
||||
$listItem->amount = $listItem->amount * $productQuConversion->factor;
|
||||
}
|
||||
@endphp
|
||||
@endif
|
||||
<td data-order={{
|
||||
$listItem->amount }}>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $listItem->amount }}</span> @if(!empty($listItem->product_id)){{ $__n($listItem->amount, $listItem->qu_name, $listItem->qu_name_plural, true) }}@endif
|
||||
<td data-order={{ <td data-order=$listItem->amount }}>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $listItem->amount }}</span>
|
||||
@if (!empty($listItem->product_id))
|
||||
{{ $__n($listItem->amount, $listItem->qu_name, $listItem->qu_name_plural, true) }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if(!empty($listItem->product_group_name)) {{ $listItem->product_group_name }} @else <span class="font-italic font-weight-light">{{ $__t('Ungrouped') }}</span> @endif
|
||||
@if (!empty($listItem->product_group_name))
|
||||
{{ $listItem->product_group_name }}
|
||||
@else
|
||||
<span class="font-italic font-weight-light">{{ $__t('Ungrouped') }}</span>
|
||||
@endif
|
||||
</td>
|
||||
<td id="shoppinglistitem-{{ $listItem->id }}-status-info"
|
||||
class="d-none">
|
||||
@if(FindObjectInArrayByPropertyValue($missingProducts, 'id', $listItem->product_id) !== null) belowminstockamount @endif
|
||||
@if($listItem->done == 1) xxDONExx @else xxUNDONExx @endif
|
||||
<td id="shoppinglistitem-{{ $listItem->id }}-status-info" class="d-none">
|
||||
@if (FindObjectInArrayByPropertyValue($missingProducts, 'id', $listItem->product_id) !== null)
|
||||
belowminstockamount
|
||||
@endif
|
||||
@if ($listItem->done == 1)
|
||||
xxDONExx
|
||||
@else
|
||||
xxUNDONExx
|
||||
@endif
|
||||
</td>
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">
|
||||
<span class="locale-number locale-number-currency">{{ $listItem->last_price_unit }}</span>
|
||||
<td class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">
|
||||
<span
|
||||
class="locale-number locale-number-currency">{{ $listItem->last_price_unit }}</span>
|
||||
</td>
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">
|
||||
<span class="locale-number locale-number-currency">{{ $listItem->last_price_total }}</span>
|
||||
<td class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">
|
||||
<span
|
||||
class="locale-number locale-number-currency">{{ $listItem->last_price_total }}</span>
|
||||
</td>
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">
|
||||
<td class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">
|
||||
{{ $listItem->default_shopping_location_name }}
|
||||
</td>
|
||||
<td>
|
||||
@foreach(explode(',', $listItem->product_barcodes) as $barcode)
|
||||
@if(!empty($barcode))
|
||||
<img class="barcode img-fluid pr-2"
|
||||
data-barcode="{{ $barcode }}">
|
||||
@foreach (explode(',', $listItem->product_barcodes) as $barcode)
|
||||
@if (!empty($barcode))
|
||||
<img class="barcode img-fluid pr-2" data-barcode="{{ $barcode }}">
|
||||
@endif
|
||||
@endforeach
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $listItem->id)
|
||||
))
|
||||
@include('components.userfields_tbody', array(
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$listItem->id
|
||||
),
|
||||
])
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $productUserfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($productUserfieldValues, 'object_id', $listItem->product_id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$productUserfieldValues,
|
||||
'object_id',
|
||||
$listItem->product_id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
|
|
@ -285,62 +283,48 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
@if(boolval($userSettings['shopping_list_show_calendar']))
|
||||
@if (boolval($userSettings['shopping_list_show_calendar']))
|
||||
<div class="col-12 col-md-4 mt-md-2 d-print-none">
|
||||
@include('components.calendarcard')
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="@if(boolval($userSettings['shopping_list_show_calendar'])) col-12 col-md-8 @else col-12 @endif d-print-none pt-2">
|
||||
<div class="@if (boolval($userSettings['shopping_list_show_calendar'])) col-12 col-md-8 @else col-12 @endif d-print-none pt-2">
|
||||
<div class="form-group">
|
||||
<label class="text-larger font-weight-bold"
|
||||
for="notes">{{ $__t('Notes') }}</label>
|
||||
<a id="save-description-button"
|
||||
class="btn btn-success btn-sm ml-1 mb-2"
|
||||
href="#">{{ $__t('Save') }}</a>
|
||||
<a id="clear-description-button"
|
||||
class="btn btn-danger btn-sm ml-1 mb-2"
|
||||
href="#">{{ $__t('Clear') }}</a>
|
||||
<textarea class="form-control wysiwyg-editor"
|
||||
id="description"
|
||||
<label class="text-larger font-weight-bold" for="notes">{{ $__t('Notes') }}</label>
|
||||
<a id="save-description-button" class="btn btn-success btn-sm ml-1 mb-2" href="#">{{ $__t('Save') }}</a>
|
||||
<a id="clear-description-button" class="btn btn-danger btn-sm ml-1 mb-2" href="#">{{ $__t('Clear') }}</a>
|
||||
<textarea class="form-control wysiwyg-editor" id="description"
|
||||
name="description">{{ FindObjectInArrayByPropertyValue($shoppingLists, 'id', $selectedShoppingListId)->description }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="shopping-list-stock-add-workflow-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="shopping-list-stock-add-workflow-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content text-center">
|
||||
<div class="modal-body">
|
||||
<iframe id="shopping-list-stock-add-workflow-purchase-form-frame"
|
||||
class="embed-responsive"
|
||||
<iframe id="shopping-list-stock-add-workflow-purchase-form-frame" class="embed-responsive"
|
||||
src=""></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span id="shopping-list-stock-add-workflow-purchase-item-count"
|
||||
class="d-none mr-auto"></span>
|
||||
<button id="shopping-list-stock-add-workflow-skip-button"
|
||||
type="button"
|
||||
<span id="shopping-list-stock-add-workflow-purchase-item-count" class="d-none mr-auto"></span>
|
||||
<button id="shopping-list-stock-add-workflow-skip-button" type="button"
|
||||
class="btn btn-primary">{{ $__t('Skip') }}</button>
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-none d-print-block">
|
||||
<div class="d-none d-print-block">
|
||||
<div id="print-header">
|
||||
<h1 class="text-center">
|
||||
<img src="{{ $U('/img/grocy_logo.svg?v=', true) }}{{ $version }}"
|
||||
height="30"
|
||||
<img src="{{ $U('/img/grocy_logo.svg?v=', true) }}{{ $version }}" height="30"
|
||||
class="d-print-flex mx-auto">
|
||||
{{ $__t("Shopping list") }}
|
||||
{{ $__t('Shopping list') }}
|
||||
</h1>
|
||||
@if (FindObjectInArrayByPropertyValue($shoppingLists, 'id', $selectedShoppingListId)->name != $__t("Shopping list"))
|
||||
@if (FindObjectInArrayByPropertyValue($shoppingLists, 'id', $selectedShoppingListId)->name != $__t('Shopping list'))
|
||||
<h3 class="text-center">
|
||||
{{ FindObjectInArrayByPropertyValue($shoppingLists, 'id', $selectedShoppingListId)->name }}
|
||||
</h3>
|
||||
|
|
@ -352,44 +336,64 @@
|
|||
</div>
|
||||
<div class="w-75 print-layout-container print-layout-type-table d-none">
|
||||
<div>
|
||||
<table id="shopping-list-print-shadow-table"
|
||||
class="table table-sm table-striped nowrap">
|
||||
{{-- TODO: DataTables: dynamic data: uihelper_shopping_list --}}
|
||||
<table id="shopping-list-print-shadow-table" class="table table-sm table-striped nowrap">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $__t('Product') }} / <em>{{ $__t('Note') }}</em></th>
|
||||
<th>{{ $__t('Amount') }}</th>
|
||||
<th>{{ $__t('Product group') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $productUserfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $productUserfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($listItems as $listItem)
|
||||
@foreach ($listItems as $listItem)
|
||||
<tr>
|
||||
<td>
|
||||
@if(!empty($listItem->product_id)) {{ $listItem->product_name }}<br>@endif<em>{!! nl2br($listItem->note) !!}</em>
|
||||
@if (!empty($listItem->product_id))
|
||||
{{ $listItem->product_name }}<br>
|
||||
@endif
|
||||
<em>
|
||||
{!! nl2br($listItem->note) !!}</em>
|
||||
</td>
|
||||
<td>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $listItem->amount }}</span> @if(!empty($listItem->product_id)){{ $__n($listItem->amount, $listItem->qu_name, $listItem->qu_name_plural, true) }}@endif
|
||||
<span
|
||||
class="locale-number locale-number-quantity-amount">{{ $listItem->amount }}</span>
|
||||
@if (!empty($listItem->product_id))
|
||||
{{ $__n($listItem->amount, $listItem->qu_name, $listItem->qu_name_plural, true) }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if(!empty($listItem->product_group_name)) {{ $listItem->product_group_name }} @else <span class="font-italic font-weight-light">{{ $__t('Ungrouped') }}</span> @endif
|
||||
@if (!empty($listItem->product_group_name))
|
||||
{{ $listItem->product_group_name }}
|
||||
@else
|
||||
<span class="font-italic font-weight-light">{{ $__t('Ungrouped') }}</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $listItem->id)
|
||||
))
|
||||
@include('components.userfields_tbody', array(
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$listItem->id
|
||||
),
|
||||
])
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $productUserfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($productUserfieldValues, 'object_id', $listItem->product_id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$productUserfieldValues,
|
||||
'object_id',
|
||||
$listItem->product_id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
|
|
@ -398,10 +402,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="w-75 print-layout-container print-layout-type-list d-none">
|
||||
@foreach($listItems as $listItem)
|
||||
@foreach ($listItems as $listItem)
|
||||
<div class="py-0">
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $listItem->amount }}</span> @if(!empty($listItem->product_id)){{ $__n($listItem->amount, $listItem->qu_name, $listItem->qu_name_plural, true) }}@endif
|
||||
@if(!empty($listItem->product_id)) {{ $listItem->product_name }}<br>@endif<em>{!! nl2br($listItem->note) !!}</em>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $listItem->amount }}</span>
|
||||
@if (!empty($listItem->product_id))
|
||||
{{ $__n($listItem->amount, $listItem->qu_name, $listItem->qu_name_plural, true) }}
|
||||
@endif
|
||||
@if (!empty($listItem->product_id))
|
||||
{{ $listItem->product_name }}<br>
|
||||
@endif
|
||||
<em>{!! nl2br($listItem->note) !!}</em>
|
||||
</div><br>
|
||||
@endforeach
|
||||
</div>
|
||||
|
|
@ -411,21 +421,17 @@
|
|||
<p id="description-for-print"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade"
|
||||
id="shoppinglist-productcard-modal"
|
||||
tabindex="-1">
|
||||
</div>
|
||||
<div class="modal fade" id="shoppinglist-productcard-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content text-center">
|
||||
<div class="modal-body">
|
||||
@include('components.productcard')
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -1,101 +1,112 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@if($mode == 'edit')
|
||||
@section('title', $__t('Edit shopping list item'))
|
||||
@if ($mode == 'edit')
|
||||
@section('title', $__t('Edit shopping list item'))
|
||||
@else
|
||||
@section('title', $__t('Create shopping list item'))
|
||||
@section('title', $__t('Create shopping list item'))
|
||||
@endif
|
||||
|
||||
@section('viewJsName', 'shoppinglistitemform')
|
||||
|
||||
@section('content')
|
||||
<script>
|
||||
<script>
|
||||
Grocy.QuantityUnits = {!! json_encode($quantityUnits) !!};
|
||||
Grocy.QuantityUnitConversionsResolved = {!! json_encode($quantityUnitConversionsResolved) !!};
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 col-xl-4 pb-3">
|
||||
<script>
|
||||
Grocy.EditMode = '{{ $mode }}';
|
||||
</script>
|
||||
|
||||
@if($mode == 'edit')
|
||||
@if ($mode == 'edit')
|
||||
<script>
|
||||
Grocy.EditObjectId = {{ $listItem->id }};
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<form id="shoppinglist-form"
|
||||
novalidate>
|
||||
<form id="shoppinglist-form" novalidate>
|
||||
|
||||
@if(GROCY_FEATURE_FLAG_SHOPPINGLIST_MULTIPLE_LISTS)
|
||||
@if (GROCY_FEATURE_FLAG_SHOPPINGLIST_MULTIPLE_LISTS)
|
||||
<div class="form-group">
|
||||
<label for="shopping_list_id">{{ $__t('Shopping list') }}</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="shopping_list_id"
|
||||
name="shopping_list_id">
|
||||
@foreach($shoppingLists as $shoppingList)
|
||||
<option @if($mode=='edit'
|
||||
&&
|
||||
$shoppingList->id == $listItem->shopping_list_id) selected="selected" @endif value="{{ $shoppingList->id }}">{{ $shoppingList->name }}</option>
|
||||
{{-- TODO: Select2: dynamic data: shopping_lists --}}
|
||||
<select class="custom-control custom-select" id="shopping_list_id" name="shopping_list_id">
|
||||
@foreach ($shoppingLists as $shoppingList)
|
||||
<option @if ($mode == 'edit' && $shoppingList->id == $listItem->shopping_list_id) selected="selected" @endif
|
||||
value="{{ $shoppingList->id }}">{{ $shoppingList->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@else
|
||||
<input type="hidden"
|
||||
id="shopping_list_id"
|
||||
name="shopping_list_id"
|
||||
value="1">
|
||||
<input type="hidden" id="shopping_list_id" name="shopping_list_id" value="1">
|
||||
@endif
|
||||
|
||||
<div>
|
||||
@php if($mode == 'edit') { $productId = $listItem->product_id; } else { $productId = ''; } @endphp
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
@php
|
||||
if ($mode == 'edit') {
|
||||
$productId = $listItem->product_id;
|
||||
} else {
|
||||
$productId = '';
|
||||
}
|
||||
@endphp
|
||||
@include('components.productpicker', [
|
||||
'productsQuery' => 'query%5B%5D=active%3D1&order=name%3Acollate%20nocase',
|
||||
'nextInputSelector' => '#amount',
|
||||
'isRequired' => true,
|
||||
'prefillById' => $productId,
|
||||
'validationMessage' => 'A product or a note is required'
|
||||
))
|
||||
'validationMessage' => 'A product or a note is required',
|
||||
])
|
||||
</div>
|
||||
|
||||
@php if($mode == 'edit') { $value = $listItem->amount; } else { $value = 1; } @endphp
|
||||
@php if($mode == 'edit') { $initialQuId = $listItem->qu_id; } else { $initialQuId = ''; } @endphp
|
||||
@include('components.productamountpicker', array(
|
||||
@php
|
||||
if ($mode == 'edit') {
|
||||
$value = $listItem->amount;
|
||||
} else {
|
||||
$value = 1;
|
||||
}
|
||||
@endphp
|
||||
@php
|
||||
if ($mode == 'edit') {
|
||||
$initialQuId = $listItem->qu_id;
|
||||
} else {
|
||||
$initialQuId = '';
|
||||
}
|
||||
@endphp
|
||||
@include('components.productamountpicker', [
|
||||
'value' => $value,
|
||||
'initialQuId' => $initialQuId,
|
||||
'min' => $DEFAULT_MIN_AMOUNT,
|
||||
'isRequired' => false
|
||||
))
|
||||
'isRequired' => false,
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<label for="note">{{ $__t('Note') }}</label>
|
||||
<textarea class="form-control"
|
||||
required
|
||||
rows="10"
|
||||
id="note"
|
||||
name="note">@if($mode == 'edit'){{ $listItem->note }}@endif</textarea>
|
||||
<textarea class="form-control" required rows="10" id="note" name="note">
|
||||
@if ($mode == 'edit')
|
||||
{{ $listItem->note }}
|
||||
@endif
|
||||
</textarea>
|
||||
<div class="invalid-feedback">{{ $__t('A product or a note is required') }}</div>
|
||||
</div>
|
||||
|
||||
@include('components.userfieldsform', array(
|
||||
@include('components.userfieldsform', [
|
||||
'userfields' => $userfields,
|
||||
'entity' => 'shopping_list'
|
||||
))
|
||||
'entity' => 'shopping_list',
|
||||
])
|
||||
|
||||
<button id="save-shoppinglist-button"
|
||||
class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
<button id="save-shoppinglist-button" class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,26 +5,21 @@
|
|||
@section('viewJsName', 'shoppinglocations')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button m-1 mt-md-0 mb-md-0 float-right show-as-dialog-link"
|
||||
href="{{ $U('/shoppinglocation/new?embedded') }}">
|
||||
{{ $__t('Add') }}
|
||||
|
|
@ -36,71 +31,59 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="shoppinglocations-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: shopping_locations --}}
|
||||
<table id="shoppinglocations-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#shoppinglocations-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#shoppinglocations-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th>{{ $__t('Description') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($shoppinglocations as $shoppinglocation)
|
||||
@foreach ($shoppinglocations as $shoppinglocation)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm show-as-dialog-link"
|
||||
href="{{ $U('/shoppinglocation/') }}{{ $shoppinglocation->id }}?embedded"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
data-toggle="tooltip" title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm shoppinglocation-delete-button"
|
||||
href="#"
|
||||
<a class="btn btn-danger btn-sm shoppinglocation-delete-button" href="#"
|
||||
data-shoppinglocation-id="{{ $shoppinglocation->id }}"
|
||||
data-shoppinglocation-name="{{ $shoppinglocation->name }}"
|
||||
data-toggle="tooltip"
|
||||
data-shoppinglocation-name="{{ $shoppinglocation->name }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
|
|
@ -112,15 +95,19 @@
|
|||
{{ $shoppinglocation->description }}
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $shoppinglocation->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$shoppinglocation->id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -4,269 +4,239 @@
|
|||
@section('viewJsName', 'stockentries')
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}" rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/viewjs/purchase.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/viewjs/purchase.js?v=', true) }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button" data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
@include('components.productpicker', [
|
||||
'productsQuery' => 'query%5B%5D=active%3D1&order=name%3Acollate%20nocase',
|
||||
'disallowAllProductWorkflows' => true,
|
||||
'isRequired' => false
|
||||
))
|
||||
'isRequired' => false,
|
||||
])
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right mt-3">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="stockentries-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: stock --}}
|
||||
<table id="stockentries-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#stockentries-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#stockentries-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th class="d-none">Hidden product_id</th> <!-- This must be in the first column for searching -->
|
||||
<th class="d-none">Hidden product_id</th>
|
||||
<!-- This must be in the first column for searching -->
|
||||
<th class="allow-grouping">{{ $__t('Product') }}</th>
|
||||
<th>{{ $__t('Amount') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) d-none @endif allow-grouping">{{ $__t('Due date') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif allow-grouping">{{ $__t('Location') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif allow-grouping">{{ $__t('Store') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">{{ $__t('Price') }}</th>
|
||||
<th class="allow-grouping"
|
||||
data-shadow-rowgroup-column="9">{{ $__t('Purchased date') }}</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) d-none @endif allow-grouping">{{ $__t('Due date') }}
|
||||
</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif allow-grouping">{{ $__t('Location') }}
|
||||
</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif allow-grouping">{{ $__t('Store') }}
|
||||
</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">{{ $__t('Price') }}</th>
|
||||
<th class="allow-grouping" data-shadow-rowgroup-column="9">{{ $__t('Purchased date') }}</th>
|
||||
<th class="d-none">Hidden purchased_date</th>
|
||||
<th>{{ $__t('Timestamp') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($stockEntries as $stockEntry)
|
||||
@foreach ($stockEntries as $stockEntry)
|
||||
<tr id="stock-{{ $stockEntry->id }}-row"
|
||||
data-due-type="{{ FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->due_type }}"
|
||||
class="@if(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $stockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $stockEntry->amount > 0) @if(FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->due_type == 1) table-secondary @else table-danger @endif @elseif(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $stockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('+' . $nextXDays . ' days'))
|
||||
&&
|
||||
$stockEntry->amount > 0) table-warning @endif">
|
||||
class="@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $stockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $stockEntry->amount > 0) @if (FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->due_type == 1) table-secondary @else table-danger @endif
|
||||
@elseif(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $stockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('+' . $nextXDays . ' days')) && $stockEntry->amount > 0)
|
||||
table-warning @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-danger btn-sm stock-consume-button"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
title="{{ $__t('Consume this stock entry') }}"
|
||||
<a class="btn btn-danger btn-sm stock-consume-button" href="#" data-toggle="tooltip"
|
||||
data-placement="left" title="{{ $__t('Consume this stock entry') }}"
|
||||
data-product-id="{{ $stockEntry->product_id }}"
|
||||
data-stock-id="{{ $stockEntry->stock_id }}"
|
||||
data-stockrow-id="{{ $stockEntry->id }}"
|
||||
data-location-id="{{ $stockEntry->location_id }}"
|
||||
data-product-name="{{ FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->name }}"
|
||||
data-product-qu-name="{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name }}"
|
||||
data-product-qu-name="{{ FindObjectInArrayByPropertyValue($quantityunits,'id',FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name }}"
|
||||
data-consume-amount="{{ $stockEntry->amount }}">
|
||||
<i class="fas fa-utensils"></i>
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
|
||||
<a class="btn btn-success btn-sm product-open-button @if($stockEntry->open == 1 || FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->enable_tare_weight_handling == 1) disabled @endif"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
|
||||
<a class="btn btn-success btn-sm product-open-button @if ($stockEntry->open == 1 || FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->enable_tare_weight_handling == 1) disabled @endif"
|
||||
href="#" data-toggle="tooltip" data-placement="left"
|
||||
title="{{ $__t('Mark this stock entry as open') }}"
|
||||
data-product-id="{{ $stockEntry->product_id }}"
|
||||
data-product-name="{{ FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->name }}"
|
||||
data-product-qu-name="{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name }}"
|
||||
data-product-qu-name="{{ FindObjectInArrayByPropertyValue($quantityunits,'id',FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name }}"
|
||||
data-stock-id="{{ $stockEntry->stock_id }}"
|
||||
data-stockrow-id="{{ $stockEntry->id }}">
|
||||
<i class="fas fa-box-open"></i>
|
||||
</a>
|
||||
@endif
|
||||
<a class="btn btn-info btn-sm show-as-dialog-link"
|
||||
href="{{ $U('/stockentry/' . $stockEntry->id . '?embedded') }}"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
title="{{ $__t('Edit stock entry') }}">
|
||||
href="{{ $U('/stockentry/' . $stockEntry->id . '?embedded') }}" data-toggle="tooltip"
|
||||
data-placement="left" title="{{ $__t('Edit stock entry') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
<button class="btn btn-sm btn-light text-secondary" type="button"
|
||||
data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
@if(GROCY_FEATURE_FLAG_SHOPPINGLIST)
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/shoppinglistitem/new?embedded&updateexistingproduct&product=' . $stockEntry->product_id ) }}">
|
||||
@if (GROCY_FEATURE_FLAG_SHOPPINGLIST)
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/shoppinglistitem/new?embedded&updateexistingproduct&product=' . $stockEntry->product_id) }}">
|
||||
<i class="fas fa-shopping-cart"></i> {{ $__t('Add to shopping list') }}
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
@endif
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/purchase?embedded&product=' . $stockEntry->product_id ) }}">
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/purchase?embedded&product=' . $stockEntry->product_id) }}">
|
||||
<i class="fas fa-cart-plus"></i> {{ $__t('Purchase') }}
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/consume?embedded&product=' . $stockEntry->product_id . '&locationId=' . $stockEntry->location_id . '&stockId=' . $stockEntry->stock_id) }}">
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/consume?embedded&product=' .$stockEntry->product_id .'&locationId=' .$stockEntry->location_id .'&stockId=' .$stockEntry->stock_id) }}">
|
||||
<i class="fas fa-utensils"></i> {{ $__t('Consume') }}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/transfer?embedded&product=' . $stockEntry->product_id . '&locationId=' . $stockEntry->location_id . '&stockId=' . $stockEntry->stock_id) }}">
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/transfer?embedded&product=' .$stockEntry->product_id .'&locationId=' .$stockEntry->location_id .'&stockId=' .$stockEntry->stock_id) }}">
|
||||
<i class="fas fa-exchange-alt"></i> {{ $__t('Transfer') }}
|
||||
</a>
|
||||
@endif
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/inventory?embedded&product=' . $stockEntry->product_id ) }}">
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/inventory?embedded&product=' . $stockEntry->product_id) }}">
|
||||
<i class="fas fa-list"></i> {{ $__t('Inventory') }}
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item stock-consume-button stock-consume-button-spoiled"
|
||||
type="button"
|
||||
href="#"
|
||||
data-product-id="{{ $stockEntry->product_id }}"
|
||||
type="button" href="#" data-product-id="{{ $stockEntry->product_id }}"
|
||||
data-product-name="{{ FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->name }}"
|
||||
data-product-qu-name="{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name }}"
|
||||
data-product-qu-name="{{ FindObjectInArrayByPropertyValue($quantityunits,'id',FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name }}"
|
||||
data-stock-id="{{ $stockEntry->stock_id }}"
|
||||
data-stockrow-id="{{ $stockEntry->id }}"
|
||||
data-location-id="{{ $stockEntry->location_id }}"
|
||||
data-consume-amount="1">
|
||||
{{ $__t('Consume this stock entry as spoiled', '1 ' . FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->name) }}
|
||||
data-location-id="{{ $stockEntry->location_id }}" data-consume-amount="1">
|
||||
{{ $__t('Consume this stock entry as spoiled','1 ' .FindObjectInArrayByPropertyValue($quantityunits,'id',FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name,FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->name) }}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_RECIPES)
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
@if (GROCY_FEATURE_FLAG_RECIPES)
|
||||
<a class="dropdown-item" type="button"
|
||||
href="{{ $U('/recipes?search=') }}{{ FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->name }}">
|
||||
{{ $__t('Search for recipes containing this product') }}
|
||||
</a>
|
||||
@endif
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item product-name-cell"
|
||||
data-product-id="{{ $stockEntry->product_id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
data-product-id="{{ $stockEntry->product_id }}" type="button" href="#">
|
||||
{{ $__t('Product overview') }}
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/stockjournal?embedded&product=') }}{{ $stockEntry->product_id }}">
|
||||
{{ $__t('Stock journal') }}
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/stockjournal/summary?embedded&product=') }}{{ $stockEntry->product_id }}">
|
||||
{{ $__t('Stock journal summary') }}
|
||||
</a>
|
||||
<a class="dropdown-item link-return"
|
||||
type="button"
|
||||
<a class="dropdown-item link-return" type="button"
|
||||
data-href="{{ $U('/product/') }}{{ $stockEntry->product_id }}">
|
||||
{{ $__t('Edit product') }}
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
<a class="dropdown-item" type="button"
|
||||
href="{{ $U('/stockentry/' . $stockEntry->id . '/grocycode?download=true') }}">
|
||||
{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Download %s grocycode', $__t('Stock entry'))) !!}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
@if (GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<a class="dropdown-item stockentry-grocycode-label-print"
|
||||
data-stock-id="{{ $stockEntry->id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
data-stock-id="{{ $stockEntry->id }}" type="button" href="#">
|
||||
{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Print %s grocycode on label printer', $__t('Stock entry'))) !!}
|
||||
</a>
|
||||
@endif
|
||||
<a class="dropdown-item stockentry-label-link"
|
||||
type="button"
|
||||
target="_blank"
|
||||
<a class="dropdown-item stockentry-label-link" type="button" target="_blank"
|
||||
href="{{ $U('/stockentry/' . $stockEntry->id . '/label') }}">
|
||||
{{ $__t('Open stock entry label in new window') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="d-none"
|
||||
data-product-id="{{ $stockEntry->product_id }}">
|
||||
<td class="d-none" data-product-id="{{ $stockEntry->product_id }}">
|
||||
{{ $stockEntry->product_id }}
|
||||
</td>
|
||||
<td class="product-name-cell cursor-link"
|
||||
data-product-id="{{ $stockEntry->product_id }}">
|
||||
<td class="product-name-cell cursor-link" data-product-id="{{ $stockEntry->product_id }}">
|
||||
{{ FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->name }}
|
||||
</td>
|
||||
<td data-order="{{ $stockEntry->amount }}">
|
||||
<span id="stock-{{ $stockEntry->id }}-amount"
|
||||
class="locale-number locale-number-quantity-amount">{{ $stockEntry->amount }}</span> <span id="product-{{ $stockEntry->product_id }}-qu-name">{{ $__n($stockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name_plural, true) }}</span>
|
||||
<span id="stock-{{ $stockEntry->id }}-opened-amount"
|
||||
class="small font-italic">@if($stockEntry->open == 1){{ $__t('Opened') }}@endif</span>
|
||||
class="locale-number locale-number-quantity-amount">{{ $stockEntry->amount }}</span>
|
||||
<span
|
||||
id="product-{{ $stockEntry->product_id }}-qu-name">{{ $__n($stockEntry->amount,FindObjectInArrayByPropertyValue($quantityunits,'id',FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name,FindObjectInArrayByPropertyValue($quantityunits,'id',FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name_plural,true) }}</span>
|
||||
<span id="stock-{{ $stockEntry->id }}-opened-amount" class="small font-italic">
|
||||
@if ($stockEntry->open == 1)
|
||||
{{ $__t('Opened') }}@endif
|
||||
</span>
|
||||
</td>
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) d-none @endif">
|
||||
<span id="stock-{{ $stockEntry->id }}-due-date">{{ $stockEntry->best_before_date }}</span>
|
||||
<td class="@if (!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) d-none @endif">
|
||||
<span
|
||||
id="stock-{{ $stockEntry->id }}-due-date">{{ $stockEntry->best_before_date }}</span>
|
||||
<time id="stock-{{ $stockEntry->id }}-due-date-timeago"
|
||||
class="timeago timeago-contextual"
|
||||
@if($stockEntry->best_before_date != "") datetime="{{ $stockEntry->best_before_date }} 23:59:59" @endif></time>
|
||||
@if ($stockEntry->best_before_date != '') datetime="{{ $stockEntry->best_before_date }} 23:59:59" @endif></time>
|
||||
</td>
|
||||
<td id="stock-{{ $stockEntry->id }}-location"
|
||||
class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif"
|
||||
class="@if (!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif"
|
||||
data-location-id="{{ $stockEntry->location_id }}">
|
||||
{{ FindObjectInArrayByPropertyValue($locations, 'id', $stockEntry->location_id)->name }}
|
||||
</td>
|
||||
<td id="stock-{{ $stockEntry->id }}-shopping-location"
|
||||
class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif"
|
||||
class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif"
|
||||
data-shopping-location-id="{{ $stockEntry->shopping_location_id }}">
|
||||
@if (FindObjectInArrayByPropertyValue($shoppinglocations, 'id', $stockEntry->shopping_location_id) !== null)
|
||||
{{ FindObjectInArrayByPropertyValue($shoppinglocations, 'id', $stockEntry->shopping_location_id)->name }}
|
||||
@endif
|
||||
</td>
|
||||
<td id="stock-{{ $stockEntry->id }}-price"
|
||||
class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif"
|
||||
class="locale-number locale-number-currency"
|
||||
data-price-id="{{ $stockEntry->price }}">
|
||||
class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif"
|
||||
class="locale-number locale-number-currency" data-price-id="{{ $stockEntry->price }}">
|
||||
{{ $stockEntry->price }}
|
||||
</td>
|
||||
<td>
|
||||
<span id="stock-{{ $stockEntry->id }}-purchased-date">{{ $stockEntry->purchased_date }}</span>
|
||||
<span
|
||||
id="stock-{{ $stockEntry->id }}-purchased-date">{{ $stockEntry->purchased_date }}</span>
|
||||
<time id="stock-{{ $stockEntry->id }}-purchased-date-timeago"
|
||||
class="timeago timeago-contextual"
|
||||
@if(!empty($stockEntry->purchased_date)) datetime="{{ $stockEntry->purchased_date }} 23:59:59" @endif></time>
|
||||
@if (!empty($stockEntry->purchased_date)) datetime="{{ $stockEntry->purchased_date }} 23:59:59" @endif></time>
|
||||
</td>
|
||||
<td class="d-none">{{ $stockEntry->purchased_date }}</td>
|
||||
<td>
|
||||
|
|
@ -275,32 +245,32 @@
|
|||
datetime="{{ $stockEntry->row_created_timestamp }}"></time>
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $stockEntry->product_id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$stockEntry->product_id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="productcard-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="productcard-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content text-center">
|
||||
<div class="modal-body">
|
||||
@include('components.productcard')
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,44 +5,35 @@
|
|||
@section('viewJsName', 'stockjournal')
|
||||
|
||||
@section('content')
|
||||
<div class="title-related-links">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button" data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3 hide-when-embedded"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3 hide-when-embedded" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-outline-dark responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/stockjournal/summary') }}">
|
||||
{{ $__t('Journal summary') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-2">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
|
|
@ -50,24 +41,21 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Product') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="product-filter">
|
||||
<select class="select2 custom-control custom-select" id="product-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($products as $product)
|
||||
<option value="{{ $product->id }}">{{ $product->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Transaction type') }}</span>
|
||||
<span class="input-group-text"><i
|
||||
class="fas fa-filter"></i> {{ $__t('Transaction type') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="transaction-type-filter">
|
||||
{{-- TODO: Select2: static data --}}
|
||||
<select class="custom-control custom-select" id="transaction-type-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($transactionTypes as $transactionType)
|
||||
@foreach ($transactionTypes as $transactionType)
|
||||
<option value="{{ $transactionType }}">{{ $__t($transactionType) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -78,10 +66,10 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Location') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="location-filter">
|
||||
{{-- TODO: Select2: dynamic data: locations --}}
|
||||
<select class="custom-control custom-select" id="location-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($locations as $location)
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->id }}">{{ $location->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -92,10 +80,10 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('User') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="user-filter">
|
||||
{{-- TODO: Select2: dynamic data: users --}}
|
||||
<select class="custom-control custom-select" id="user-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($users as $user)
|
||||
@foreach ($users as $user)
|
||||
<option value="{{ $user->id }}">{{ $user->display_name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -106,11 +94,9 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-clock"></i> {{ $__t('Date range') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="daterange-filter">
|
||||
<select class="custom-control custom-select" id="daterange-filter">
|
||||
<option value="1">{{ $__n(1, '%s month', '%s months') }}</option>
|
||||
<option value="6"
|
||||
selected>{{ $__n(6, '%s month', '%s months') }}</option>
|
||||
<option value="6" selected>{{ $__n(6, '%s month', '%s months') }}</option>
|
||||
<option value="12">{{ $__n(1, '%s year', '%s years') }}</option>
|
||||
<option value="24">{{ $__n(2, '%s month', '%s years') }}</option>
|
||||
<option value="9999">{{ $__t('All') }}</option>
|
||||
|
|
@ -119,54 +105,48 @@
|
|||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right mt-1">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="row mt-2">
|
||||
<div class="col">
|
||||
<table id="stock-journal-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: uihelper_stock_journal --}}
|
||||
<table id="stock-journal-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#stock-journal-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#stock-journal-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th class="allow-grouping">{{ $__t('Product') }}</th>
|
||||
<th>{{ $__t('Amount') }}</th>
|
||||
<th>{{ $__t('Transaction time') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Transaction type') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif allow-grouping">{{ $__t('Location') }}</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif allow-grouping">{{ $__t('Location') }}
|
||||
</th>
|
||||
<th class="allow-grouping">{{ $__t('Done by') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($stockLog as $stockLogEntry)
|
||||
@foreach ($stockLog as $stockLogEntry)
|
||||
<tr id="stock-booking-{{ $stockLogEntry->id }}-row"
|
||||
class="@if($stockLogEntry->undone == 1) text-muted @endif stock-booking-correlation-{{ $stockLogEntry->correlation_id }}"
|
||||
class="@if ($stockLogEntry->undone == 1) text-muted @endif stock-booking-correlation-{{ $stockLogEntry->correlation_id }}"
|
||||
data-correlation-id="{{ $stockLogEntry->correlation_id }}">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-secondary btn-xs undo-stock-booking-button @if($stockLogEntry->undone == 1) disabled @endif"
|
||||
href="#"
|
||||
data-booking-id="{{ $stockLogEntry->id }}"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
title="{{ $__t('Undo transaction') }}">
|
||||
<a class="btn btn-secondary btn-xs undo-stock-booking-button @if ($stockLogEntry->undone == 1) disabled @endif"
|
||||
href="#" data-booking-id="{{ $stockLogEntry->id }}" data-toggle="tooltip"
|
||||
data-placement="left" title="{{ $__t('Undo transaction') }}">
|
||||
<i class="fas fa-undo"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="name-anchor @if($stockLogEntry->undone == 1) text-strike-through @endif">{{ $stockLogEntry->product_name }}</span>
|
||||
@if($stockLogEntry->undone == 1)
|
||||
<span
|
||||
class="name-anchor @if ($stockLogEntry->undone == 1) text-strike-through @endif">{{ $stockLogEntry->product_name }}</span>
|
||||
@if ($stockLogEntry->undone == 1)
|
||||
<br>
|
||||
{{ $__t('Undone on') . ' ' . $stockLogEntry->undone_timestamp }}
|
||||
<time class="timeago timeago-contextual"
|
||||
|
|
@ -174,7 +154,9 @@
|
|||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $stockLogEntry->amount }}</span> {{ $__n($stockLogEntry->amount, $stockLogEntry->qu_name, $stockLogEntry->qu_name_plural, true) }}
|
||||
<span
|
||||
class="locale-number locale-number-quantity-amount">{{ $stockLogEntry->amount }}</span>
|
||||
{{ $__n($stockLogEntry->amount, $stockLogEntry->qu_name, $stockLogEntry->qu_name_plural, true) }}
|
||||
</td>
|
||||
<td>
|
||||
{{ $stockLogEntry->row_created_timestamp }}
|
||||
|
|
@ -187,7 +169,7 @@
|
|||
<span class="font-italic text-muted">{{ $__t('Spoiled') }}</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif">
|
||||
<td class="@if (!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif">
|
||||
{{ $stockLogEntry->location_name }}
|
||||
</td>
|
||||
<td>
|
||||
|
|
@ -198,5 +180,5 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,33 +5,27 @@
|
|||
@section('viewJsName', 'stockjournalsummary')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button" data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-2">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
|
|
@ -39,24 +33,22 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Product') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="product-filter">
|
||||
{{-- TODO: Select2: dynamic data: products --}}
|
||||
<select class="select2 custom-control custom-select" id="product-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($products as $product)
|
||||
<option value="{{ $product->id }}">{{ $product->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Transaction type') }}</span>
|
||||
<span class="input-group-text"><i
|
||||
class="fas fa-filter"></i> {{ $__t('Transaction type') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="transaction-type-filter">
|
||||
{{-- TODO: Select2: static data --}}
|
||||
<select class="custom-control custom-select" id="transaction-type-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($transactionTypes as $transactionType)
|
||||
@foreach ($transactionTypes as $transactionType)
|
||||
<option value="{{ $transactionType }}">{{ $__t($transactionType) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -67,10 +59,10 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('User') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="user-filter">
|
||||
{{-- TODO: Select2: dynamic data: users --}}
|
||||
<select class="custom-control custom-select" id="user-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($users as $user)
|
||||
@foreach ($users as $user)
|
||||
<option value="{{ $user->id }}">{{ $user->display_name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -78,27 +70,23 @@
|
|||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right mt-1">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="row mt-2">
|
||||
<div class="col">
|
||||
<table id="stock-journal-summary-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: uihelper_stock_journal_summary --}}
|
||||
<table id="stock-journal-summary-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#stock-journal-summary-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#stock-journal-summary-table" href="#"><i
|
||||
class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th class="allow-grouping">{{ $__t('Product') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Transaction type') }}</th>
|
||||
|
|
@ -107,7 +95,7 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($entries as $journalEntry)
|
||||
@foreach ($entries as $journalEntry)
|
||||
<tr>
|
||||
<td class="fit-content border-right"></td>
|
||||
<td>
|
||||
|
|
@ -120,12 +108,14 @@
|
|||
{{ $journalEntry->user_display_name }}
|
||||
</td>
|
||||
<td>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $journalEntry->amount }}</span> {{ $__n($journalEntry->amount, $journalEntry->qu_name, $journalEntry->qu_name_plural, true) }}
|
||||
<span
|
||||
class="locale-number locale-number-quantity-amount">{{ $journalEntry->amount }}</span>
|
||||
{{ $__n($journalEntry->amount, $journalEntry->qu_name, $journalEntry->qu_name_plural, true) }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,33 +5,28 @@
|
|||
@section('viewJsName', 'stockoverview')
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}" rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/viewjs/purchase.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/viewjs/purchase.js?v=', true) }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title mr-2 order-0">
|
||||
@yield('title')
|
||||
</h2>
|
||||
<h2 class="mb-0 mr-auto order-3 order-md-1 width-xs-sm-100">
|
||||
<span id="info-current-stock"
|
||||
class="text-muted small"></span>
|
||||
<span id="info-current-stock" class="text-muted small"></span>
|
||||
</h2>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-outline-dark responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/stockjournal') }}">
|
||||
{{ $__t('Journal') }}
|
||||
|
|
@ -40,7 +35,7 @@
|
|||
href="{{ $U('/stockentries') }}">
|
||||
{{ $__t('Stock entries') }}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
<a class="btn btn-outline-dark responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/locationcontentsheet') }}">
|
||||
{{ $__t('Location Content Sheet') }}
|
||||
|
|
@ -50,59 +45,46 @@
|
|||
</div>
|
||||
<div class="border-top border-bottom my-2 py-1">
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
<div id="info-expired-products"
|
||||
data-status-filter="expired"
|
||||
<div id="info-expired-products" data-status-filter="expired"
|
||||
class="error-message status-filter-message responsive-button mr-2"></div>
|
||||
<div id="info-overdue-products"
|
||||
data-status-filter="overdue"
|
||||
<div id="info-overdue-products" data-status-filter="overdue"
|
||||
class="secondary-message status-filter-message responsive-button mr-2"></div>
|
||||
<div id="info-duesoon-products"
|
||||
data-next-x-days="{{ $nextXDays }}"
|
||||
data-status-filter="duesoon"
|
||||
<div id="info-duesoon-products" data-next-x-days="{{ $nextXDays }}" data-status-filter="duesoon"
|
||||
class="warning-message status-filter-message responsive-button mr-2"></div>
|
||||
@endif
|
||||
<div id="info-missing-products"
|
||||
data-status-filter="belowminstockamount"
|
||||
<div id="info-missing-products" data-status-filter="belowminstockamount"
|
||||
class="normal-message status-filter-message responsive-button"></div>
|
||||
<div class="float-right">
|
||||
<a class="btn btn-sm btn-outline-info d-md-none mt-1"
|
||||
data-toggle="collapse"
|
||||
href="#table-filter-row"
|
||||
<a class="btn btn-sm btn-outline-info d-md-none mt-1" data-toggle="collapse" href="#table-filter-row"
|
||||
role="button">
|
||||
<i class="fas fa-filter"></i>
|
||||
</a>
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info mt-1"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info mt-1" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
</div>
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Location') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="location-filter">
|
||||
{{-- TODO: Select2: dynamic data: locations --}}
|
||||
<select class="custom-control custom-select" id="location-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($locations as $location)
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->name }}">{{ $location->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -114,10 +96,10 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Product group') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="product-group-filter">
|
||||
{{-- TODO: Select2: dynamic data: product_groups --}}
|
||||
<select class="custom-control custom-select" id="product-group-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($productGroups as $productGroup)
|
||||
@foreach ($productGroups as $productGroup)
|
||||
<option value="{{ $productGroup->name }}">{{ $productGroup->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -128,10 +110,8 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Status') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="status-filter">
|
||||
<option class="bg-white"
|
||||
value="all">{{ $__t('All') }}</option>
|
||||
<select class="custom-control custom-select" id="status-filter">
|
||||
<option class="bg-white" value="all">{{ $__t('All') }}</option>
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
<option value="duesoon">{{ $__t('Due soon') }}</option>
|
||||
<option value="overdue">{{ $__t('Overdue') }}</option>
|
||||
|
|
@ -142,33 +122,31 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="stock-overview-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: uihelper_stock_current_overview --}}
|
||||
<table id="stock-overview-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#stock-overview-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#stock-overview-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Product') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Product group') }}</th>
|
||||
<th>{{ $__t('Amount') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">{{ $__t('Value') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) d-none @endif allow-grouping">{{ $__t('Next due date') }}</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">{{ $__t('Value') }}</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) d-none @endif allow-grouping">
|
||||
{{ $__t('Next due date') }}</th>
|
||||
<th class="d-none">Hidden location</th>
|
||||
<th class="d-none">Hidden status</th>
|
||||
<th class="d-none">Hidden product group</th>
|
||||
<th>{{ $__t('Calories') }} ({{ $__t('Per stock quantity unit') }})</th>
|
||||
<th>{{ $__t('Calories') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Last purchased') }}</th>
|
||||
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">{{ $__t('Last price') }}</th>
|
||||
<th class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">{{ $__t('Last price') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Min. stock amount') }}</th>
|
||||
<th>{{ $__t('Product description') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Parent product') }}</th>
|
||||
|
|
@ -176,136 +154,133 @@
|
|||
<th>{{ $__t('Product picture') }}</th>
|
||||
<th>{{ $__t('Average price') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($currentStock as $currentStockEntry)
|
||||
@foreach ($currentStock as $currentStockEntry)
|
||||
<tr id="product-{{ $currentStockEntry->product_id }}-row"
|
||||
class="@if(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $currentStockEntry->amount > 0) @if($currentStockEntry->due_type == 1) table-secondary @else table-danger @endif @elseif(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('+' . $nextXDays . ' days')) && $currentStockEntry->amount > 0) table-warning @elseif ($currentStockEntry->product_missing) table-info @endif">
|
||||
class="@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $currentStockEntry->amount > 0) @if ($currentStockEntry->due_type == 1) table-secondary @else table-danger @endif
|
||||
@elseif(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('+' . $nextXDays . ' days')) && $currentStockEntry->amount > 0)
|
||||
table-warning
|
||||
@elseif ($currentStockEntry->product_missing)
|
||||
table-info @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="permission-STOCK_CONSUME btn btn-success btn-sm product-consume-button @if($currentStockEntry->amount_aggregated < $currentStockEntry->quick_consume_amount || $currentStockEntry->enable_tare_weight_handling == 1) disabled @endif"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
title="{{ $__t('Consume %1$s of %2$s', floatval($currentStockEntry->quick_consume_amount) . ' ' . $currentStockEntry->qu_unit_name, $currentStockEntry->product_name) }}"
|
||||
<a class="permission-STOCK_CONSUME btn btn-success btn-sm product-consume-button @if ($currentStockEntry->amount_aggregated < $currentStockEntry->quick_consume_amount || $currentStockEntry->enable_tare_weight_handling == 1) disabled @endif"
|
||||
href="#" data-toggle="tooltip" data-placement="left"
|
||||
title="{{ $__t('Consume %1$s of %2$s',floatval($currentStockEntry->quick_consume_amount) . ' ' . $currentStockEntry->qu_unit_name,$currentStockEntry->product_name) }}"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}"
|
||||
data-product-name="{{ $currentStockEntry->product_name }}"
|
||||
data-product-qu-name="{{ $currentStockEntry->qu_unit_name }}"
|
||||
data-consume-amount="{{ $currentStockEntry->quick_consume_amount }}">
|
||||
<i class="fas fa-utensils"></i> <span class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->quick_consume_amount }}</span>
|
||||
<i class="fas fa-utensils"></i> <span
|
||||
class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->quick_consume_amount }}</span>
|
||||
</a>
|
||||
<a id="product-{{ $currentStockEntry->product_id }}-consume-all-button"
|
||||
class="permission-STOCK_CONSUME btn btn-danger btn-sm product-consume-button @if($currentStockEntry->amount_aggregated == 0) disabled @endif"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
data-placement="right"
|
||||
class="permission-STOCK_CONSUME btn btn-danger btn-sm product-consume-button @if ($currentStockEntry->amount_aggregated == 0) disabled @endif"
|
||||
href="#" data-toggle="tooltip" data-placement="right"
|
||||
title="{{ $__t('Consume all %s which are currently in stock', $currentStockEntry->product_name) }}"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}"
|
||||
data-product-name="{{ $currentStockEntry->product_name }}"
|
||||
data-product-qu-name="{{ $currentStockEntry->qu_unit_name }}"
|
||||
data-consume-amount="@if($currentStockEntry->enable_tare_weight_handling == 1){{$currentStockEntry->tare_weight}}@else{{$currentStockEntry->amount}}@endif"
|
||||
data-original-total-stock-amount="{{$currentStockEntry->amount}}">
|
||||
data-consume-amount="@if ($currentStockEntry->enable_tare_weight_handling == 1) {{ $currentStockEntry->tare_weight }}@else{{ $currentStockEntry->amount }} @endif"
|
||||
data-original-total-stock-amount="{{ $currentStockEntry->amount }}">
|
||||
<i class="fas fa-utensils"></i> {{ $__t('All') }}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
|
||||
<a class="btn btn-success btn-sm product-open-button @if($currentStockEntry->amount_aggregated < $currentStockEntry->quick_consume_amount || $currentStockEntry->amount_aggregated == $currentStockEntry->amount_opened_aggregated || $currentStockEntry->enable_tare_weight_handling == 1) disabled @endif"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
title="{{ $__t('Mark %1$s of %2$s as open', floatval($currentStockEntry->quick_consume_amount) . ' ' . $currentStockEntry->qu_unit_name, $currentStockEntry->product_name) }}"
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
|
||||
<a class="btn btn-success btn-sm product-open-button @if ($currentStockEntry->amount_aggregated < $currentStockEntry->quick_consume_amount || $currentStockEntry->amount_aggregated == $currentStockEntry->amount_opened_aggregated || $currentStockEntry->enable_tare_weight_handling == 1) disabled @endif"
|
||||
href="#" data-toggle="tooltip" data-placement="left"
|
||||
title="{{ $__t('Mark %1$s of %2$s as open',floatval($currentStockEntry->quick_consume_amount) . ' ' . $currentStockEntry->qu_unit_name,$currentStockEntry->product_name) }}"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}"
|
||||
data-product-name="{{ $currentStockEntry->product_name }}"
|
||||
data-product-qu-name="{{ $currentStockEntry->qu_unit_name }}"
|
||||
data-open-amount="{{ $currentStockEntry->quick_consume_amount }}">
|
||||
<i class="fas fa-box-open"></i> <span class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->quick_consume_amount }}</span>
|
||||
<i class="fas fa-box-open"></i> <span
|
||||
class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->quick_consume_amount }}</span>
|
||||
</a>
|
||||
@endif
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
<button class="btn btn-sm btn-light text-secondary" type="button"
|
||||
data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="table-inline-menu dropdown-menu dropdown-menu-right">
|
||||
@if(GROCY_FEATURE_FLAG_SHOPPINGLIST)
|
||||
@if (GROCY_FEATURE_FLAG_SHOPPINGLIST)
|
||||
<a class="dropdown-item show-as-dialog-link permission-SHOPPINGLIST_ITEMS_ADD"
|
||||
type="button"
|
||||
href="{{ $U('/shoppinglistitem/new?embedded&updateexistingproduct&product=' . $currentStockEntry->product_id ) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-shopping-cart"></i></span> <span class="dropdown-item-text">{{ $__t('Add to shopping list') }}</span>
|
||||
href="{{ $U('/shoppinglistitem/new?embedded&updateexistingproduct&product=' . $currentStockEntry->product_id) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-shopping-cart"></i></span>
|
||||
<span
|
||||
class="dropdown-item-text">{{ $__t('Add to shopping list') }}</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
@endif
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_PURCHASE"
|
||||
type="button"
|
||||
href="{{ $U('/purchase?embedded&product=' . $currentStockEntry->product_id ) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-cart-plus"></i></span> <span class="dropdown-item-text">{{ $__t('Purchase') }}</span>
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_PURCHASE" type="button"
|
||||
href="{{ $U('/purchase?embedded&product=' . $currentStockEntry->product_id) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-cart-plus"></i></span> <span
|
||||
class="dropdown-item-text">{{ $__t('Purchase') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_CONSUME @if($currentStockEntry->amount_aggregated <= 0) disabled @endif"
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_CONSUME @if ($currentStockEntry->amount_aggregated <= 0) disabled @endif"
|
||||
type="button"
|
||||
href="{{ $U('/consume?embedded&product=' . $currentStockEntry->product_id ) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-utensils"></i></span> <span class="dropdown-item-text">{{ $__t('Consume') }}</span>
|
||||
href="{{ $U('/consume?embedded&product=' . $currentStockEntry->product_id) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-utensils"></i></span> <span
|
||||
class="dropdown-item-text">{{ $__t('Consume') }}</span>
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_TRANSFER @if($currentStockEntry->amount <= 0) disabled @endif"
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_TRANSFER @if ($currentStockEntry->amount <= 0) disabled @endif"
|
||||
type="button"
|
||||
href="{{ $U('/transfer?embedded&product=' . $currentStockEntry->product_id) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-exchange-alt"></i></span> <span class="dropdown-item-text">{{ $__t('Transfer') }}</span>
|
||||
<span class="dropdown-item-icon"><i class="fas fa-exchange-alt"></i></span>
|
||||
<span class="dropdown-item-text">{{ $__t('Transfer') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_INVENTORY"
|
||||
type="button"
|
||||
href="{{ $U('/inventory?embedded&product=' . $currentStockEntry->product_id ) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-list"></i></span> <span class="dropdown-item-text">{{ $__t('Inventory') }}</span>
|
||||
href="{{ $U('/inventory?embedded&product=' . $currentStockEntry->product_id) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-list"></i></span> <span
|
||||
class="dropdown-item-text">{{ $__t('Inventory') }}</span>
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_RECIPES)
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
@if (GROCY_FEATURE_FLAG_RECIPES)
|
||||
<a class="dropdown-item" type="button"
|
||||
href="{{ $U('/recipes?search=') }}{{ $currentStockEntry->product_name }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Search for recipes containing this product') }}</span>
|
||||
<span
|
||||
class="dropdown-item-text">{{ $__t('Search for recipes containing this product') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item product-name-cell"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}"
|
||||
type="button"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}" type="button"
|
||||
href="#">
|
||||
<span class="dropdown-item-text">{{ $__t('Product overview') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/stockentries?embedded&product=') }}{{ $currentStockEntry->product_id }}"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Stock entries') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/stockjournal?embedded&product=') }}{{ $currentStockEntry->product_id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Stock journal') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
<a class="dropdown-item show-as-dialog-link" type="button"
|
||||
href="{{ $U('/stockjournal/summary?embedded&product_id=') }}{{ $currentStockEntry->product_id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Stock journal summary') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item permission-MASTER_DATA_EDIT link-return"
|
||||
type="button"
|
||||
<a class="dropdown-item permission-MASTER_DATA_EDIT link-return" type="button"
|
||||
data-href="{{ $U('/product/') }}{{ $currentStockEntry->product_id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Edit product') }}</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
<a class="dropdown-item" type="button"
|
||||
href="{{ $U('/product/' . $currentStockEntry->product_id . '/grocycode?download=true') }}">
|
||||
{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Download %s grocycode', $__t('Product'))) !!}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
@if (GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<a class="dropdown-item product-grocycode-label-print"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}"
|
||||
type="button"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}" type="button"
|
||||
href="#">
|
||||
{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Print %s grocycode on label printer', $__t('Product'))) !!}
|
||||
</a>
|
||||
|
|
@ -319,25 +294,34 @@
|
|||
<span class="d-none">{{ $currentStockEntry->product_barcodes }}</span>
|
||||
</td>
|
||||
<td>
|
||||
@if($currentStockEntry->product_group_name !== null){{ $currentStockEntry->product_group_name }}@endif
|
||||
@if ($currentStockEntry->product_group_name !== null)
|
||||
{{ $currentStockEntry->product_group_name }}@endif
|
||||
</td>
|
||||
<td data-order="{{ $currentStockEntry->amount }}">
|
||||
<span id="product-{{ $currentStockEntry->product_id }}-amount"
|
||||
class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->amount }}</span> <span id="product-{{ $currentStockEntry->product_id }}-qu-name">{{ $__n($currentStockEntry->amount, $currentStockEntry->qu_unit_name, $currentStockEntry->qu_unit_name_plural) }}</span>
|
||||
class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->amount }}</span>
|
||||
<span
|
||||
id="product-{{ $currentStockEntry->product_id }}-qu-name">{{ $__n($currentStockEntry->amount, $currentStockEntry->qu_unit_name, $currentStockEntry->qu_unit_name_plural) }}</span>
|
||||
<span id="product-{{ $currentStockEntry->product_id }}-opened-amount"
|
||||
class="small font-italic">@if($currentStockEntry->amount_opened > 0){{ $__t('%s opened', $currentStockEntry->amount_opened) }}@endif</span>
|
||||
@if($currentStockEntry->is_aggregated_amount == 1)
|
||||
class="small font-italic">
|
||||
@if ($currentStockEntry->amount_opened > 0)
|
||||
{{ $__t('%s opened', $currentStockEntry->amount_opened) }}@endif
|
||||
</span>
|
||||
@if ($currentStockEntry->is_aggregated_amount == 1)
|
||||
<span class="pl-1 text-secondary">
|
||||
<i class="fas fa-custom-sigma-sign"></i> <span id="product-{{ $currentStockEntry->product_id }}-amount-aggregated"
|
||||
class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->amount_aggregated }}</span> {{ $__n($currentStockEntry->amount_aggregated, $currentStockEntry->qu_unit_name, $currentStockEntry->qu_unit_name_plural, true) }}
|
||||
@if($currentStockEntry->amount_opened_aggregated > 0)<span id="product-{{ $currentStockEntry->product_id }}-opened-amount-aggregated"
|
||||
class="small font-italic">{{ $__t('%s opened', $currentStockEntry->amount_opened_aggregated) }}</span>@endif
|
||||
<i class="fas fa-custom-sigma-sign"></i> <span
|
||||
id="product-{{ $currentStockEntry->product_id }}-amount-aggregated"
|
||||
class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->amount_aggregated }}</span>
|
||||
{{ $__n($currentStockEntry->amount_aggregated,$currentStockEntry->qu_unit_name,$currentStockEntry->qu_unit_name_plural,true) }}
|
||||
@if ($currentStockEntry->amount_opened_aggregated > 0)<span
|
||||
id="product-{{ $currentStockEntry->product_id }}-opened-amount-aggregated"
|
||||
class="small font-italic">{{ $__t('%s opened', $currentStockEntry->amount_opened_aggregated) }}</span>
|
||||
@endif
|
||||
</span>
|
||||
@endif
|
||||
@if(boolval($userSettings['show_icon_on_stock_overview_page_when_product_is_on_shopping_list']))
|
||||
@if($currentStockEntry->on_shopping_list)
|
||||
<span class="text-muted cursor-normal"
|
||||
data-toggle="tooltip"
|
||||
@if (boolval($userSettings['show_icon_on_stock_overview_page_when_product_is_on_shopping_list']))
|
||||
@if ($currentStockEntry->on_shopping_list)
|
||||
<span class="text-muted cursor-normal" data-toggle="tooltip"
|
||||
title="{{ $__t('This product is currently on a shopping list') }}">
|
||||
<i class="fas fa-shopping-cart"></i>
|
||||
</span>
|
||||
|
|
@ -348,56 +332,63 @@
|
|||
<span id="product-{{ $currentStockEntry->product_id }}-value"
|
||||
class="locale-number locale-number-currency">{{ $currentStockEntry->value }}</span>
|
||||
</td>
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) d-none @endif">
|
||||
<span id="product-{{ $currentStockEntry->product_id }}-next-due-date">{{ $currentStockEntry->best_before_date }}</span>
|
||||
<td class="@if (!GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) d-none @endif">
|
||||
<span
|
||||
id="product-{{ $currentStockEntry->product_id }}-next-due-date">{{ $currentStockEntry->best_before_date }}</span>
|
||||
<time id="product-{{ $currentStockEntry->product_id }}-next-due-date-timeago"
|
||||
class="timeago timeago-contextual"
|
||||
@if(!empty($currentStockEntry->best_before_date)) datetime="{{ $currentStockEntry->best_before_date }} 23:59:59" @endif></time>
|
||||
@if (!empty($currentStockEntry->best_before_date)) datetime="{{ $currentStockEntry->best_before_date }} 23:59:59" @endif></time>
|
||||
</td>
|
||||
<td class="d-none">
|
||||
@foreach(FindAllObjectsInArrayByPropertyValue($currentStockLocations, 'product_id', $currentStockEntry->product_id) as $locationsForProduct)
|
||||
@foreach (FindAllObjectsInArrayByPropertyValue($currentStockLocations, 'product_id', $currentStockEntry->product_id) as $locationsForProduct)
|
||||
xx{{ FindObjectInArrayByPropertyValue($locations, 'id', $locationsForProduct->location_id)->name }}xx
|
||||
@endforeach
|
||||
</td>
|
||||
<td class="d-none">
|
||||
@if($currentStockEntry->best_before_date < date('Y-m-d
|
||||
@if ($currentStockEntry->best_before_date <
|
||||
date(
|
||||
'Y-m-d
|
||||
23:59:59',
|
||||
strtotime('-'
|
||||
. '1'
|
||||
. ' days'
|
||||
))
|
||||
&&
|
||||
$currentStockEntry->amount > 0) @if($currentStockEntry->due_type == 1) overdue @else expired @endif @elseif($currentStockEntry->best_before_date < date('Y-m-d
|
||||
strtotime('-' . '1' . ' days'),
|
||||
) && $currentStockEntry->amount > 0)
|
||||
@if ($currentStockEntry->due_type == 1) overdue
|
||||
@else
|
||||
expired @endif
|
||||
@elseif($currentStockEntry->best_before_date <
|
||||
date(
|
||||
'Y-m-d
|
||||
23:59:59',
|
||||
strtotime('+'
|
||||
.
|
||||
$nextXDays
|
||||
. ' days'
|
||||
))
|
||||
&&
|
||||
$currentStockEntry->amount > 0) duesoon @endif
|
||||
@if($currentStockEntry->amount_aggregated > 0) instockX @endif
|
||||
@if ($currentStockEntry->product_missing) belowminstockamount @endif
|
||||
strtotime('+' . $nextXDays . ' days'),
|
||||
) && $currentStockEntry->amount > 0)
|
||||
duesoon
|
||||
@endif
|
||||
@if ($currentStockEntry->amount_aggregated > 0) instockX @endif
|
||||
@if ($currentStockEntry->product_missing) belowminstockamount
|
||||
@endif
|
||||
</td>
|
||||
<td class="d-none">
|
||||
xx{{ $currentStockEntry->product_group_name }}xx
|
||||
</td>
|
||||
<td>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->product_calories }}</span>
|
||||
<span
|
||||
class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->product_calories }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->calories }}</span>
|
||||
<span
|
||||
class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->calories }}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ $currentStockEntry->last_purchased }}
|
||||
<time class="timeago timeago-contextual"
|
||||
datetime="{{ $currentStockEntry->last_purchased }}"></time>
|
||||
</td>
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">
|
||||
<span class="locale-number locale-number-currency">{{ $currentStockEntry->last_price }}</span>
|
||||
<td class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">
|
||||
<span
|
||||
class="locale-number locale-number-currency">{{ $currentStockEntry->last_price }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->min_stock_amount }}</span>
|
||||
<span
|
||||
class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->min_stock_amount }}</span>
|
||||
</td>
|
||||
<td>
|
||||
{!! $currentStockEntry->product_description !!}
|
||||
|
|
@ -410,41 +401,42 @@
|
|||
{{ $currentStockEntry->product_default_location_name }}
|
||||
</td>
|
||||
<td>
|
||||
@if(!empty($currentStockEntry->product_picture_file_name))
|
||||
<img data-src="{{ $U('/api/files/productpictures/' . base64_encode($currentStockEntry->product_picture_file_name) . '?force_serve_as=picture&best_fit_width=64&best_fit_height=64') }}"
|
||||
@if (!empty($currentStockEntry->product_picture_file_name))
|
||||
<img data-src="{{ $U('/api/files/productpictures/' .base64_encode($currentStockEntry->product_picture_file_name) .'?force_serve_as=picture&best_fit_width=64&best_fit_height=64') }}"
|
||||
class="lazy">
|
||||
@endif
|
||||
</td>
|
||||
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">
|
||||
<span class="locale-number locale-number-currency">{{ $currentStockEntry->average_price }}</span>
|
||||
<td class="@if (!GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) d-none @endif">
|
||||
<span
|
||||
class="locale-number locale-number-currency">{{ $currentStockEntry->average_price }}</span>
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $currentStockEntry->product_id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$currentStockEntry->product_id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="stockoverview-productcard-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal fade" id="stockoverview-productcard-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content text-center">
|
||||
<div class="modal-body">
|
||||
@include('components.productcard')
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,27 +5,27 @@
|
|||
@section('viewJsName', 'stocksettings')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-12">
|
||||
<div id="productpresets">
|
||||
<h4>{{ $__t('Presets for new products') }}</h4>
|
||||
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
<div class="form-group">
|
||||
<label for="product_presets_location_id">{{ $__t('Location') }}</label>
|
||||
<select class="custom-control custom-select user-setting-control"
|
||||
id="product_presets_location_id"
|
||||
{{-- TODO: Select2: dynamic data: locations --}}
|
||||
<select class="custom-control custom-select user-setting-control" id="product_presets_location_id"
|
||||
data-setting-key="product_presets_location_id">
|
||||
<option value="-1"></option>
|
||||
@foreach($locations as $location)
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->id }}">{{ $location->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -34,11 +34,11 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label for="product_presets_product_group_id">{{ $__t('Product group') }}</label>
|
||||
<select class="custom-control custom-select user-setting-control"
|
||||
id="product_presets_product_group_id"
|
||||
{{-- TODO: Select2: dynamic data: product_groups --}}
|
||||
<select class="custom-control custom-select user-setting-control" id="product_presets_product_group_id"
|
||||
data-setting-key="product_presets_product_group_id">
|
||||
<option value="-1"></option>
|
||||
@foreach($productGroups as $productGroup)
|
||||
@foreach ($productGroups as $productGroup)
|
||||
<option value="{{ $productGroup->id }}">{{ $productGroup->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -46,29 +46,28 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label for="product_presets_qu_id">{{ $__t('Quantity unit') }}</label>
|
||||
<select class="custom-control custom-select user-setting-control"
|
||||
id="product_presets_qu_id"
|
||||
{{-- TODO: Select2: dynamic data: quantity_units --}}
|
||||
<select class="custom-control custom-select user-setting-control" id="product_presets_qu_id"
|
||||
data-setting-key="product_presets_qu_id">
|
||||
<option value="-1"></option>
|
||||
@foreach($quantityunits as $quantityunit)
|
||||
@foreach ($quantityunits as $quantityunit)
|
||||
<option value="{{ $quantityunit->id }}">{{ $quantityunit->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@include('components.numberpicker', array(
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'product_presets_default_due_days',
|
||||
'additionalAttributes' => 'data-setting-key="product_presets_default_due_days"',
|
||||
'label' => 'Default due days',
|
||||
'min' => -1,
|
||||
'additionalCssClasses' => 'user-setting-control'
|
||||
))
|
||||
'additionalCssClasses' => 'user-setting-control',
|
||||
])
|
||||
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox"
|
||||
class="form-check-input custom-control-input user-setting-control"
|
||||
<input type="checkbox" class="form-check-input custom-control-input user-setting-control"
|
||||
id="product_presets_treat_opened_as_out_of_stock"
|
||||
data-setting-key="product_presets_treat_opened_as_out_of_stock">
|
||||
<label class="form-check-label custom-control-label"
|
||||
|
|
@ -81,18 +80,17 @@
|
|||
</div>
|
||||
|
||||
<h4 class="mt-2">{{ $__t('Stock overview') }}</h4>
|
||||
@include('components.numberpicker', array(
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'stock_due_soon_days',
|
||||
'additionalAttributes' => 'data-setting-key="stock_due_soon_days"',
|
||||
'label' => 'Due soon days',
|
||||
'min' => 1,
|
||||
'additionalCssClasses' => 'user-setting-control'
|
||||
))
|
||||
'additionalCssClasses' => 'user-setting-control',
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox"
|
||||
class="form-check-input custom-control-input user-setting-control"
|
||||
<input type="checkbox" class="form-check-input custom-control-input user-setting-control"
|
||||
id="show_icon_on_stock_overview_page_when_product_is_on_shopping_list"
|
||||
data-setting-key="show_icon_on_stock_overview_page_when_product_is_on_shopping_list">
|
||||
<label class="form-check-label custom-control-label"
|
||||
|
|
@ -103,23 +101,20 @@
|
|||
</div>
|
||||
|
||||
<h4 class="mt-2">{{ $__t('Purchase') }}</h4>
|
||||
@include('components.numberpicker', array(
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'stock_default_purchase_amount',
|
||||
'additionalAttributes' => 'data-setting-key="stock_default_purchase_amount"',
|
||||
'label' => 'Default amount for purchase',
|
||||
'min' => '0.',
|
||||
'decimals' => $userSettings['stock_decimal_places_amounts'],
|
||||
'additionalCssClasses' => 'user-setting-control locale-number-input locale-number-quantity-amount',
|
||||
))
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox"
|
||||
class="form-check-input custom-control-input user-setting-control"
|
||||
id="show_purchased_date_on_purchase"
|
||||
data-setting-key="show_purchased_date_on_purchase">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="show_purchased_date_on_purchase">
|
||||
<input type="checkbox" class="form-check-input custom-control-input user-setting-control"
|
||||
id="show_purchased_date_on_purchase" data-setting-key="show_purchased_date_on_purchase">
|
||||
<label class="form-check-label custom-control-label" for="show_purchased_date_on_purchase">
|
||||
{{ $__t('Show purchased date on purchase and inventory page (otherwise the purchased date defaults to today)') }}
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -127,8 +122,7 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox"
|
||||
class="form-check-input custom-control-input user-setting-control"
|
||||
<input type="checkbox" class="form-check-input custom-control-input user-setting-control"
|
||||
id="show_warning_on_purchase_when_due_date_is_earlier_than_next"
|
||||
data-setting-key="show_warning_on_purchase_when_due_date_is_earlier_than_next">
|
||||
<label class="form-check-label custom-control-label"
|
||||
|
|
@ -139,20 +133,19 @@
|
|||
</div>
|
||||
|
||||
<h4 class="mt-2">{{ $__t('Consume') }}</h4>
|
||||
@include('components.numberpicker', array(
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'stock_default_consume_amount',
|
||||
'additionalAttributes' => 'data-setting-key="stock_default_consume_amount"',
|
||||
'label' => 'Default amount for consume',
|
||||
'min' => 0,
|
||||
'decimals' => $userSettings['stock_decimal_places_amounts'],
|
||||
'additionalCssClasses' => 'user-setting-control locale-number-input locale-number-quantity-amount',
|
||||
'additionalGroupCssClasses' => 'mb-0'
|
||||
))
|
||||
'additionalGroupCssClasses' => 'mb-0',
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox"
|
||||
class="form-check-input custom-control-input user-setting-control"
|
||||
<input type="checkbox" class="form-check-input custom-control-input user-setting-control"
|
||||
id="stock_default_consume_amount_use_quick_consume_amount"
|
||||
data-setting-key="stock_default_consume_amount_use_quick_consume_amount">
|
||||
<label class="form-check-label custom-control-label"
|
||||
|
|
@ -164,43 +157,37 @@
|
|||
|
||||
<h4 class="mt-2">{{ $__t('Common') }}</h4>
|
||||
|
||||
@include('components.numberpicker', array(
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'stock_decimal_places_amounts',
|
||||
'additionalAttributes' => 'data-setting-key="stock_decimal_places_amounts"',
|
||||
'label' => 'Decimal places allowed for amounts',
|
||||
'min' => 0,
|
||||
'additionalCssClasses' => 'user-setting-control'
|
||||
))
|
||||
'additionalCssClasses' => 'user-setting-control',
|
||||
])
|
||||
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
@include('components.numberpicker', array(
|
||||
@if (GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'stock_decimal_places_prices',
|
||||
'additionalAttributes' => 'data-setting-key="stock_decimal_places_prices"',
|
||||
'label' => 'Decimal places allowed for prices',
|
||||
'min' => 0,
|
||||
'additionalCssClasses' => 'user-setting-control'
|
||||
))
|
||||
'additionalCssClasses' => 'user-setting-control',
|
||||
])
|
||||
|
||||
<div class="form-group mt-n3">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox"
|
||||
class="form-check-input custom-control-input user-setting-control"
|
||||
id="stock_auto_decimal_separator_prices"
|
||||
data-setting-key="stock_auto_decimal_separator_prices">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="stock_auto_decimal_separator_prices">
|
||||
<input type="checkbox" class="form-check-input custom-control-input user-setting-control"
|
||||
id="stock_auto_decimal_separator_prices" data-setting-key="stock_auto_decimal_separator_prices">
|
||||
<label class="form-check-label custom-control-label" for="stock_auto_decimal_separator_prices">
|
||||
{{ $__t('Add decimal separator automatically for price inputs') }}
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('When enabled, you always have to enter the value including decimal places, the decimal separator will be automatically added based on the amount of allowed decimal places') }}"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<a href="{{ $U('/stockoverview') }}"
|
||||
class="btn btn-success">{{ $__t('OK') }}</a>
|
||||
<a href="{{ $U('/stockoverview') }}" class="btn btn-success">{{ $__t('OK') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,102 +5,84 @@
|
|||
@section('viewJsName', 'taskcategories')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100" id="related-links">
|
||||
<a class="btn btn-primary responsive-button m-1 mt-md-0 mb-md-0 float-right show-as-dialog-link"
|
||||
href="{{ $U('/taskcategory/new?embedded') }}">
|
||||
{{ $__t('Add') }}
|
||||
</a>
|
||||
<a class="btn btn-outline-secondary"
|
||||
href="{{ $U('/userfields?entity=task_categories') }}">
|
||||
<a class="btn btn-outline-secondary" href="{{ $U('/userfields?entity=task_categories') }}">
|
||||
{{ $__t('Configure userfields') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="taskcategories-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: task_categories --}}
|
||||
<table id="taskcategories-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#taskcategories-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#taskcategories-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th>{{ $__t('Description') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($taskCategories as $taskCategory)
|
||||
@foreach ($taskCategories as $taskCategory)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm show-as-dialog-link"
|
||||
href="{{ $U('/taskcategory/') }}{{ $taskCategory->id }}?embedded"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
data-toggle="tooltip" title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm task-category-delete-button"
|
||||
href="#"
|
||||
<a class="btn btn-danger btn-sm task-category-delete-button" href="#"
|
||||
data-category-id="{{ $taskCategory->id }}"
|
||||
data-category-name="{{ $taskCategory->name }}"
|
||||
data-toggle="tooltip"
|
||||
data-category-name="{{ $taskCategory->name }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
|
|
@ -112,15 +94,19 @@
|
|||
{{ $taskCategory->description }}
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $taskCategory->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$taskCategory->id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -1,64 +1,59 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@if($mode == 'edit')
|
||||
@section('title', $__t('Edit task'))
|
||||
@if ($mode == 'edit')
|
||||
@section('title', $__t('Edit task'))
|
||||
@else
|
||||
@section('title', $__t('Create task'))
|
||||
@section('title', $__t('Create task'))
|
||||
@endif
|
||||
|
||||
@section('viewJsName', 'taskform')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-12">
|
||||
<script>
|
||||
Grocy.EditMode = '{{ $mode }}';
|
||||
</script>
|
||||
|
||||
@if($mode == 'edit')
|
||||
@if ($mode == 'edit')
|
||||
<script>
|
||||
Grocy.EditObjectId = {{ $task->id }};
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<form id="task-form"
|
||||
novalidate>
|
||||
<form id="task-form" novalidate>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">{{ $__t('Name') }}</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
required
|
||||
id="name"
|
||||
name="name"
|
||||
value="@if($mode == 'edit'){{ $task->name }}@endif">
|
||||
<input type="text" class="form-control" required id="name" name="name"
|
||||
value="@if ($mode == 'edit') {{ $task->name }} @endif">
|
||||
<div class="invalid-feedback">{{ $__t('A name is required') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">{{ $__t('Description') }}</label>
|
||||
<textarea class="form-control"
|
||||
rows="4"
|
||||
id="description"
|
||||
name="description">@if($mode == 'edit'){{ $task->description }}@endif</textarea>
|
||||
<textarea class="form-control" rows="4" id="description" name="description">
|
||||
@if ($mode == 'edit')
|
||||
{{ $task->description }}
|
||||
@endif
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$initialDueDate = null;
|
||||
if ($mode == 'edit' && !empty($task->due_date))
|
||||
{
|
||||
if ($mode == 'edit' && !empty($task->due_date)) {
|
||||
$initialDueDate = date('Y-m-d', strtotime($task->due_date));
|
||||
}
|
||||
@endphp
|
||||
@include('components.datetimepicker', array(
|
||||
@include('components.datetimepicker', [
|
||||
'id' => 'due_date',
|
||||
'label' => 'Due',
|
||||
'format' => 'YYYY-MM-DD',
|
||||
|
|
@ -69,48 +64,46 @@
|
|||
'invalidFeedback' => $__t('A due date is required'),
|
||||
'nextInputSelector' => 'category_id',
|
||||
'additionalGroupCssClasses' => 'date-only-datetimepicker',
|
||||
'isRequired' => false
|
||||
))
|
||||
'isRequired' => false,
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<label for="category_id">{{ $__t('Category') }}</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="category_id"
|
||||
name="category_id">
|
||||
{{-- TODO: Select2: dynamic data: task_categories --}}
|
||||
<select class="custom-control custom-select" id="category_id" name="category_id">
|
||||
<option></option>
|
||||
@foreach($taskCategories as $taskCategory)
|
||||
<option @if($mode=='edit'
|
||||
&&
|
||||
$taskCategory->id == $task->category_id) selected="selected" @endif value="{{ $taskCategory->id }}">{{ $taskCategory->name }}</option>
|
||||
@foreach ($taskCategories as $taskCategory)
|
||||
<option @if ($mode == 'edit' && $taskCategory->id == $task->category_id) selected="selected" @endif
|
||||
value="{{ $taskCategory->id }}">{{ $taskCategory->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$initUserId = GROCY_USER_ID;
|
||||
if ($mode == 'edit')
|
||||
{
|
||||
if ($mode == 'edit') {
|
||||
$initUserId = $task->assigned_to_user_id;
|
||||
}
|
||||
@endphp
|
||||
@include('components.userpicker', array(
|
||||
@include('components.userpicker', [
|
||||
'label' => 'Assigned to',
|
||||
'users' => $users,
|
||||
'prefillByUserId' => $initUserId
|
||||
))
|
||||
'prefillByUserId' => $initUserId,
|
||||
])
|
||||
|
||||
@include('components.userfieldsform', array(
|
||||
@include('components.userfieldsform', [
|
||||
'userfields' => $userfields,
|
||||
'entity' => 'tasks'
|
||||
))
|
||||
'entity' => 'tasks',
|
||||
])
|
||||
|
||||
@if($mode == 'edit')
|
||||
@if ($mode == 'edit')
|
||||
<button class="btn btn-success save-task-button">{{ $__t('Save') }}</button>
|
||||
@else
|
||||
<button class="btn btn-success save-task-button">{{ $__t('Save & close') }}</button>
|
||||
<button class="btn btn-primary save-task-button add-another">{{ $__t('Save & add another task') }}</button>
|
||||
<button
|
||||
class="btn btn-primary save-task-button add-another">{{ $__t('Save & add another task') }}</button>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,68 +5,53 @@
|
|||
@section('viewJsName', 'tasks')
|
||||
|
||||
@push('pageStyles')
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}"
|
||||
rel="stylesheet">
|
||||
<link href="{{ $U('/node_modules/animate.css/animate.min.css?v=', true) }}{{ $version }}" rel="stylesheet">
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 float-right order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100 m-1 mt-md-0 mb-md-0 float-right"
|
||||
id="related-links">
|
||||
<a class="btn btn-primary responsive-button show-as-dialog-link"
|
||||
href="{{ $U('/task/new?embedded') }}">
|
||||
<a class="btn btn-primary responsive-button show-as-dialog-link" href="{{ $U('/task/new?embedded') }}">
|
||||
{{ $__t('Add') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-top border-bottom my-2 py-1">
|
||||
<div id="info-overdue-tasks"
|
||||
data-status-filter="overdue"
|
||||
<div id="info-overdue-tasks" data-status-filter="overdue"
|
||||
class="error-message status-filter-message responsive-button mr-2"></div>
|
||||
<div id="info-due-today-tasks"
|
||||
data-status-filter="duetoday"
|
||||
<div id="info-due-today-tasks" data-status-filter="duetoday"
|
||||
class="normal-message status-filter-message responsive-button mr-2"></div>
|
||||
<div id="info-due-soon-tasks"
|
||||
data-status-filter="duesoon"
|
||||
data-next-x-days="{{ $nextXDays }}"
|
||||
class="warning-message status-filter-message responsive-button @if($nextXDays == 0) d-none @endif"></div>
|
||||
<div id="info-due-soon-tasks" data-status-filter="duesoon" data-next-x-days="{{ $nextXDays }}"
|
||||
class="warning-message status-filter-message responsive-button @if ($nextXDays == 0) d-none @endif">
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<a class="btn btn-sm btn-outline-info d-md-none mt-1"
|
||||
data-toggle="collapse"
|
||||
href="#table-filter-row"
|
||||
<a class="btn btn-sm btn-outline-info d-md-none mt-1" data-toggle="collapse" href="#table-filter-row"
|
||||
role="button">
|
||||
<i class="fas fa-filter"></i>
|
||||
</a>
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info mt-1"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info mt-1" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
|
|
@ -74,12 +59,11 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Status') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="status-filter">
|
||||
<select class="custom-control custom-select" id="status-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
<option value="overdue">{{ $__t('Overdue') }}</option>
|
||||
<option value="duetoday">{{ $__t('Due today') }}</option>
|
||||
@if($nextXDays > 0)
|
||||
@if ($nextXDays > 0)
|
||||
<option value="duesoon">{{ $__t('Due soon') }}</option>
|
||||
@endif
|
||||
</select>
|
||||
|
|
@ -87,118 +71,112 @@
|
|||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="form-check custom-control custom-checkbox">
|
||||
<input class="form-check-input custom-control-input"
|
||||
type="checkbox"
|
||||
id="show-done-tasks">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="show-done-tasks">
|
||||
<input class="form-check-input custom-control-input" type="checkbox" id="show-done-tasks">
|
||||
<label class="form-check-label custom-control-label" for="show-done-tasks">
|
||||
{{ $__t('Show done tasks') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="tasks-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: tasks --}}
|
||||
<table id="tasks-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#tasks-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#tasks-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Task') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Due') }}</th>
|
||||
<th class="allow-grouping"
|
||||
data-shadow-rowgroup-column="6">{{ $__t('Category') }}</th>
|
||||
<th class="allow-grouping" data-shadow-rowgroup-column="6">{{ $__t('Category') }}</th>
|
||||
<th class="allow-grouping">{{ $__t('Assigned to') }}</th>
|
||||
<th class="d-none">Hidden status</th>
|
||||
<th class="d-none">Hidden category_id</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($tasks as $task)
|
||||
@foreach ($tasks as $task)
|
||||
<tr id="task-{{ $task->id }}-row"
|
||||
class="@if($task->due_type == 'overdue') table-danger @elseif($task->due_type == 'duetoday') table-info @elseif($task->due_type == 'duesoon') table-warning @endif">
|
||||
class="@if ($task->due_type == 'overdue') table-danger @elseif($task->due_type == 'duetoday') table-info @elseif($task->due_type == 'duesoon') table-warning @endif">
|
||||
<td class="fit-content border-right">
|
||||
@if($task->done == 0)
|
||||
<a class="btn btn-success btn-sm do-task-button"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
title="{{ $__t('Mark task as completed') }}"
|
||||
data-task-id="{{ $task->id }}"
|
||||
data-task-name="{{ $task->name }}">
|
||||
@if ($task->done == 0)
|
||||
<a class="btn btn-success btn-sm do-task-button" href="#" data-toggle="tooltip"
|
||||
data-placement="left" title="{{ $__t('Mark task as completed') }}"
|
||||
data-task-id="{{ $task->id }}" data-task-name="{{ $task->name }}">
|
||||
<i class="fas fa-check"></i>
|
||||
</a>
|
||||
@else
|
||||
<a class="btn btn-secondary btn-sm undo-task-button"
|
||||
href="#"
|
||||
data-toggle="tooltip"
|
||||
data-placement="left"
|
||||
title="{{ $__t('Undo task', $task->name) }}"
|
||||
data-task-id="{{ $task->id }}"
|
||||
data-task-name="{{ $task->name }}">
|
||||
<a class="btn btn-secondary btn-sm undo-task-button" href="#" data-toggle="tooltip"
|
||||
data-placement="left" title="{{ $__t('Undo task', $task->name) }}"
|
||||
data-task-id="{{ $task->id }}" data-task-name="{{ $task->name }}">
|
||||
<i class="fas fa-undo"></i>
|
||||
</a>
|
||||
@endif
|
||||
<a class="btn btn-info btn-sm show-as-dialog-link"
|
||||
href="{{ $U('/task/') }}{{ $task->id }}?embedded"
|
||||
data-toggle="tooltip"
|
||||
href="{{ $U('/task/') }}{{ $task->id }}?embedded" data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-danger delete-task-button"
|
||||
href="#"
|
||||
data-task-id="{{ $task->id }}"
|
||||
data-task-name="{{ $task->name }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<a class="btn btn-sm btn-danger delete-task-button" href="#"
|
||||
data-task-id="{{ $task->id }}" data-task-name="{{ $task->name }}"
|
||||
data-toggle="tooltip" title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td id="task-{{ $task->id }}-name"
|
||||
class="@if($task->done == 1) text-strike-through @endif">
|
||||
class="@if ($task->done == 1) text-strike-through @endif">
|
||||
{{ $task->name }}
|
||||
</td>
|
||||
<td>
|
||||
<span>{{ $task->due_date }}</span>
|
||||
<time class="timeago timeago-contextual"
|
||||
datetime="{{ $task->due_date }}"></time>
|
||||
<time class="timeago timeago-contextual" datetime="{{ $task->due_date }}"></time>
|
||||
</td>
|
||||
<td>
|
||||
@if($task->category_id != null) <span>{{ FindObjectInArrayByPropertyValue($taskCategories, 'id', $task->category_id)->name }}</span> @else <span class="font-italic font-weight-light">{{ $__t('Uncategorized') }}</span>@endif
|
||||
@if ($task->category_id != null)
|
||||
<span>{{ FindObjectInArrayByPropertyValue($taskCategories, 'id', $task->category_id)->name }}</span>
|
||||
@else
|
||||
<span class="font-italic font-weight-light">{{ $__t('Uncategorized') }}</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if($task->assigned_to_user_id != null) <span>{{ GetUserDisplayName(FindObjectInArrayByPropertyValue($users, 'id', $task->assigned_to_user_id)) }}</span> @endif
|
||||
@if ($task->assigned_to_user_id != null)
|
||||
<span>{{ GetUserDisplayName(FindObjectInArrayByPropertyValue($users, 'id', $task->assigned_to_user_id)) }}</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="d-none">
|
||||
{{ $task->due_type }}
|
||||
@if($task->due_type == 'duetoday')
|
||||
@if ($task->due_type == 'duetoday')
|
||||
duesoon
|
||||
@endif
|
||||
</td>
|
||||
<td class="d-none">
|
||||
@if($task->category_id != null) {{ FindObjectInArrayByPropertyValue($taskCategories, 'id', $task->category_id)->name }} @else {{ $__t('Uncategorized') }} @endif
|
||||
@if ($task->category_id != null)
|
||||
{{ FindObjectInArrayByPropertyValue($taskCategories, 'id', $task->category_id)->name }}
|
||||
@else
|
||||
{{ $__t('Uncategorized') }}
|
||||
@endif
|
||||
</td>
|
||||
@include('components.userfields_tbody',
|
||||
array( 'userfields'=> $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $task->id)
|
||||
))
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$task->id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,58 +5,59 @@
|
|||
@section('viewJsName', 'transfer')
|
||||
|
||||
@section('content')
|
||||
<script>
|
||||
<script>
|
||||
Grocy.QuantityUnits = {!! json_encode($quantityUnits) !!};
|
||||
Grocy.QuantityUnitConversionsResolved = {!! json_encode($quantityUnitConversionsResolved) !!};
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 col-xl-4 pb-3">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
|
||||
<hr class="my-2">
|
||||
|
||||
<form id="transfer-form"
|
||||
novalidate>
|
||||
<form id="transfer-form" novalidate>
|
||||
|
||||
@include('components.productpicker', array(
|
||||
'products' => $products,
|
||||
'barcodes' => $barcodes,
|
||||
@include('components.productpicker', [
|
||||
'productsQuery' => 'query%5B%5D=active%3D1&only_in_stock=1&order=name%3Acollate%20nocase',
|
||||
'nextInputSelector' => '#location_id_from',
|
||||
'disallowAddProductWorkflows' => true
|
||||
))
|
||||
'disallowAddProductWorkflows' => true,
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<label for="location_id_from">{{ $__t('From location') }}</label>
|
||||
<select required
|
||||
class="custom-control custom-select location-combobox"
|
||||
id="location_id_from"
|
||||
{{-- TODO: Select2: dynamic data: locations --}}
|
||||
<select required class="custom-control custom-select location-combobox" id="location_id_from"
|
||||
name="location_id_from">
|
||||
<option></option>
|
||||
@foreach($locations as $location)
|
||||
<option value="{{ $location->id }}"
|
||||
data-is-freezer="{{ $location->is_freezer }}">{{ $location->name }}</option>
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->id }}" data-is-freezer="{{ $location->is_freezer }}">
|
||||
{{ $location->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="invalid-feedback">{{ $__t('A location is required') }}</div>
|
||||
</div>
|
||||
|
||||
@include('components.productamountpicker', array(
|
||||
@include('components.productamountpicker', [
|
||||
'value' => 1,
|
||||
'additionalHtmlContextHelp' => '<div id="tare-weight-handling-info"
|
||||
class="text-info font-italic d-none">' . $__t('Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated') . '</div>'
|
||||
))
|
||||
'additionalHtmlContextHelp' =>
|
||||
'<div id="tare-weight-handling-info"
|
||||
class="text-info font-italic d-none">' .
|
||||
$__t(
|
||||
'Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated'
|
||||
) .
|
||||
'</div>',
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<label for="location_id_to">{{ $__t('To location') }}</label>
|
||||
<select required
|
||||
class="custom-control custom-select location-combobox"
|
||||
id="location_id_to"
|
||||
{{-- TODO: Select2: dynamic data: locations --}}
|
||||
<select required class="custom-control custom-select location-combobox" id="location_id_to"
|
||||
name="location_id_to">
|
||||
<option></option>
|
||||
@foreach($locations as $location)
|
||||
<option value="{{ $location->id }}"
|
||||
data-is-freezer="{{ $location->is_freezer }}">{{ $location->name }}</option>
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->id }}" data-is-freezer="{{ $location->is_freezer }}">
|
||||
{{ $location->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="invalid-feedback">{{ $__t('A location is required') }}</div>
|
||||
|
|
@ -64,29 +65,22 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="form-check-input custom-control-input"
|
||||
type="checkbox"
|
||||
id="use_specific_stock_entry"
|
||||
name="use_specific_stock_entry"
|
||||
value="1">
|
||||
<input class="form-check-input custom-control-input" type="checkbox" id="use_specific_stock_entry"
|
||||
name="use_specific_stock_entry" value="1">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="use_specific_stock_entry">{{ $__t('Use a specific stock item') }}
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
title="{{ $__t('The first item in this list would be picked by the default rule which is "Opened first, then first due first, then first in first out"') }}"></i>
|
||||
</label>
|
||||
</div>
|
||||
<select disabled
|
||||
class="custom-control custom-select mt-2"
|
||||
id="specific_stock_entry"
|
||||
<select disabled class="custom-control custom-select mt-2" id="specific_stock_entry"
|
||||
name="specific_stock_entry">
|
||||
<option></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button id="save-transfer-button"
|
||||
class="btn btn-success">{{ $__t('OK') }}</button>
|
||||
<button id="save-transfer-button" class="btn btn-success">{{ $__t('OK') }}</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -94,5 +88,5 @@
|
|||
<div class="col-12 col-md-6 col-xl-4 hide-when-embedded">
|
||||
@include('components.productcard')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,21 +5,17 @@
|
|||
@section('viewJsName', 'userentities')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -32,66 +28,54 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="userentities-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: userentities --}}
|
||||
<table id="userentities-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#userentities-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#userentities-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
<th>{{ $__t('Caption') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($userentities as $userentity)
|
||||
@foreach ($userentities as $userentity)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm show-as-dialog-link"
|
||||
href="{{ $U('/userentity/') }}{{ $userentity->id }}?embedded"
|
||||
data-toggle="tooltip"
|
||||
href="{{ $U('/userentity/') }}{{ $userentity->id }}?embedded" data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm userentity-delete-button"
|
||||
href="#"
|
||||
<a class="btn btn-danger btn-sm userentity-delete-button" href="#"
|
||||
data-userentity-id="{{ $userentity->id }}"
|
||||
data-userentity-name="{{ $userentity->name }}"
|
||||
data-toggle="tooltip"
|
||||
data-userentity-name="{{ $userentity->name }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
|
|
@ -111,5 +95,5 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -1,48 +1,44 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@if($mode == 'edit')
|
||||
@section('title', $__t('Edit userfield'))
|
||||
@if ($mode == 'edit')
|
||||
@section('title', $__t('Edit userfield'))
|
||||
@else
|
||||
@section('title', $__t('Create userfield'))
|
||||
@section('title', $__t('Create userfield'))
|
||||
@endif
|
||||
|
||||
@section('viewJsName', 'userfieldform')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-12">
|
||||
<script>
|
||||
Grocy.EditMode = '{{ $mode }}';
|
||||
</script>
|
||||
|
||||
@if($mode == 'edit')
|
||||
@if ($mode == 'edit')
|
||||
<script>
|
||||
Grocy.EditObjectId = {{ $userfield->id }};
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<form id="userfield-form"
|
||||
novalidate>
|
||||
<form id="userfield-form" novalidate>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="entity">{{ $__t('Entity') }}</label>
|
||||
<select required
|
||||
class="custom-control custom-select"
|
||||
id="entity"
|
||||
name="entity">
|
||||
{{-- TODO: Select2: dynamic data: userentities --}}
|
||||
<select required class="custom-control custom-select" id="entity" name="entity">
|
||||
<option></option>
|
||||
@foreach($entities as $entity)
|
||||
<option @if($mode=='edit'
|
||||
&&
|
||||
$userfield->entity == $entity) selected="selected" @endif value="{{ $entity }}">{{ $entity }}</option>
|
||||
@foreach ($entities as $entity)
|
||||
<option @if ($mode == 'edit' && $userfield->entity == $entity) selected="selected" @endif
|
||||
value="{{ $entity }}">{{ $entity }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="invalid-feedback">{{ $__t('A entity is required') }}</div>
|
||||
|
|
@ -51,59 +47,50 @@
|
|||
<div class="form-group">
|
||||
<label for="name">
|
||||
{{ $__t('Name') }}
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('This is the internal field name, e. g. for the API') }}"></i>
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
required
|
||||
pattern="^[a-zA-Z0-9]*$"
|
||||
id="name"
|
||||
name="name"
|
||||
value="@if($mode == 'edit'){{ $userfield->name }}@endif">
|
||||
<div class="invalid-feedback">{{ $__t('This is required and can only contain letters and numbers') }}</div>
|
||||
<input type="text" class="form-control" required pattern="^[a-zA-Z0-9]*$" id="name" name="name"
|
||||
value="@if ($mode == 'edit') {{ $userfield->name }} @endif">
|
||||
<div class="invalid-feedback">{{ $__t('This is required and can only contain letters and numbers') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">
|
||||
{{ $__t('Caption') }}
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip" data-trigger="hover click"
|
||||
title="{{ $__t('This is used to display the field on the frontend') }}"></i>
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
required
|
||||
id="caption"
|
||||
name="caption"
|
||||
value="@if($mode == 'edit'){{ $userfield->caption }}@endif">
|
||||
<input type="text" class="form-control" required id="caption" name="caption"
|
||||
value="@if ($mode == 'edit') {{ $userfield->caption }} @endif">
|
||||
<div class="invalid-feedback">{{ $__t('A caption is required') }}</div>
|
||||
</div>
|
||||
|
||||
@php if($mode == 'edit' && !empty($userfield->sort_number)) { $value = $userfield->sort_number; } else { $value = ''; } @endphp
|
||||
@include('components.numberpicker', array(
|
||||
@php
|
||||
if ($mode == 'edit' && !empty($userfield->sort_number)) {
|
||||
$value = $userfield->sort_number;
|
||||
} else {
|
||||
$value = '';
|
||||
}
|
||||
@endphp
|
||||
@include('components.numberpicker', [
|
||||
'id' => 'sort_number',
|
||||
'label' => 'Sort number',
|
||||
'min' => 0,
|
||||
'value' => $value,
|
||||
'isRequired' => false,
|
||||
'hint' => $__t('Multiple Userfields will be ordered by that number on the input form')
|
||||
))
|
||||
'hint' => $__t('Multiple Userfields will be ordered by that number on the input form'),
|
||||
])
|
||||
|
||||
<div class="form-group">
|
||||
<label for="type">{{ $__t('Type') }}</label>
|
||||
<select required
|
||||
class="custom-control custom-select"
|
||||
id="type"
|
||||
name="type">
|
||||
{{-- TODO: Select2: static data --}}
|
||||
<select required class="custom-control custom-select" id="type" name="type">
|
||||
<option></option>
|
||||
@foreach($userfieldTypes as $userfieldType)
|
||||
<option @if($mode=='edit'
|
||||
&&
|
||||
$userfield->type == $userfieldType) selected="selected" @endif value="{{ $userfieldType }}">{{ $__t($userfieldType) }}</option>
|
||||
@foreach ($userfieldTypes as $userfieldType)
|
||||
<option @if ($mode == 'edit' && $userfield->type == $userfieldType) selected="selected" @endif
|
||||
value="{{ $userfieldType }}">{{ $__t($userfieldType) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="invalid-feedback">{{ $__t('A type is required') }}</div>
|
||||
|
|
@ -112,17 +99,18 @@
|
|||
<div class="form-group d-none">
|
||||
<label for="config">{{ $__t('Configuration') }} <span id="config-hint"
|
||||
class="small text-muted"></span></label>
|
||||
<textarea class="form-control"
|
||||
rows="10"
|
||||
id="config"
|
||||
name="config">@if($mode == 'edit'){{ $userfield->config }}@endif</textarea>
|
||||
<textarea class="form-control" rows="10" id="config" name="config">
|
||||
@if ($mode == 'edit')
|
||||
{{ $userfield->config }}
|
||||
@endif
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input @if($mode=='edit'
|
||||
&&
|
||||
$userfield->show_as_column_in_tables == 1) checked @endif class="form-check-input custom-control-input" type="checkbox" id="show_as_column_in_tables" name="show_as_column_in_tables" value="1">
|
||||
<input @if ($mode == 'edit' && $userfield->show_as_column_in_tables == 1) checked @endif
|
||||
class="form-check-input custom-control-input" type="checkbox" id="show_as_column_in_tables"
|
||||
name="show_as_column_in_tables" value="1">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="show_as_column_in_tables">{{ $__t('Show as column in tables') }}</label>
|
||||
</div>
|
||||
|
|
@ -130,24 +118,21 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input @if($mode=='edit'
|
||||
&&
|
||||
$userfield->input_required == 1) checked @endif class="form-check-input custom-control-input" type="checkbox" id="input_required" name="input_required" value="1">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="input_required">
|
||||
<input @if ($mode == 'edit' && $userfield->input_required == 1) checked @endif
|
||||
class="form-check-input custom-control-input" type="checkbox" id="input_required"
|
||||
name="input_required" value="1">
|
||||
<label class="form-check-label custom-control-label" for="input_required">
|
||||
{{ $__t('Mandatory') }}
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
<i class="fas fa-question-circle text-muted" data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
title="{{ $__t('When enabled, then this field must be filled on the destination form') }}"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="save-userfield-button"
|
||||
class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
<button id="save-userfield-button" class="btn btn-success">{{ $__t('Save') }}</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,49 +5,40 @@
|
|||
@section('viewJsName', 'userfields')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100 m-1 mt-md-0 mb-md-0 float-right"
|
||||
id="related-links">
|
||||
<a id="new-userfield-button"
|
||||
class="btn btn-primary responsive-button show-as-dialog-link"
|
||||
<a id="new-userfield-button" class="btn btn-primary responsive-button show-as-dialog-link"
|
||||
href="{{ $U('/userfield/new?embedded') }}">
|
||||
{{ $__t('Add') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
|
|
@ -55,10 +46,10 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('Entity') }}</span>
|
||||
</div>
|
||||
<select class="custom-control custom-select"
|
||||
id="entity-filter">
|
||||
{{-- TODO: Select2: dynamic data: userfields --}}
|
||||
<select class="custom-control custom-select" id="entity-filter">
|
||||
<option value="all">{{ $__t('All') }}</option>
|
||||
@foreach($entities as $entity)
|
||||
@foreach ($entities as $entity)
|
||||
<option value="{{ $entity }}">{{ $entity }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
|
@ -66,27 +57,22 @@
|
|||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="userfields-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: userfields --}}
|
||||
<table id="userfields-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#userfields-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#userfields-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th class="allow-grouping">{{ $__t('Entity') }}</th>
|
||||
<th>{{ $__t('Name') }}</th>
|
||||
|
|
@ -96,20 +82,17 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($userfields as $userfield)
|
||||
@foreach ($userfields as $userfield)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm show-as-dialog-link"
|
||||
href="{{ $U('/userfield/') }}{{ $userfield->id }}?embedded"
|
||||
data-toggle="tooltip"
|
||||
href="{{ $U('/userfield/') }}{{ $userfield->id }}?embedded" data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm userfield-delete-button"
|
||||
href="#"
|
||||
<a class="btn btn-danger btn-sm userfield-delete-button" href="#"
|
||||
data-userfield-id="{{ $userfield->id }}"
|
||||
data-userfield-name="{{ $userfield->name }}"
|
||||
data-toggle="tooltip"
|
||||
data-userfield-name="{{ $userfield->name }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
|
|
@ -134,5 +117,5 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
@section('viewJsName', 'userobjects')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title mr-2 order-0">
|
||||
|
|
@ -15,16 +15,12 @@
|
|||
<span class="text-muted small">{{ $userentity->description }}</span>
|
||||
</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -41,76 +37,71 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="userobjects-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: userobjects --}}
|
||||
<table id="userobjects-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right d-print-none"></th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($userobjects as $userobject)
|
||||
@foreach ($userobjects as $userobject)
|
||||
<tr>
|
||||
<td class="fit-content border-right d-print-none">
|
||||
<a class="btn btn-info btn-sm show-as-dialog-link"
|
||||
href="{{ $U('/userobject/' . $userentity->name . '/') }}{{ $userobject->id }}?embedded"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
data-toggle="tooltip" title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm userobject-delete-button"
|
||||
href="#"
|
||||
data-userobject-id="{{ $userobject->id }}"
|
||||
data-toggle="tooltip"
|
||||
<a class="btn btn-danger btn-sm userobject-delete-button" href="#"
|
||||
data-userobject-id="{{ $userobject->id }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $userobject->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$userobject->id
|
||||
),
|
||||
])
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
|
|
@ -5,28 +5,23 @@
|
|||
@section('viewJsName', 'users')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3" type="button"
|
||||
data-toggle="collapse" data-target="#related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100 m-1 mt-md-0 mb-md-0 float-right"
|
||||
id="related-links">
|
||||
<a class="btn btn-primary responsive-button"
|
||||
href="{{ $U('/user/new') }}">
|
||||
<a class="btn btn-primary responsive-button" href="{{ $U('/user/new') }}">
|
||||
{{ $__t('Add') }}
|
||||
</a>
|
||||
<a class="btn btn-outline-secondary m-1 mt-md-0 mb-md-0 float-right"
|
||||
|
|
@ -36,77 +31,62 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse d-md-flex" id="table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
</div>
|
||||
<input type="text"
|
||||
id="search"
|
||||
class="form-control"
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
<input type="text" id="search" class="form-control" placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
class="btn btn-sm btn-outline-info"
|
||||
href="#">
|
||||
<a id="clear-filter-button" class="btn btn-sm btn-outline-info" href="#">
|
||||
{{ $__t('Clear filter') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table id="users-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
{{-- TODO: DataTables: dynamic data: users --}}
|
||||
<table id="users-table" class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-right"><a class="text-muted change-table-columns-visibility-button"
|
||||
data-toggle="tooltip"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#users-table"
|
||||
href="#"><i class="fas fa-eye"></i></a>
|
||||
data-toggle="tooltip" data-toggle="tooltip" title="{{ $__t('Table options') }}"
|
||||
data-table-selector="#users-table" href="#"><i class="fas fa-eye"></i></a>
|
||||
</th>
|
||||
<th>{{ $__t('Username') }}</th>
|
||||
<th>{{ $__t('First name') }}</th>
|
||||
<th>{{ $__t('Last name') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
))
|
||||
@include('components.userfields_thead', [
|
||||
'userfields' => $userfields,
|
||||
])
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($users as $user)
|
||||
@foreach ($users as $user)
|
||||
<tr>
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-info btn-sm"
|
||||
href="{{ $U('/user/') }}{{ $user->id }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<a class="btn btn-info btn-sm" href="{{ $U('/user/') }}{{ $user->id }}"
|
||||
data-toggle="tooltip" title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-info btn-sm"
|
||||
href="{{ $U('/user/' . $user->id . '/permissions') }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Configure user permissions') }}">
|
||||
<a class="btn btn-info btn-sm" href="{{ $U('/user/' . $user->id . '/permissions') }}"
|
||||
data-toggle="tooltip" title="{{ $__t('Configure user permissions') }}">
|
||||
<i class="fas fa-lock"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm user-delete-button @if($user->id == GROCY_USER_ID) disabled @endif"
|
||||
href="#"
|
||||
data-user-id="{{ $user->id }}"
|
||||
data-user-username="{{ $user->username }}"
|
||||
data-toggle="tooltip"
|
||||
<a class="btn btn-danger btn-sm user-delete-button @if ($user->id == GROCY_USER_ID) disabled @endif"
|
||||
href="#" data-user-id="{{ $user->id }}"
|
||||
data-user-username="{{ $user->username }}" data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
|
|
@ -121,14 +101,18 @@
|
|||
{{ $user->last_name }}
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
@include('components.userfields_tbody', [
|
||||
'userfields' => $userfields,
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $user->id)
|
||||
))
|
||||
'userfieldValues' => FindAllObjectsInArrayByPropertyValue(
|
||||
$userfieldValues,
|
||||
'object_id',
|
||||
$user->id
|
||||
),
|
||||
])
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
|
|
|||
49
yarn.lock
49
yarn.lock
|
|
@ -238,7 +238,7 @@ datatables.net-bs4@1.10.16:
|
|||
datatables.net "1.10.16"
|
||||
jquery ">=1.7"
|
||||
|
||||
datatables.net-bs4@>=1.11.3, datatables.net-bs4@^1.10.22:
|
||||
datatables.net-bs4@>=1.11.3:
|
||||
version "1.11.4"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-bs4/-/datatables.net-bs4-1.11.4.tgz#caa82ab1a989bf1462f075b91213df865060d1ec"
|
||||
integrity sha512-4V2uSxFloX1jRIsy4eAt1INyp5M5Pq5SV017/naq3zpVKraaFwqFjLhtkx64UHGXqcPj7egvj27dVcdnNIKnNA==
|
||||
|
|
@ -246,7 +246,15 @@ datatables.net-bs4@>=1.11.3, datatables.net-bs4@^1.10.22:
|
|||
datatables.net ">=1.11.3"
|
||||
jquery ">=1.7"
|
||||
|
||||
datatables.net-colreorder-bs4@^1.5.2:
|
||||
datatables.net-bs4@^1.11.5:
|
||||
version "1.11.5"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-bs4/-/datatables.net-bs4-1.11.5.tgz#7767a27991c75716ce7fef1aa6a41d01bfeaefbd"
|
||||
integrity sha512-tYE9MnyWIW4P8nm41yQnCqW4dUPK3M7qsuxQY5rViFFKDEU0bUUNAnE9+69P5/4Exe4uyXaY05xFkD00niPZUA==
|
||||
dependencies:
|
||||
datatables.net ">=1.11.3"
|
||||
jquery ">=1.7"
|
||||
|
||||
datatables.net-colreorder-bs4@^1.5.5:
|
||||
version "1.5.5"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-colreorder-bs4/-/datatables.net-colreorder-bs4-1.5.5.tgz#52ca2f95148572583ad619d6c9cdb44f1952d355"
|
||||
integrity sha512-MGAJ/k/FeSK2Kccio5k0oBRacBpmaIKP2wXYfC64ONZFjOFxKBSmFevfg7yPdipYdYDwoQqDnmw0MpdIL7UUug==
|
||||
|
|
@ -255,7 +263,7 @@ datatables.net-colreorder-bs4@^1.5.2:
|
|||
datatables.net-colreorder ">=1.5.4"
|
||||
jquery ">=1.7"
|
||||
|
||||
datatables.net-colreorder@>=1.5.4, datatables.net-colreorder@^1.5.2:
|
||||
datatables.net-colreorder@>=1.5.4, datatables.net-colreorder@^1.5.5:
|
||||
version "1.5.5"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-colreorder/-/datatables.net-colreorder-1.5.5.tgz#0de93e460cba5eb0167c0c491a2da0c76a2e3b12"
|
||||
integrity sha512-AUwv5A/87I4hg7GY/WbhRrDhqng9b019jLvvKutHibSPCEtMDWqyNtuP0q8zYoquqU9UQ1/nqXLW/ld8TzIDYQ==
|
||||
|
|
@ -263,12 +271,12 @@ datatables.net-colreorder@>=1.5.4, datatables.net-colreorder@^1.5.2:
|
|||
datatables.net ">=1.11.3"
|
||||
jquery ">=1.7"
|
||||
|
||||
datatables.net-plugins@^1.10.20:
|
||||
version "1.11.4"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-plugins/-/datatables.net-plugins-1.11.4.tgz#8eb7915cd9f43ba8ba256b89e06917aa24d1ac41"
|
||||
integrity sha512-39yyyoCCavagE0mO1BFsrRPeak5BwOlbtSACdGpPNf3jG5Lm7D6vEUPPcpGy6eLxjCiG/orMxlAqb8E5lSZtoA==
|
||||
datatables.net-plugins@^1.11.5:
|
||||
version "1.11.5"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-plugins/-/datatables.net-plugins-1.11.5.tgz#c53ebbd0ab3473a08c6ae36eb1990a66de599b89"
|
||||
integrity sha512-+Rsf/fyLG8GyFqp7Bvd1ElqWGQO3NPsx2VADn9X8QaZbctshGVW0sqvR5V7iHHgY6OY1LR0+t6qIMhan9BM4gA==
|
||||
|
||||
datatables.net-rowgroup-bs4@^1.1.2:
|
||||
datatables.net-rowgroup-bs4@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-rowgroup-bs4/-/datatables.net-rowgroup-bs4-1.1.4.tgz#dd4fad888edea895acd06fe8cf66816809d78eec"
|
||||
integrity sha512-D0+LxraRjvV1RpPNtXJuW9Z4Jn90Sykb7ytJC/5eJsEYc9WnLUvxWEok7fqPpl3dWphQKg5ZbWpKG55Gd1IIXA==
|
||||
|
|
@ -277,7 +285,7 @@ datatables.net-rowgroup-bs4@^1.1.2:
|
|||
datatables.net-rowgroup ">=1.1.3"
|
||||
jquery ">=1.7"
|
||||
|
||||
datatables.net-rowgroup@>=1.1.3, datatables.net-rowgroup@^1.1.2:
|
||||
datatables.net-rowgroup@>=1.1.3, datatables.net-rowgroup@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-rowgroup/-/datatables.net-rowgroup-1.1.4.tgz#3eea91951d46f6c207d2e0c03cb3d635b7b09689"
|
||||
integrity sha512-Oe9mL3X8RXLOQZblJVWTYD0melyw3xoPeQ3T2x1k2guTFxob8/2caKuzn95oFJau6tvbhsvY/QneTaCzHRKnnQ==
|
||||
|
|
@ -285,7 +293,7 @@ datatables.net-rowgroup@>=1.1.3, datatables.net-rowgroup@^1.1.2:
|
|||
datatables.net ">=1.11.3"
|
||||
jquery ">=1.7"
|
||||
|
||||
datatables.net-select-bs4@^1.3.1:
|
||||
datatables.net-select-bs4@^1.3.4:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-select-bs4/-/datatables.net-select-bs4-1.3.4.tgz#07336260be0aa7741a61b82188350f4d1e422a02"
|
||||
integrity sha512-hXxxTwR9Mx8xwD55g8hNiLt035afguKZ9Ejsdm5/mo3wmm9ml7gs8QG5fJuMRwtrdP9EKcnjc54+zVoHymEcgw==
|
||||
|
|
@ -294,7 +302,7 @@ datatables.net-select-bs4@^1.3.1:
|
|||
datatables.net-select ">=1.3.3"
|
||||
jquery ">=1.7"
|
||||
|
||||
datatables.net-select@>=1.3.3, datatables.net-select@^1.3.1:
|
||||
datatables.net-select@>=1.3.3, datatables.net-select@^1.3.4:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-select/-/datatables.net-select-1.3.4.tgz#7970587a8d8db8ba70a4cccb89b8519bb518116d"
|
||||
integrity sha512-iQ/dBHIWkhfCBxzNdtef79seCNO1ZsA5zU0Uiw3R2mlwmjcJM1xn6pFNajke6SX7VnlzndGDHGqzzEljSqz4pA==
|
||||
|
|
@ -309,13 +317,20 @@ datatables.net@1.10.16:
|
|||
dependencies:
|
||||
jquery ">=1.7"
|
||||
|
||||
datatables.net@>=1.11.3, datatables.net@^1.10.22:
|
||||
datatables.net@>=1.11.3:
|
||||
version "1.11.4"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-1.11.4.tgz#5f3e1ec134fa532e794fbd47c13f8333d7a5c455"
|
||||
integrity sha512-z9LG4O0VYOYzp+rnArLExvnUWV8ikyWBcHYZEKDfVuz7BKxQdEq4a/tpO0Trbm+FL1+RY7UEIh+UcYNY/hwGxA==
|
||||
dependencies:
|
||||
jquery ">=1.7"
|
||||
|
||||
datatables.net@^1.11.5:
|
||||
version "1.11.5"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-1.11.5.tgz#858a69953a01e1d5b18786769802117b04b8e3c9"
|
||||
integrity sha512-nlFst2xfwSWaQgaOg5sXVG3cxYC0tH8E8d65289w9ROgF2TmLULOOpcdMpyxxUim/qEwVSEem42RjkTWEpr3eA==
|
||||
dependencies:
|
||||
jquery ">=1.7"
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
|
|
@ -686,6 +701,16 @@ safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
select2-theme-bootstrap4@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/select2-theme-bootstrap4/-/select2-theme-bootstrap4-1.0.1.tgz#aa476930988cb61b05c77d1173a937688d2bb4be"
|
||||
integrity sha512-gn9zN+ZppzsjRCQzHMfhDyizcCNlzbsB059qAXjyEaFYx7OA3677LYEIbYiGmliuF6y9Dbuae8X8Rk/Z4SBleQ==
|
||||
|
||||
select2@^4.0.13:
|
||||
version "4.0.13"
|
||||
resolved "https://registry.yarnpkg.com/select2/-/select2-4.0.13.tgz#0dbe377df3f96167c4c1626033e924372d8ef44d"
|
||||
integrity sha512-1JeB87s6oN/TDxQQYCvS5EFoQyvV6eYMZZ0AeA4tdFDYWN3BAGZ8npr17UBFddU0lgAt3H0yjX3X6/ekOj1yjw==
|
||||
|
||||
sprintf-js@^1.0.3, sprintf-js@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user