mirror of
https://github.com/grocy/grocy.git
synced 2025-08-17 19:16:37 +00:00
Squashed commit
Improve journal pages loading time (new date range filter) Various small style adjustments (meal plan page and others) Pulled German translations from Transifex Show the shopping list total value (closes #1309) Make it possible to copy recipes (closes #714) Implemented optional "auto decimal separator for price inputs" (closes #1345) Removed table grouped column fixed order restriction (closes #1402) Don't filter out style, class, id attributes of html text (closes #1298) Added product picture as column on the stock overview page (closes #1283) Added grocycodes also for chores and batteries (+ camera barcode scanning for /choretracking and /batterytracking, this now closes #221)
This commit is contained in:
@@ -1,27 +1,27 @@
|
||||
> ⚠️ The following PHP extensions are now additionally required: `json`, `intl`, `zlib`
|
||||
|
||||
> ⚠️ PHP 8 is now supported and from now on the only tested runtime version (but as of now, PHP 7.2 should still work).
|
||||
> ⚠️ PHP 8 is now supported and from now on the only tested runtime version (although currently PHP 7.2 should still work).
|
||||
|
||||
### New feature: (Own) Product and stock entry labels/barcodes ("grocycode")
|
||||
- Print own labels/barcodes for products and/or every stock entry and then scan that code on every place a product or stock entry can be selected
|
||||
### New feature: (Own) Product/stock entry/chores/batteries labels/barcodes ("grocycode")
|
||||
- Print own labels/barcodes for products/stock entries/chores/batteries and then scan that code on every place a product/stock entry/chore/battery can be selected
|
||||
- Can be printed (or downloaded) via
|
||||
- The product edit page
|
||||
- The context/more menu per line on the stock overview and stock entries page
|
||||
- Automatically on purchase (new option on the purchase page, defaults can be configured per product)
|
||||
- The product/chore/battery edit page
|
||||
- The context/more menu per line on the overview pages and for stock entries on the stock entries page
|
||||
- Automatically on purchase (new option on the purchase page, defaults can be configured per product) for stock entries
|
||||
- The used barcode type can be configured via the `config.php` option `GROCYCODE_TYPE`:
|
||||
- `1D` (default) will produce a `Code128` 1D barcode (supported by the integrated camera barcode scanner)
|
||||
- `2D` will produce a `DataMatrix` 2D barcode (currently not supported by the integrated camera barcode scanner, but can be probably printed smaller)
|
||||
- Label printer functionality can be enabled via the new feature flag `FEATURE_FLAG_LABELPRINTER` (defaults to disabled)
|
||||
- Label printer functionality can be enabled via the new feature flag `FEATURE_FLAG_LABEL_PRINTER` (defaults to disabled)
|
||||
- Label printer communication happens via WebHooks - see the new `LABEL_PRINTER*` `config.php` options
|
||||
- Those grocycodes can also be used without a label printer - you can view or download the pictures and print them manually
|
||||
- grocycodes can also be used without a label printer - you can view or download thm as pictures and print them manually
|
||||
- More information:
|
||||
- https://github.com/grocy/grocy/blob/master/docs/grocycode.md
|
||||
- https://github.com/grocy/grocy/blob/master/docs/label-printing.md
|
||||
- (Thanks a lot @mistressofjellyfish)
|
||||
- (Thanks a lot @mistressofjellyfish for the initial work on this)
|
||||
|
||||
### New feature: Shopping list thermal printer support
|
||||
- The shopping list can now be printed on a thermal printer
|
||||
- The printer must compatible to the `ESC/POS` protocol and needs to be locally attached or network reachable to/by the machine hosting grocy (so the server)
|
||||
- The printer must be compatible to the `ESC/POS` protocol and needs to be locally attached or network reachable to/by the machine hosting grocy (so the server)
|
||||
- See the new `TPRINTER*` `config.php` options to configure the printer connection and other options
|
||||
- => New button on the shopping list print dialog
|
||||
- Can be enabled via the new feature flag `FEATURE_FLAG_THERMAL_PRINTER` (defaults to disabled)
|
||||
@@ -31,11 +31,13 @@
|
||||
- Product barcodes are now enforced to be unique across products
|
||||
- On the stock overview page it's now also possible to search/filter by product barcodes (via the general search field)
|
||||
- The product picker on the consume and transfer page now only shows products which are currently in stock
|
||||
- Added a filter option to only show in-stock products on the stock overview and products list page (master data)
|
||||
- Added new columns on the stock overview page (hidden by default): Product description, product default location, parent product
|
||||
- Added a filter option to only show in-stock products on the stock overview and products list (master data) page
|
||||
- Added new columns on the stock overview page (hidden by default): Product description, product default location, parent product, product picture
|
||||
- Added a new product option "Should not be frozen" (defaults to disabled and only visible when `FEATURE_FLAG_STOCK_PRODUCT_FREEZING` is enabled)
|
||||
- When enabled, on moving the product to a freezer location (so when freezing it), a corresponding warning will be shown
|
||||
- Optimized that when opening a product which has "Default due days after opened" set, the resulting date now never extends the original due date
|
||||
- Added a new stock setting (top right corner settings menu) "Add decimal separator automatically for price inputs" (defaults to disabled)
|
||||
- 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
|
||||
- Fixed that editing stock entries was not possible
|
||||
- Fixed that consuming with Scan Mode was not possible
|
||||
- Fixed that the current stock total value (header of the stock overview page) didn't include decimal amounts (thanks @Ape)
|
||||
@@ -50,6 +52,7 @@
|
||||
### Shopping list improvements/fixes
|
||||
- The amount now defaults to `1` for adding items quicker
|
||||
- Added a status filter for only _done_ items
|
||||
- The total value is now also shown (based on "Last price (Total)" per item, displayed on the page header and only when `FEATURE_FLAG_STOCK_PRICE_TRACKING` is enabled)
|
||||
- Fixed that shopping list prints had a grey background (thanks @Forceu)
|
||||
- Fixed the form validation on the shopping list item page (thanks @Forceu)
|
||||
- Fixed that when adding products to the shopping list from the stock overview page, the used quantity unit was always the products default purchase QU (and not the selected one)
|
||||
@@ -60,6 +63,7 @@
|
||||
- Recipe printing improvements (thanks @Ape)
|
||||
- Calories are now always displayed per single serving (on the recipe and meal plan page)
|
||||
- The note of an ingredient will now also be added to the corresponding shopping list item when using "Put missing products on the shopping list"
|
||||
- It's now possible to copy a recipe (button/dropdown menu item per recipe)
|
||||
- Fixed that the recipe page was slow when there were a lot meal plan recipe entries
|
||||
- Fixed that "Only check if any amount is in stock" (recipe ingredient option) didn't work for stock amounts < 1
|
||||
- Fixed that when adding missing items to the shopping list, on the popup deselected items got also added
|
||||
@@ -72,6 +76,7 @@
|
||||
- Meal plan entries can now be visually marked as "done" (new button per entry)
|
||||
- This happens automatically on consuming a recipe/product from the meal plan page
|
||||
- It's now possible to copy all entries of a day to another day (in the dropdown of the add button in the header of each day column)
|
||||
- The "Display recipe" button was removed, instead clicking the recipe title now displays the recipe (and this now also works for products; shows the product card)
|
||||
- Fixed that stock fulfillment checking used the desired servings of the recipe (those selected on the recipes page, not them from the meal plan entry)
|
||||
|
||||
### Chores improvements/fixes
|
||||
@@ -92,6 +97,7 @@
|
||||
- The username attribute is now configurable
|
||||
- Filtering of accounts is now possible
|
||||
- => See the new `LDAP*` `config.php` options
|
||||
- Improved the page loading time of all journal pages (stock/chores/batteries) by adding a new date range filter
|
||||
- Some night mode style improvements (thanks @BlizzWave and @KTibow)
|
||||
- Help tooltips are now additionally also triggered by clicking on them (instead of only hovering them, which doesn't work on mobile / touch devices)
|
||||
- The camera barcode scanner now also supports Code 39 barcodes (used for example in Germany on pharma products (PZN)) (thanks @andreheuer)
|
||||
@@ -101,8 +107,9 @@
|
||||
|
||||
### API improvements/fixes
|
||||
> ❗ Numbers are now returned as numbers (so technically without quotes around them, were strings for nearly all endpoints before - should practically be no real difference)
|
||||
- Added a new API endpoint `/system/localization-strings` to get the localization strings (gettext JSON representation; in the by the user desired language)
|
||||
- The `GET /chores` endpoint now also returns the `next_execution_assigned_user` per chore (like the endpoint `GET /chores/{choreId}` already did for a single chore)
|
||||
- Added a new endpoint `/system/localization-strings` to get the localization strings (gettext JSON representation; in the by the user desired language)
|
||||
- Added a new endpoint `/recipes/{recipeId}/copy` to copy a recipe
|
||||
- The `GET /chores` endpoint now also returns the `next_execution_assigned_user` object per chore (like the endpoint `GET /chores/{choreId}` already did for a single chore)
|
||||
- The `GET /tasks` endpoint now also returns the assigned user and category object per task
|
||||
- Empty Userfields are now also returned (were previously omitted, endpoint `GET /objects/{entity}` and `GET /objects/{entity}/{objectId}`)
|
||||
- Fixed that due soon products with `due_type` = "Expiration date" were missing in `due_products` of the `/stock/volatile` endpoint
|
||||
|
@@ -148,6 +148,7 @@ DefaultUserSetting('product_presets_product_group_id', -1); // Default product g
|
||||
DefaultUserSetting('product_presets_qu_id', -1); // Default quantity unit id for new products (-1 means no quantity unit is preset)
|
||||
DefaultUserSetting('stock_decimal_places_amounts', 4); // Default decimal places allowed for amounts
|
||||
DefaultUserSetting('stock_decimal_places_prices', 2); // Default decimal places allowed for prices
|
||||
DefaultUserSetting('stock_auto_decimal_separator_prices', false);
|
||||
DefaultUserSetting('stock_due_soon_days', 5);
|
||||
DefaultUserSetting('stock_default_purchase_amount', 0);
|
||||
DefaultUserSetting('stock_default_consume_amount', 1);
|
||||
@@ -204,7 +205,7 @@ Setting('FEATURE_FLAG_TASKS', true);
|
||||
Setting('FEATURE_FLAG_BATTERIES', true);
|
||||
Setting('FEATURE_FLAG_EQUIPMENT', true);
|
||||
Setting('FEATURE_FLAG_CALENDAR', true);
|
||||
Setting('FEATURE_FLAG_LABELPRINTER', false);
|
||||
Setting('FEATURE_FLAG_LABEL_PRINTER', false);
|
||||
|
||||
// Sub feature flags
|
||||
Setting('FEATURE_FLAG_STOCK_PRICE_TRACKING', true);
|
||||
|
@@ -210,7 +210,8 @@ class BaseController
|
||||
{
|
||||
$htmlPurifierConfig = \HTMLPurifier_Config::createDefault();
|
||||
$htmlPurifierConfig->set('Cache.SerializerPath', GROCY_DATAPATH . '/viewcache');
|
||||
$htmlPurifierConfig->set('HTML.Allowed', 'div,b,strong,i,em,u,a[href|title|target],iframe[src|width|height|frameborder],ul,ol,li,p[style],br,span[style],img[width|height|alt|src],table[border|width|style],tbody,tr,td,th,blockquote');
|
||||
$htmlPurifierConfig->set('HTML.Allowed', 'div,b,strong,i,em,u,a[href|title|target],iframe[src|width|height|frameborder],ul,ol,li,p[style],br,span[style],img[width|height|alt|src],table[border|width|style],tbody,tr,td,th,blockquote,*[style|class|id]');
|
||||
$htmlPurifierConfig->set('Attr.EnableID', true);
|
||||
$htmlPurifierConfig->set('HTML.SafeIframe', true);
|
||||
$htmlPurifierConfig->set('CSS.AllowedProperties', 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align');
|
||||
$htmlPurifierConfig->set('URI.AllowedSchemes', ['data' => true, 'http' => true, 'https' => true]);
|
||||
|
@@ -3,6 +3,8 @@
|
||||
namespace Grocy\Controllers;
|
||||
|
||||
use Grocy\Controllers\Users\User;
|
||||
use Grocy\Helpers\WebhookRunner;
|
||||
use Grocy\Helpers\Grocycode;
|
||||
|
||||
class BatteriesApiController extends BaseApiController
|
||||
{
|
||||
@@ -62,6 +64,30 @@ class BatteriesApiController extends BaseApiController
|
||||
}
|
||||
}
|
||||
|
||||
public function BatteryPrintLabel(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
try
|
||||
{
|
||||
$battery = $this->getDatabase()->batteries()->where('id', $args['batteryId'])->fetch();
|
||||
|
||||
$webhookData = array_merge([
|
||||
'battery' => $battery->name,
|
||||
'grocycode' => (string)(new Grocycode(Grocycode::BATTERY, $args['batteryId'])),
|
||||
], GROCY_LABEL_PRINTER_PARAMS);
|
||||
|
||||
if (GROCY_LABEL_PRINTER_RUN_SERVER)
|
||||
{
|
||||
(new WebhookRunner())->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON);
|
||||
}
|
||||
|
||||
return $this->ApiResponse($response, $webhookData);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct(\DI\Container $container)
|
||||
{
|
||||
parent::__construct($container);
|
||||
|
@@ -2,6 +2,10 @@
|
||||
|
||||
namespace Grocy\Controllers;
|
||||
|
||||
use Grocy\Helpers\Grocycode;
|
||||
use jucksearm\barcode\lib\BarcodeFactory;
|
||||
use jucksearm\barcode\lib\DatamatrixFactory;
|
||||
|
||||
class BatteriesController extends BaseController
|
||||
{
|
||||
public function BatteriesList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
@@ -48,8 +52,25 @@ class BatteriesController 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)
|
||||
{
|
||||
$months = $request->getQueryParams()['months'];
|
||||
$where = "tracked_time > DATE(DATE('now', 'localtime'), '-$months months')";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default 2 years
|
||||
$where = "tracked_time > DATE(DATE('now', 'localtime'), '-24 months')";
|
||||
}
|
||||
|
||||
if (isset($request->getQueryParams()['battery']) && filter_var($request->getQueryParams()['battery'], FILTER_VALIDATE_INT) !== false)
|
||||
{
|
||||
$batteryId = $request->getQueryParams()['battery'];
|
||||
$where .= " AND battery_id = $batteryId";
|
||||
}
|
||||
|
||||
return $this->renderPage($response, 'batteriesjournal', [
|
||||
'chargeCycles' => $this->getDatabase()->battery_charge_cycles()->orderBy('tracked_time', 'DESC'),
|
||||
'chargeCycles' => $this->getDatabase()->battery_charge_cycles()->where($where)->orderBy('tracked_time', 'DESC'),
|
||||
'batteries' => $this->getDatabase()->batteries()->where('active = 1')->orderBy('name', 'COLLATE NOCASE')
|
||||
]);
|
||||
}
|
||||
@@ -75,6 +96,40 @@ class BatteriesController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
public function BatteryGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
$size = $request->getQueryParam('size', null);
|
||||
$gc = new Grocycode(Grocycode::BATTERY, $args['batteryId']);
|
||||
|
||||
if (GROCY_GROCYCODE_TYPE == '2D')
|
||||
{
|
||||
$png = (new DatamatrixFactory())->setCode((string) $gc)->setSize($size)->getDatamatrixPngData();
|
||||
}
|
||||
else
|
||||
{
|
||||
$png = (new BarcodeFactory())->setType('C128')->setCode((string) $gc)->setHeight($size)->getBarcodePngData();
|
||||
}
|
||||
|
||||
$isDownload = $request->getQueryParam('download', false);
|
||||
if ($isDownload)
|
||||
{
|
||||
$response = $response->withHeader('Content-Type', 'application/octet-stream')
|
||||
->withHeader('Content-Disposition', 'attachment; filename=grocycode.png')
|
||||
->withHeader('Content-Length', strlen($png))
|
||||
->withHeader('Cache-Control', 'no-cache')
|
||||
->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $response->withHeader('Content-Type', 'image/png')
|
||||
->withHeader('Content-Length', strlen($png))
|
||||
->withHeader('Cache-Control', 'no-cache')
|
||||
->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
}
|
||||
$response->getBody()->write($png);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function __construct(\DI\Container $container)
|
||||
{
|
||||
parent::__construct($container);
|
||||
|
@@ -3,6 +3,8 @@
|
||||
namespace Grocy\Controllers;
|
||||
|
||||
use Grocy\Controllers\Users\User;
|
||||
use Grocy\Helpers\WebhookRunner;
|
||||
use Grocy\Helpers\Grocycode;
|
||||
|
||||
class ChoresApiController extends BaseApiController
|
||||
{
|
||||
@@ -108,6 +110,30 @@ class ChoresApiController extends BaseApiController
|
||||
}
|
||||
}
|
||||
|
||||
public function ChorePrintLabel(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
try
|
||||
{
|
||||
$chore = $this->getDatabase()->chores()->where('id', $args['choreId'])->fetch();
|
||||
|
||||
$webhookData = array_merge([
|
||||
'chore' => $chore->name,
|
||||
'grocycode' => (string)(new Grocycode(Grocycode::CHORE, $args['choreId'])),
|
||||
], GROCY_LABEL_PRINTER_PARAMS);
|
||||
|
||||
if (GROCY_LABEL_PRINTER_RUN_SERVER)
|
||||
{
|
||||
(new WebhookRunner())->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON);
|
||||
}
|
||||
|
||||
return $this->ApiResponse($response, $webhookData);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct(\DI\Container $container)
|
||||
{
|
||||
parent::__construct($container);
|
||||
|
@@ -2,6 +2,10 @@
|
||||
|
||||
namespace Grocy\Controllers;
|
||||
|
||||
use Grocy\Helpers\Grocycode;
|
||||
use jucksearm\barcode\lib\BarcodeFactory;
|
||||
use jucksearm\barcode\lib\DatamatrixFactory;
|
||||
|
||||
class ChoresController extends BaseController
|
||||
{
|
||||
public function ChoreEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
@@ -59,8 +63,25 @@ 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)
|
||||
{
|
||||
$months = $request->getQueryParams()['months'];
|
||||
$where = "tracked_time > DATE(DATE('now', 'localtime'), '-$months months')";
|
||||
}
|
||||
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)
|
||||
{
|
||||
$choreId = $request->getQueryParams()['chore'];
|
||||
$where .= " AND chore_id = $choreId";
|
||||
}
|
||||
|
||||
return $this->renderPage($response, 'choresjournal', [
|
||||
'choresLog' => $this->getDatabase()->chores_log()->orderBy('tracked_time', 'DESC'),
|
||||
'choresLog' => $this->getDatabase()->chores_log()->where($where)->orderBy('tracked_time', 'DESC'),
|
||||
'chores' => $this->getDatabase()->chores()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||
'users' => $this->getDatabase()->users()->orderBy('username'),
|
||||
'userfields' => $this->getUserfieldsService()->GetFields('chores_log'),
|
||||
@@ -92,6 +113,40 @@ class ChoresController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
public function ChoreGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
$size = $request->getQueryParam('size', null);
|
||||
$gc = new Grocycode(Grocycode::CHORE, $args['choreId']);
|
||||
|
||||
if (GROCY_GROCYCODE_TYPE == '2D')
|
||||
{
|
||||
$png = (new DatamatrixFactory())->setCode((string) $gc)->setSize($size)->getDatamatrixPngData();
|
||||
}
|
||||
else
|
||||
{
|
||||
$png = (new BarcodeFactory())->setType('C128')->setCode((string) $gc)->setHeight($size)->getBarcodePngData();
|
||||
}
|
||||
|
||||
$isDownload = $request->getQueryParam('download', false);
|
||||
if ($isDownload)
|
||||
{
|
||||
$response = $response->withHeader('Content-Type', 'application/octet-stream')
|
||||
->withHeader('Content-Disposition', 'attachment; filename=grocycode.png')
|
||||
->withHeader('Content-Length', strlen($png))
|
||||
->withHeader('Cache-Control', 'no-cache')
|
||||
->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $response->withHeader('Content-Type', 'image/png')
|
||||
->withHeader('Content-Length', strlen($png))
|
||||
->withHeader('Cache-Control', 'no-cache')
|
||||
->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
}
|
||||
$response->getBody()->write($png);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function __construct(\DI\Container $container)
|
||||
{
|
||||
parent::__construct($container);
|
||||
|
@@ -63,6 +63,20 @@ class RecipesApiController extends BaseApiController
|
||||
}
|
||||
}
|
||||
|
||||
public function CopyRecipe(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return $this->ApiResponse($response, [
|
||||
'created_object_id' => $this->getRecipesService()->CopyRecipe($args['recipeId'])
|
||||
]);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct(\DI\Container $container)
|
||||
{
|
||||
parent::__construct($container);
|
||||
|
@@ -619,6 +619,8 @@ class StockApiController extends BaseApiController
|
||||
}
|
||||
|
||||
public function ProductPrintLabel(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
try
|
||||
{
|
||||
$product = $this->getDatabase()->products()->where('id', $args['productId'])->fetch();
|
||||
|
||||
@@ -634,8 +636,15 @@ class StockApiController extends BaseApiController
|
||||
|
||||
return $this->ApiResponse($response, $webhookData);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function StockEntryPrintLabel(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
try
|
||||
{
|
||||
$stockEntry = $this->getDatabase()->stock()->where('id', $args['entryId'])->fetch();
|
||||
$product = $this->getDatabase()->products()->where('id', $stockEntry->product_id)->fetch();
|
||||
@@ -647,7 +656,7 @@ class StockApiController extends BaseApiController
|
||||
|
||||
if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
$webhookData['duedate'] = $this->getLocalizationService()->__t('DD') . ': ' . $stockEntry->best_before_date;
|
||||
$webhookData['due_date'] = $this->getLocalizationService()->__t('DD') . ': ' . $stockEntry->best_before_date;
|
||||
}
|
||||
|
||||
if (GROCY_LABEL_PRINTER_RUN_SERVER)
|
||||
@@ -657,6 +666,11 @@ class StockApiController extends BaseApiController
|
||||
|
||||
return $this->ApiResponse($response, $webhookData);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function RemoveProductFromShoppingList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
|
@@ -35,10 +35,27 @@ 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)
|
||||
{
|
||||
$months = $request->getQueryParams()['months'];
|
||||
$where = "row_created_timestamp > DATE(DATE('now', 'localtime'), '-$months months')";
|
||||
}
|
||||
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)
|
||||
{
|
||||
$productId = $request->getQueryParams()['product'];
|
||||
$where .= " AND product_id = $productId";
|
||||
}
|
||||
|
||||
$usersService = $this->getUsersService();
|
||||
|
||||
return $this->renderPage($response, 'stockjournal', [
|
||||
'stockLog' => $this->getDatabase()->uihelper_stock_journal()->orderBy('row_created_timestamp', 'DESC'),
|
||||
'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(),
|
||||
@@ -176,9 +193,7 @@ class StockController extends BaseController
|
||||
public function ProductGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
$size = $request->getQueryParam('size', null);
|
||||
$product = $this->getDatabase()->products($args['productId']);
|
||||
|
||||
$gc = new Grocycode(Grocycode::PRODUCT, $product->id);
|
||||
$gc = new Grocycode(Grocycode::PRODUCT, $args['productId']);
|
||||
|
||||
if (GROCY_GROCYCODE_TYPE == '2D')
|
||||
{
|
||||
@@ -190,7 +205,6 @@ class StockController extends BaseController
|
||||
}
|
||||
|
||||
$isDownload = $request->getQueryParam('download', false);
|
||||
|
||||
if ($isDownload)
|
||||
{
|
||||
$response = $response->withHeader('Content-Type', 'application/octet-stream')
|
||||
@@ -489,7 +503,6 @@ class StockController extends BaseController
|
||||
}
|
||||
|
||||
$isDownload = $request->getQueryParam('download', false);
|
||||
|
||||
if ($isDownload)
|
||||
{
|
||||
$response = $response->withHeader('Content-Type', 'application/octet-stream')
|
||||
|
@@ -1,7 +1,7 @@
|
||||
Label printing
|
||||
====
|
||||
|
||||
To enable label printing, set `FEATURE_FLAG_LABELPRINTER` to `true`in your `config.php`. You also need to provide a webhook target that is responsible for printing.
|
||||
To enable label printing, set `FEATURE_FLAG_LABEL_PRINTER` to `true`in your `config.php`. You also need to provide a webhook target that is responsible for printing.
|
||||
|
||||
Why webhook?
|
||||
---
|
||||
@@ -28,7 +28,7 @@ Both methods fire this request upon printing:
|
||||
```
|
||||
POST /your/printing/api/endpoint HTTP/1.1
|
||||
|
||||
product=<productname>&grocycode=grocy:x:xxx&duedate=DD:%2021-06-09&...
|
||||
product=<productname>&grocycode=grocy:x:xxx&due_date=DD:%2021-06-09&...
|
||||
|
||||
```
|
||||
|
||||
|
@@ -1555,7 +1555,7 @@
|
||||
},
|
||||
"/stock/entry/{entryId}/printlabel": {
|
||||
"get": {
|
||||
"summary": "Prints the label of the given stock entry",
|
||||
"summary": "Prints the grocycode / stock entry label of the given entry on the configured label printer",
|
||||
"tags": [
|
||||
"Stock"
|
||||
],
|
||||
@@ -2285,7 +2285,7 @@
|
||||
},
|
||||
"/stock/products/{productId}/printlabel": {
|
||||
"get": {
|
||||
"summary": "Prints the product label of the given product",
|
||||
"summary": "Prints the grocycode label of the given product on the configured label printer",
|
||||
"tags": [
|
||||
"Stock"
|
||||
],
|
||||
@@ -3462,6 +3462,54 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/recipes/{recipeId}/copy": {
|
||||
"post": {
|
||||
"summary": "Copies a recipe",
|
||||
"tags": [
|
||||
"Recipes"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "recipeId",
|
||||
"required": true,
|
||||
"description": "A valid recipe id of the recipe to copy",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The operation was successful",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_object_id": {
|
||||
"type": "number",
|
||||
"format": "integer",
|
||||
"description": "The id of the created recipe"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "The operation was not successful (possible errors are: Invalid recipe id)",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error400"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/chores": {
|
||||
"get": {
|
||||
"summary": "Returns all chores incl. the next estimated execution time per chore",
|
||||
@@ -3688,6 +3736,48 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/chores/{choreId}/printlabel": {
|
||||
"get": {
|
||||
"summary": "Prints the grocycode label of the given chore on the configured label printer",
|
||||
"tags": [
|
||||
"Chores"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "choreId",
|
||||
"required": true,
|
||||
"description": "A valid chore id",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The operation was successful",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"description": "WebHook data"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "The operation was not successful (possible errors are: Not existing chore, error on WebHook execution)",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error400"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/batteries": {
|
||||
"get": {
|
||||
"summary": "Returns all batteries incl. the next estimated charge time per battery",
|
||||
@@ -3868,6 +3958,48 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/batteries/{batteryId}/printlabel": {
|
||||
"get": {
|
||||
"summary": "Prints the grocycode label of the given battery on the configured label printer",
|
||||
"tags": [
|
||||
"Batteries"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "batteryId",
|
||||
"required": true,
|
||||
"description": "A valid battery id",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The operation was successful",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"description": "WebHook data"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "The operation was not successful (possible errors are: Not existing battery, error on WebHook execution)",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error400"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tasks": {
|
||||
"get": {
|
||||
"summary": "Returns all tasks which are not done yet",
|
||||
|
@@ -1,7 +1,6 @@
|
||||
#
|
||||
# Translators:
|
||||
# Bernd Bestel <bernd@berrnd.de>, 2019
|
||||
# @RubenKelevra <ruben@freifunk-nrw.de>, 2021
|
||||
# Bernd Bestel <bernd@berrnd.de>, 2021
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
@@ -9,7 +8,7 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-05-01T17:59:17+00:00\n"
|
||||
"PO-Revision-Date: 2019-09-17 10:45+0000\n"
|
||||
"Last-Translator: @RubenKelevra <ruben@freifunk-nrw.de>, 2021\n"
|
||||
"Last-Translator: Bernd Bestel <bernd@berrnd.de>, 2021\n"
|
||||
"Language-Team: German (https://www.transifex.com/grocy/teams/93189/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -19,7 +18,7 @@ msgstr ""
|
||||
"X-Domain: grocy/chore_assignment_types\n"
|
||||
|
||||
msgid "no-assignment"
|
||||
msgstr "keine Zuweisung"
|
||||
msgstr "Niemandem zuweisen"
|
||||
|
||||
msgid "who-least-did-first"
|
||||
msgstr "Wer es am seltensten gemacht hat zuerst"
|
||||
|
@@ -1,7 +1,6 @@
|
||||
#
|
||||
# Translators:
|
||||
# Bernd Bestel <bernd@berrnd.de>, 2019
|
||||
# @RubenKelevra <ruben@freifunk-nrw.de>, 2021
|
||||
# Bernd Bestel <bernd@berrnd.de>, 2021
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
@@ -9,7 +8,7 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-05-01T17:59:17+00:00\n"
|
||||
"PO-Revision-Date: 2019-05-01 17:42+0000\n"
|
||||
"Last-Translator: @RubenKelevra <ruben@freifunk-nrw.de>, 2021\n"
|
||||
"Last-Translator: Bernd Bestel <bernd@berrnd.de>, 2021\n"
|
||||
"Language-Team: German (https://www.transifex.com/grocy/teams/93189/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -22,7 +21,7 @@ msgid "manually"
|
||||
msgstr "Manuell"
|
||||
|
||||
msgid "dynamic-regular"
|
||||
msgstr "Dynamisch-Regelmäßig"
|
||||
msgstr "Dynamisch regelmäßig"
|
||||
|
||||
msgid "daily"
|
||||
msgstr "Täglich"
|
||||
|
@@ -1,7 +1,6 @@
|
||||
#
|
||||
# Translators:
|
||||
# Bernd Bestel <bernd@berrnd.de>, 2020
|
||||
# @RubenKelevra <ruben@freifunk-nrw.de>, 2021
|
||||
# Bernd Bestel <bernd@berrnd.de>, 2021
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
@@ -9,7 +8,7 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-05-01T17:59:17+00:00\n"
|
||||
"PO-Revision-Date: 2019-05-01 17:42+0000\n"
|
||||
"Last-Translator: @RubenKelevra <ruben@freifunk-nrw.de>, 2021\n"
|
||||
"Last-Translator: Bernd Bestel <bernd@berrnd.de>, 2021\n"
|
||||
"Language-Team: German (https://www.transifex.com/grocy/teams/93189/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -19,7 +18,7 @@ msgstr ""
|
||||
"X-Domain: grocy/demo_data\n"
|
||||
|
||||
msgid "Cookies"
|
||||
msgstr "Kekse"
|
||||
msgstr "Cookies"
|
||||
|
||||
msgid "Chocolate"
|
||||
msgstr "Schokolade"
|
||||
@@ -91,7 +90,7 @@ msgid "Cheese"
|
||||
msgstr "Käse"
|
||||
|
||||
msgid "Cold cuts"
|
||||
msgstr "Wurst-Aufschnitt"
|
||||
msgstr "Aufschnitt"
|
||||
|
||||
msgid "Paprika"
|
||||
msgstr "Paprika"
|
||||
@@ -115,19 +114,19 @@ msgid "Warranty ends"
|
||||
msgstr "Garantie endet"
|
||||
|
||||
msgid "TV remote control"
|
||||
msgstr "TV-Fernbedienung"
|
||||
msgstr "TV Fernbedienung"
|
||||
|
||||
msgid "Alarm clock"
|
||||
msgstr "Wecker"
|
||||
|
||||
msgid "Heat remote control"
|
||||
msgstr "Fernbedienung der Heizung"
|
||||
msgstr "Fernbedienung Heizung"
|
||||
|
||||
msgid "Lawn mowed in the garden"
|
||||
msgstr "Rasen im Garten gemäht"
|
||||
|
||||
msgid "Some good snacks"
|
||||
msgstr "Gutes Knabberzeug"
|
||||
msgstr "Paar gute Snacks"
|
||||
|
||||
msgid "Pizza dough"
|
||||
msgstr "Pizzateig"
|
||||
@@ -163,10 +162,10 @@ msgid "Italian"
|
||||
msgstr "Italienisch"
|
||||
|
||||
msgid "This is the note content of the recipe ingredient"
|
||||
msgstr "Dies ist die Notiz zur Zutat"
|
||||
msgstr "Dies ist der Inhalt der Notiz der Zutat"
|
||||
|
||||
msgid "Demo User"
|
||||
msgstr "Demo-Benutzer"
|
||||
msgstr "Demo Benutzer"
|
||||
|
||||
msgid "Gram"
|
||||
msgid_plural "Grams"
|
||||
@@ -198,14 +197,13 @@ msgid "Fork and improve grocy"
|
||||
msgstr "grocy forken und verbessern"
|
||||
|
||||
msgid "Find a solution for what to do when I forget the door keys"
|
||||
msgstr ""
|
||||
"Eine Lösung finden, was zu tun ist, wenn ich die Türschlüssel vergesse"
|
||||
msgstr "Eine Lösung für \"Haustürschlüssel vergessen\" finden"
|
||||
|
||||
msgid "Sweets"
|
||||
msgstr "Süßigkeiten"
|
||||
|
||||
msgid "Bakery products"
|
||||
msgstr "Bäckerei-Produkte"
|
||||
msgstr "Bäckerei Produkte"
|
||||
|
||||
msgid "Tinned food"
|
||||
msgstr "Konservern"
|
||||
@@ -214,7 +212,7 @@ msgid "Butchery products"
|
||||
msgstr "Metzgerei"
|
||||
|
||||
msgid "Vegetables/Fruits"
|
||||
msgstr "Obst und Gemüse"
|
||||
msgstr "Obst/Gemüse"
|
||||
|
||||
msgid "Refrigerated products"
|
||||
msgstr "Kühlregal"
|
||||
@@ -294,7 +292,7 @@ msgstr[0] "Scheibe"
|
||||
msgstr[1] "Scheiben"
|
||||
|
||||
msgid "Example userentity"
|
||||
msgstr "Beispiel-Benutzerentität"
|
||||
msgstr "Beispiel Benutzerentität"
|
||||
|
||||
msgid "This is an example user entity..."
|
||||
msgstr "Dies ist eine Beispiel-Benutzerentität"
|
||||
@@ -303,7 +301,7 @@ msgid "Custom field"
|
||||
msgstr "Benutzerdefiniertes Feld"
|
||||
|
||||
msgid "Example field value..."
|
||||
msgstr "Beispiel-Feldwert..."
|
||||
msgstr "Beispiel Feldwert..."
|
||||
|
||||
msgid "Waffle rolls"
|
||||
msgstr "Waffelröllchen"
|
||||
@@ -330,7 +328,7 @@ msgid "current release"
|
||||
msgstr "aktuelles Release"
|
||||
|
||||
msgid "not yet released"
|
||||
msgstr "noch nicht erschienen"
|
||||
msgstr "noch nicht freigegeben"
|
||||
|
||||
msgid "Portuguese (Brazil)"
|
||||
msgstr "Portugiesisch (Brasilien)"
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Translators:
|
||||
# Bernd Bestel <bernd@berrnd.de>, 2020
|
||||
# @RubenKelevra <ruben@freifunk-nrw.de>, 2021
|
||||
# Bernd Bestel <bernd@berrnd.de>, 2021
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
@@ -9,7 +9,7 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-05-01T17:59:17+00:00\n"
|
||||
"PO-Revision-Date: 2020-08-29 16:33+0000\n"
|
||||
"Last-Translator: @RubenKelevra <ruben@freifunk-nrw.de>, 2021\n"
|
||||
"Last-Translator: Bernd Bestel <bernd@berrnd.de>, 2021\n"
|
||||
"Language-Team: German (https://www.transifex.com/grocy/teams/93189/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -36,23 +36,23 @@ msgstr "Benutzer anzeigen"
|
||||
|
||||
# Edit own user data / change own password
|
||||
msgid "USERS_EDIT_SELF"
|
||||
msgstr "Eigene Benutzerdaten bearbeiten und eigenes Passwort ändern"
|
||||
msgstr "Eigene Benutzerdaten bearbeiten / eigenes Passwort ändern"
|
||||
|
||||
# Undo charge cycle
|
||||
msgid "BATTERIES_UNDO_CHARGE_CYCLE"
|
||||
msgstr "Akku-Ladezyklus rückgängig machen"
|
||||
msgstr "Ladezyklus rückgängig machen"
|
||||
|
||||
# Track charge cycle
|
||||
msgid "BATTERIES_TRACK_CHARGE_CYCLE"
|
||||
msgstr "Akku-Ladezyklus erfassen"
|
||||
msgstr "Ladezyklus erfassen"
|
||||
|
||||
# Track execution
|
||||
msgid "CHORE_TRACK_EXECUTION"
|
||||
msgstr "Hausarbeit-Ausführung erfassen"
|
||||
msgstr "Ausführung erfassen"
|
||||
|
||||
# Undo execution
|
||||
msgid "CHORE_UNDO_EXECUTION"
|
||||
msgstr "Hausarbeit-Ausführung rückgängig machen"
|
||||
msgstr "Ausführung rückgängig machen"
|
||||
|
||||
# Edit master data
|
||||
msgid "MASTER_DATA_EDIT"
|
||||
@@ -60,15 +60,15 @@ msgstr "Stammdaten bearbeiten"
|
||||
|
||||
# Undo execution
|
||||
msgid "TASKS_UNDO_EXECUTION"
|
||||
msgstr "Aufgabe-Ausführung rückgängig machen"
|
||||
msgstr "Ausführung rückgängig machen"
|
||||
|
||||
# Mark completed
|
||||
msgid "TASKS_MARK_COMPLETED"
|
||||
msgstr "Aufgabe als erledigt markieren"
|
||||
msgstr "Als erledigt markieren"
|
||||
|
||||
# Edit stock entries
|
||||
msgid "STOCK_EDIT"
|
||||
msgstr "Lagerbestand bearbeiten"
|
||||
msgstr "Bestandseinträge bearbeiten"
|
||||
|
||||
# Transfer
|
||||
msgid "STOCK_TRANSFER"
|
||||
@@ -92,11 +92,11 @@ msgstr "Einkauf"
|
||||
|
||||
# Add items
|
||||
msgid "SHOPPINGLIST_ITEMS_ADD"
|
||||
msgstr "Einträge zur Einkaufsliste hinzufügen"
|
||||
msgstr "Eintrag hinzufügen"
|
||||
|
||||
# Remove items
|
||||
msgid "SHOPPINGLIST_ITEMS_DELETE"
|
||||
msgstr "Einträge von Einkaufsliste entfernen"
|
||||
msgstr "Eintrag entfernen"
|
||||
|
||||
# User management
|
||||
msgid "USERS"
|
||||
@@ -104,11 +104,11 @@ msgstr "Benutzerverwaltung"
|
||||
|
||||
# Stock
|
||||
msgid "STOCK"
|
||||
msgstr "Lager"
|
||||
msgstr "Bestand"
|
||||
|
||||
# Shopping list
|
||||
msgid "SHOPPINGLIST"
|
||||
msgstr "Einkaufsliste"
|
||||
msgstr "Einkaufszettel"
|
||||
|
||||
# Chores
|
||||
msgid "CHORES"
|
||||
@@ -116,7 +116,7 @@ msgstr "Hausarbeiten"
|
||||
|
||||
# Batteries
|
||||
msgid "BATTERIES"
|
||||
msgstr "Akkus"
|
||||
msgstr "Batterien"
|
||||
|
||||
# Tasks
|
||||
msgid "TASKS"
|
||||
@@ -128,7 +128,7 @@ msgstr "Rezepte"
|
||||
|
||||
# Equipment
|
||||
msgid "EQUIPMENT"
|
||||
msgstr "Haushaltsgeräte"
|
||||
msgstr "Ausstattung"
|
||||
|
||||
# Calendar
|
||||
msgid "CALENDAR"
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -2084,13 +2084,11 @@ msgstr ""
|
||||
msgid "Download"
|
||||
msgstr ""
|
||||
|
||||
msgid "Download stock entry grocycode"
|
||||
# Example: Download *Product* grocycode
|
||||
msgid "Download %s grocycode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Download product grocycode"
|
||||
msgstr ""
|
||||
|
||||
msgid "grocycode is a unique referer to this product in your grocy instance - print it onto a label and scan it like any other barcode"
|
||||
msgid "grocycode is a unique referer to this %s in your grocy instance - print it onto a label and scan it like any other barcode"
|
||||
msgstr ""
|
||||
|
||||
# Abbreviation for "due date"
|
||||
@@ -2121,13 +2119,11 @@ msgstr ""
|
||||
msgid "Error while executing WebHook"
|
||||
msgstr ""
|
||||
|
||||
msgid "Print product grocycode on label printer"
|
||||
# Example: Print *Product* grocycode on label printer
|
||||
msgid "Print %s grocycode on label printer"
|
||||
msgstr ""
|
||||
|
||||
msgid "Print stock entry grocycode on label printer"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open stock entry print label in new window"
|
||||
msgid "Open stock entry label in new window"
|
||||
msgstr ""
|
||||
|
||||
msgid "Thermal printer"
|
||||
@@ -2193,3 +2189,37 @@ msgstr ""
|
||||
|
||||
msgid "Add recipe"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy this day"
|
||||
msgstr ""
|
||||
|
||||
msgid "Date range"
|
||||
msgstr ""
|
||||
|
||||
msgid "%s month"
|
||||
msgid_plural "%s months"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%s year"
|
||||
msgid_plural "%s years"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "Display product"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy recipe"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy of %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add decimal separator automatically for price inputs"
|
||||
msgstr ""
|
||||
|
||||
msgid "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"
|
||||
msgstr ""
|
||||
|
||||
msgid "Stock entry"
|
||||
msgstr ""
|
||||
|
@@ -33,7 +33,8 @@ SELECT
|
||||
p.description as product_description,
|
||||
l.name AS product_default_location_name,
|
||||
p_parent.id AS parent_product_id,
|
||||
p_parent.name AS parent_product_name
|
||||
p_parent.name AS parent_product_name,
|
||||
p.picture_file_name AS product_picture_file_name
|
||||
FROM (
|
||||
SELECT *
|
||||
FROM stock_current
|
||||
@@ -92,7 +93,8 @@ SELECT
|
||||
p.description AS product_description,
|
||||
l.name AS product_default_location_name,
|
||||
p_parent.id AS parent_product_id,
|
||||
p_parent.name AS parent_product_name
|
||||
p_parent.name AS parent_product_name,
|
||||
p.picture_file_name AS product_picture_file_name
|
||||
FROM (
|
||||
SELECT *
|
||||
FROM stock_current
|
||||
|
28
migrations/0147.sql
Normal file
28
migrations/0147.sql
Normal file
@@ -0,0 +1,28 @@
|
||||
DROP VIEW uihelper_stock_journal;
|
||||
CREATE VIEW uihelper_stock_journal
|
||||
AS
|
||||
SELECT
|
||||
sl.id,
|
||||
sl.row_created_timestamp,
|
||||
sl.correlation_id,
|
||||
sl.undone,
|
||||
sl.undone_timestamp,
|
||||
sl.transaction_type,
|
||||
sl.spoiled,
|
||||
sl.amount,
|
||||
sl.location_id,
|
||||
l.name AS location_name,
|
||||
p.name AS product_name,
|
||||
qu.name AS qu_name,
|
||||
qu.name_plural AS qu_name_plural,
|
||||
u.display_name AS user_display_name,
|
||||
p.id AS product_id
|
||||
FROM stock_log sl
|
||||
JOIN users_dto u
|
||||
ON sl.user_id = u.id
|
||||
JOIN products p
|
||||
ON sl.product_id = p.id
|
||||
JOIN locations l
|
||||
ON sl.location_id = l.id
|
||||
JOIN quantity_units qu
|
||||
ON p.qu_id_stock = qu.id;
|
@@ -270,7 +270,6 @@ a:not([href]) {
|
||||
padding: 0;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Barcodescanner Quagga */
|
||||
@@ -318,6 +317,10 @@ a:not([href]) {
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.ls-n1 {
|
||||
letter-spacing: -0.1rem;
|
||||
}
|
||||
|
||||
.text-larger {
|
||||
font-size: 125%;
|
||||
}
|
||||
|
@@ -681,7 +681,7 @@ $("textarea.wysiwyg-editor").summernote({
|
||||
|
||||
function LoadImagesLazy()
|
||||
{
|
||||
$(".lazy").Lazy({
|
||||
$(".lazy:visible").Lazy({
|
||||
enableThrottle: true,
|
||||
throttle: 500
|
||||
});
|
||||
@@ -801,13 +801,6 @@ $.extend(true, $.fn.dataTable.defaults, {
|
||||
if ("dataSrc" in rowGroup)
|
||||
{
|
||||
api.rowGroup().dataSrc(rowGroup.dataSrc);
|
||||
|
||||
// Apply fixed order for group column
|
||||
var fixedOrder = {
|
||||
pre: [rowGroup.dataSrc, 'asc']
|
||||
};
|
||||
|
||||
api.order.fixed(fixedOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1105,7 +1098,7 @@ $(document).on("click", ".change-table-columns-rowgroup-toggle", function()
|
||||
dataTable.rowGroup().enable(false);
|
||||
|
||||
// Remove fixed order
|
||||
dataTable.order.fixed({});
|
||||
//dataTable.order.fixed({});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1116,12 +1109,6 @@ $(document).on("click", ".change-table-columns-rowgroup-toggle", function()
|
||||
|
||||
dataTable.rowGroup().enable(true);
|
||||
dataTable.rowGroup().dataSrc(columnIndex);
|
||||
|
||||
// Apply fixed order for group column
|
||||
var fixedOrder = {
|
||||
pre: [columnIndex, 'asc']
|
||||
};
|
||||
dataTable.order.fixed(fixedOrder);
|
||||
}
|
||||
|
||||
var settingKey = 'datatables_rowGroup_' + dataTable.settings()[0].sTableId;
|
||||
|
@@ -1,5 +1,4 @@
|
||||
var batteriesJournalTable = $('#batteries-journal-table').DataTable({
|
||||
'paginate': true,
|
||||
'order': [[2, 'desc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
@@ -12,13 +11,16 @@ batteriesJournalTable.columns.adjust().draw();
|
||||
$("#battery-filter").on("change", function()
|
||||
{
|
||||
var value = $(this).val();
|
||||
var text = $("#battery-filter option:selected").text();
|
||||
if (value === "all")
|
||||
{
|
||||
text = "";
|
||||
RemoveUriParam("battery");
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateUriParam("battery", value);
|
||||
}
|
||||
|
||||
batteriesJournalTable.column(1).search(text).draw();
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
$("#search").on("keyup", Delay(function()
|
||||
@@ -36,14 +38,27 @@ $("#clear-filter-button").on("click", function()
|
||||
{
|
||||
$("#search").val("");
|
||||
$("#battery-filter").val("all");
|
||||
batteriesJournalTable.column(1).search("").draw();
|
||||
batteriesJournalTable.search("").draw();
|
||||
$("#daterange-filter").val("24");
|
||||
|
||||
RemoveUriParam("months");
|
||||
RemoveUriParam("battery");
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
$("#daterange-filter").on("change", function()
|
||||
{
|
||||
UpdateUriParam("months", $(this).val());
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
if (typeof GetUriParam("battery") !== "undefined")
|
||||
{
|
||||
$("#battery-filter").val(GetUriParam("battery"));
|
||||
$("#battery-filter").trigger("change");
|
||||
}
|
||||
|
||||
if (typeof GetUriParam("months") !== "undefined")
|
||||
{
|
||||
$("#daterange-filter").val(GetUriParam("months"));
|
||||
}
|
||||
|
||||
$(document).on('click', '.undo-battery-execution-button', function(e)
|
||||
|
@@ -122,6 +122,21 @@ $(document).on("click", ".battery-name-cell", function(e)
|
||||
$("#batteriesoverview-batterycard-modal").modal("show");
|
||||
});
|
||||
|
||||
$(document).on('click', '.battery-grocycode-label-print', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
document.activeElement.blur();
|
||||
|
||||
var batteryId = $(e.currentTarget).attr('data-battery-id');
|
||||
Grocy.Api.Get('batteries/' + batteryId + '/printlabel', function(labelData)
|
||||
{
|
||||
if (Grocy.Webhooks.labelprinter !== undefined)
|
||||
{
|
||||
Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, labelData);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function RefreshStatistics()
|
||||
{
|
||||
var nextXDays = $("#info-due-batteries").data("next-x-days");
|
||||
|
@@ -83,6 +83,21 @@ $('#battery-form input').keydown(function(event)
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '.battery-grocycode-label-print', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
document.activeElement.blur();
|
||||
|
||||
var batteryId = $(e.currentTarget).attr('data-chore-id');
|
||||
Grocy.Api.Get('batteries/' + batteryId + '/printlabel', function(labelData)
|
||||
{
|
||||
if (Grocy.Webhooks.labelprinter !== undefined)
|
||||
{
|
||||
Grocy.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, labelData);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Grocy.Components.UserfieldsForm.Load();
|
||||
$('#name').focus();
|
||||
Grocy.FrontendHelpers.ValidateForm('battery-form');
|
||||
|
@@ -60,7 +60,8 @@ $('#battery_id').on('change', function(e)
|
||||
|
||||
$('.combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4'
|
||||
bsVersion: '4',
|
||||
clearIfNoMatch: false
|
||||
});
|
||||
|
||||
$('#battery_id').val('');
|
||||
@@ -97,6 +98,16 @@ $('#tracked_time').find('input').on('keypress', function(e)
|
||||
Grocy.FrontendHelpers.ValidateForm('batterytracking-form');
|
||||
});
|
||||
|
||||
$(document).on("Grocy.BarcodeScanned", function(e, barcode, target)
|
||||
{
|
||||
if (!(target == "@batterypicker" || target == "undefined" || target == undefined)) // Default target
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$('#battery_id_text_input').val(barcode).trigger('change');
|
||||
});
|
||||
|
||||
function UndoChargeCycle(chargeCycleId)
|
||||
{
|
||||
Grocy.Api.Post('batteries/charge-cycles/' + chargeCycleId.toString() + '/undo', {},
|
||||
@@ -110,3 +121,38 @@ function UndoChargeCycle(chargeCycleId)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$('#battery_id_text_input').on('blur', function(e)
|
||||
{
|
||||
if ($('#battery_id').hasClass("combobox-menu-visible"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var input = $('#battery_id_text_input').val().toString();
|
||||
var possibleOptionElement = [];
|
||||
|
||||
// grocycode handling
|
||||
if (input.startsWith("grcy"))
|
||||
{
|
||||
var gc = input.split(":");
|
||||
if (gc[1] == "b")
|
||||
{
|
||||
possibleOptionElement = $("#battery_id option[value=\"" + gc[2] + "\"]").first();
|
||||
}
|
||||
}
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
$('#battery_id').val(possibleOptionElement.val());
|
||||
$('#battery_id').data('combobox').refresh();
|
||||
$('#battery_id').trigger('change');
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#battery_id').val(null);
|
||||
$('#battery_id_text_input').val("");
|
||||
$('#battery_id').data('combobox').refresh();
|
||||
$('#battery_id').trigger('change');
|
||||
}
|
||||
});
|
||||
|
@@ -237,3 +237,18 @@ Grocy.Components.ProductPicker.GetPicker().on('change', 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.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, labelData);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@@ -1,5 +1,4 @@
|
||||
var choresJournalTable = $('#chores-journal-table').DataTable({
|
||||
'paginate': true,
|
||||
'order': [[2, 'desc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
@@ -12,13 +11,22 @@ choresJournalTable.columns.adjust().draw();
|
||||
$("#chore-filter").on("change", function()
|
||||
{
|
||||
var value = $(this).val();
|
||||
var text = $("#chore-filter option:selected").text();
|
||||
if (value === "all")
|
||||
{
|
||||
text = "";
|
||||
RemoveUriParam("chore");
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateUriParam("chore", value);
|
||||
}
|
||||
|
||||
choresJournalTable.column(1).search(text).draw();
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
$("#daterange-filter").on("change", function()
|
||||
{
|
||||
UpdateUriParam("months", $(this).val());
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
$("#search").on("keyup", Delay(function()
|
||||
@@ -36,14 +44,21 @@ $("#clear-filter-button").on("click", function()
|
||||
{
|
||||
$("#search").val("");
|
||||
$("#chore-filter").val("all");
|
||||
choresJournalTable.column(1).search("").draw();
|
||||
choresJournalTable.search("").draw();
|
||||
$("#daterange-filter").val("24");
|
||||
|
||||
RemoveUriParam("months");
|
||||
RemoveUriParam("chore");
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
if (typeof GetUriParam("chore") !== "undefined")
|
||||
{
|
||||
$("#chore-filter").val(GetUriParam("chore"));
|
||||
$("#chore-filter").trigger("change");
|
||||
}
|
||||
|
||||
if (typeof GetUriParam("months") !== "undefined")
|
||||
{
|
||||
$("#daterange-filter").val(GetUriParam("months"));
|
||||
}
|
||||
|
||||
$(document).on('click', '.undo-chore-execution-button', function(e)
|
||||
|
@@ -185,6 +185,21 @@ $(document).on("click", ".chore-name-cell", function(e)
|
||||
$("#choresoverview-chorecard-modal").modal("show");
|
||||
});
|
||||
|
||||
$(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.FrontendHelpers.RunWebhook(Grocy.Webhooks.labelprinter, labelData);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function RefreshStatistics()
|
||||
{
|
||||
var nextXDays = $("#info-due-chores").data("next-x-days");
|
||||
|
@@ -83,7 +83,8 @@ $('#chore_id').on('change', function(e)
|
||||
|
||||
$('.combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4'
|
||||
bsVersion: '4',
|
||||
clearIfNoMatch: false
|
||||
});
|
||||
|
||||
$('#chore_id_text_input').focus();
|
||||
@@ -113,6 +114,16 @@ $('#choretracking-form input').keydown(function(event)
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("Grocy.BarcodeScanned", function(e, barcode, target)
|
||||
{
|
||||
if (!(target == "@chorepicker" || target == "undefined" || target == undefined)) // Default target
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$('#chore_id_text_input').val(barcode).trigger('change');
|
||||
});
|
||||
|
||||
Grocy.Components.DateTimePicker.GetInputElement().on('keypress', function(e)
|
||||
{
|
||||
Grocy.FrontendHelpers.ValidateForm('choretracking-form');
|
||||
@@ -131,3 +142,38 @@ function UndoChoreExecution(executionId)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$('#chore_id_text_input').on('blur', function(e)
|
||||
{
|
||||
if ($('#chore_id').hasClass("combobox-menu-visible"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var input = $('#chore_id_text_input').val().toString();
|
||||
var possibleOptionElement = [];
|
||||
|
||||
// grocycode handling
|
||||
if (input.startsWith("grcy"))
|
||||
{
|
||||
var gc = input.split(":");
|
||||
if (gc[1] == "c")
|
||||
{
|
||||
possibleOptionElement = $("#chore_id option[value=\"" + gc[2] + "\"]").first();
|
||||
}
|
||||
}
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
$('#chore_id').val(possibleOptionElement.val());
|
||||
$('#chore_id').data('combobox').refresh();
|
||||
$('#chore_id').trigger('change');
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#chore_id').val(null);
|
||||
$('#chore_id_text_input').val("");
|
||||
$('#chore_id').data('combobox').refresh();
|
||||
$('#chore_id').trigger('change');
|
||||
}
|
||||
});
|
||||
|
@@ -92,3 +92,20 @@ $(".numberpicker").on("keydown", function(e)
|
||||
$(this).parent().find(".numberpicker-down-button").click();
|
||||
}
|
||||
});
|
||||
|
||||
$(".numberpicker.locale-number-input.locale-number-currency").on("blur", function()
|
||||
{
|
||||
if (BoolVal(Grocy.UserSettings.stock_auto_decimal_separator_prices))
|
||||
{
|
||||
var value = this.value.toString();
|
||||
var decimalPlaces = parseInt(Grocy.UserSettings.stock_decimal_places_prices);
|
||||
|
||||
if (value.length <= decimalPlaces)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var valueNew = parseFloat(value.substring(0, value.length - decimalPlaces) + '.' + value.slice(decimalPlaces * -1)).toLocaleString(undefined, { minimumFractionDigits: decimalPlaces, maximumFractionDigits: decimalPlaces });
|
||||
$(this).val(valueNew);
|
||||
}
|
||||
});
|
||||
|
@@ -84,7 +84,7 @@ Grocy.Components.ProductCard.Refresh = function(productId)
|
||||
|
||||
if (productDetails.last_price !== null)
|
||||
{
|
||||
$('#productcard-product-last-price').text(__t("%1$s per %2$s", Number.parseFloat(productDetails.last_price).toLocaleString() + ' ' + Grocy.Currency, productDetails.quantity_unit_stock.name));
|
||||
$('#productcard-product-last-price').text(__t("%1$s per %2$s", Number.parseFloat(productDetails.last_price).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }), productDetails.quantity_unit_stock.name));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -93,7 +93,7 @@ Grocy.Components.ProductCard.Refresh = function(productId)
|
||||
|
||||
if (productDetails.avg_price !== null)
|
||||
{
|
||||
$('#productcard-product-average-price').text(__t("%1$s per %2$s", Number.parseFloat(productDetails.avg_price).toLocaleString() + ' ' + Grocy.Currency, productDetails.quantity_unit_stock.name));
|
||||
$('#productcard-product-average-price').text(__t("%1$s per %2$s", Number.parseFloat(productDetails.avg_price).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency, minimumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_prices }), productDetails.quantity_unit_stock.name));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -149,18 +149,17 @@ $('#product_id_text_input').on('blur', function(e)
|
||||
var input = $('#product_id_text_input').val().toString();
|
||||
var possibleOptionElement = [];
|
||||
|
||||
// did we enter a grocycode?
|
||||
// grocycode handling
|
||||
if (input.startsWith("grcy"))
|
||||
{
|
||||
var gc = input.split(":");
|
||||
if (gc[1] == "p")
|
||||
{
|
||||
// find product id
|
||||
possibleOptionElement = $("#product_id option[value=\"" + gc[2] + "\"]").first();
|
||||
$("#product_id").data("grocycode", true);
|
||||
}
|
||||
}
|
||||
else // process barcode as usual
|
||||
else // Normal product barcode handling
|
||||
{
|
||||
possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + input + ",\"]").first();
|
||||
}
|
||||
|
@@ -32,10 +32,10 @@ var calendar = $("#calendar").fullCalendar({
|
||||
<div class="btn-group mr-2 my-1"> \
|
||||
<button type="button" class="btn btn-outline-dark btn-xs add-recipe-button" data-toggle="tooltip" title="' + __t('Add recipe') + '"><i class="fas fa-plus"></i></a></button> \
|
||||
<button type="button" class="btn btn-outline-dark btn-xs dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button> \
|
||||
<div class="dropdown-menu"> \
|
||||
<a class="dropdown-item add-note-button" href="#">' + __t('Add note') + '</a> \
|
||||
<a class="dropdown-item add-product-button" href="#">' + __t('Add product') + '</a> \
|
||||
<a class="dropdown-item copy-day-button" href="#">' + __t('Copy this day') + '</a> \
|
||||
<div class="table-inline-menu dropdown-menu"> \
|
||||
<a class="dropdown-item add-note-button" href="#"><span class="dropdown-item-text">' + __t('Add note') + '</span></a> \
|
||||
<a class="dropdown-item add-product-button" href="#"><span class="dropdown-item-text">' + __t('Add product') + '</span></a> \
|
||||
<a class="dropdown-item copy-day-button" href="#"><span class="dropdown-item-text">' + __t('Copy this day') + '</span></a> \
|
||||
</div> \
|
||||
</div>');
|
||||
|
||||
@@ -121,11 +121,11 @@ var calendar = $("#calendar").fullCalendar({
|
||||
var costsAndCaloriesPerServing = ""
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
costsAndCaloriesPerServing = '<h5 class="small text-truncate"><span class="locale-number locale-number-currency">' + resolvedRecipe.costs + '</span> / <span class="locale-number locale-number-generic">' + resolvedRecipe.calories + '</span> kcal ' + __t('per serving') + '<h5>';
|
||||
costsAndCaloriesPerServing = '<h5 class="small text-truncate mb-1"><span class="locale-number locale-number-currency">' + resolvedRecipe.costs + '</span> / <span class="locale-number locale-number-generic">' + resolvedRecipe.calories + '</span> kcal ' + __t('per serving') + '</h5>';
|
||||
}
|
||||
else
|
||||
{
|
||||
costsAndCaloriesPerServing = '<h5 class="small text-truncate"><span class="locale-number locale-number-generic">' + resolvedRecipe.calories + '</span> kcal ' + __t('per serving') + '<h5>';
|
||||
costsAndCaloriesPerServing = '<h5 class="small text-truncate mb-1"><span class="locale-number locale-number-generic">' + resolvedRecipe.calories + '</span> kcal ' + __t('per serving') + '</h5>';
|
||||
}
|
||||
|
||||
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK)
|
||||
@@ -136,16 +136,15 @@ var calendar = $("#calendar").fullCalendar({
|
||||
|
||||
element.html('\
|
||||
<div> \
|
||||
<h5 class="text-truncate ' + additionalTitleCssClasses + '">' + recipe.name + '<h5> \
|
||||
<h5 class="small text-truncate">' + __n(mealPlanEntry.recipe_servings, "%s serving", "%s servings") + '</h5> \
|
||||
<h5 class="small timeago-contextual text-truncate">' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '</h5> \
|
||||
<h5 class="text-truncate mb-1 cursor-link display-recipe-button ' + additionalTitleCssClasses + '" data-toggle="tooltip" title="' + __t("Display recipe") + '" data-recipe-id="' + recipe.id.toString() + '" data-recipe-name="' + recipe.name + '" data-mealplan-servings="' + mealPlanEntry.recipe_servings + '" data-recipe-type="' + recipe.type + '">' + recipe.name + '</h5> \
|
||||
<h5 class="small text-truncate mb-1">' + __n(mealPlanEntry.recipe_servings, "%s serving", "%s servings") + '</h5> \
|
||||
<h5 class="small timeago-contextual text-truncate mb-1">' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '</h5> \
|
||||
' + costsAndCaloriesPerServing + ' \
|
||||
<h5> \
|
||||
<a class="ml-1 btn btn-outline-info btn-xs edit-meal-plan-entry-button" href="#" data-toggle="tooltip" title="' + __t("Edit this item") + '"><i class="fas fa-edit"></i></a> \
|
||||
<a class="btn btn-outline-danger btn-xs remove-recipe-button" href="#" data-toggle="tooltip" title="' + __t("Delete this item") + '"><i class="fas fa-trash"></i></a> \
|
||||
<a class="ml-1 btn btn-outline-primary btn-xs recipe-order-missing-button ' + recipeOrderMissingButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Put missing products on shopping list") + '" data-recipe-id="' + recipe.id.toString() + '" data-mealplan-servings="' + mealPlanEntry.recipe_servings + '" data-recipe-name="' + recipe.name + '" data-recipe-type="' + recipe.type + '"><i class="fas fa-cart-plus"></i></a> \
|
||||
<a class="btn btn-outline-success btn-xs recipe-consume-button ' + recipeConsumeButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Consume all ingredients needed by this recipe") + '" data-recipe-id="' + internalShadowRecipe.id.toString() + '" data-mealplan-entry-id="' + mealPlanEntry.id.toString() + '" data-recipe-name="' + recipe.name + '" data-recipe-type="' + recipe.type + '"><i class="fas fa-utensils"></i></a> \
|
||||
<a class="ml-1 btn btn-outline-secondary btn-xs recipe-popup-button" href="#" data-toggle="tooltip" title="' + __t("Display recipe") + '" data-recipe-id="' + recipe.id.toString() + '" data-recipe-name="' + recipe.name + '" data-mealplan-servings="' + mealPlanEntry.recipe_servings + '" data-recipe-type="' + recipe.type + '"><i class="fas fa-eye"></i></a> \
|
||||
' + doneButtonHtml + ' \
|
||||
</h5> \
|
||||
</div>');
|
||||
@@ -166,11 +165,11 @@ var calendar = $("#calendar").fullCalendar({
|
||||
var costsAndCaloriesPerDay = ""
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-currency">' + dayRecipeResolved.costs + '</span> / <span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '<h5>';
|
||||
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-currency">' + dayRecipeResolved.costs + '</span> / <span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '</h5>';
|
||||
}
|
||||
else
|
||||
{
|
||||
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '<h5>';
|
||||
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '</h5>';
|
||||
}
|
||||
$(".fc-day-header[data-date='" + dayRecipeName + "']").append('<h5 id="day-summary-' + dayRecipeName + '" class="small text-truncate border-top pt-1 pb-0">' + costsAndCaloriesPerDay + '</h5>');
|
||||
}
|
||||
@@ -214,18 +213,18 @@ var calendar = $("#calendar").fullCalendar({
|
||||
var costsAndCaloriesPerServing = ""
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
costsAndCaloriesPerServing = '<h5 class="small text-truncate"><span class="locale-number locale-number-currency">' + productDetails.last_price * mealPlanEntry.product_amount + '</span> / <span class="locale-number locale-number-generic">' + productDetails.product.calories * mealPlanEntry.product_amount + '</span> kcal ' + '<h5>';
|
||||
costsAndCaloriesPerServing = '<h5 class="small text-truncate mb-1"><span class="locale-number locale-number-currency">' + productDetails.last_price * mealPlanEntry.product_amount + '</span> / <span class="locale-number locale-number-generic">' + productDetails.product.calories * mealPlanEntry.product_amount + '</span> kcal ' + '</h5>';
|
||||
}
|
||||
else
|
||||
{
|
||||
costsAndCaloriesPerServing = '<h5 class="small text-truncate"><span class="locale-number locale-number-generic">' + productDetails.product.calories * mealPlanEntry.product_amount + '</span> kcal ' + '<h5>';
|
||||
costsAndCaloriesPerServing = '<h5 class="small text-truncate mb-1"><span class="locale-number locale-number-generic">' + productDetails.product.calories * mealPlanEntry.product_amount + '</span> kcal ' + '</h5>';
|
||||
}
|
||||
|
||||
element.html('\
|
||||
<div> \
|
||||
<h5 class="text-truncate ' + additionalTitleCssClasses + '">' + productDetails.product.name + '<h5> \
|
||||
<h5 class="small text-truncate"><span class="locale-number locale-number-quantity-amount">' + mealPlanEntry.product_amount + "</span> " + __n(mealPlanEntry.product_amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural) + '</h5> \
|
||||
<h5 class="small timeago-contextual text-truncate">' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '</h5> \
|
||||
<h5 class="text-truncate mb-1 cursor-link display-product-button ' + additionalTitleCssClasses + '" data-toggle="tooltip" title="' + __t("Display product") + '" data-product-id="' + productDetails.product.id.toString() + '">' + productDetails.product.name + '</h5> \
|
||||
<h5 class="small text-truncate mb-1"><span class="locale-number locale-number-quantity-amount">' + mealPlanEntry.product_amount + "</span> " + __n(mealPlanEntry.product_amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural) + '</h5> \
|
||||
<h5 class="small timeago-contextual text-truncate mb-1">' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '</h5> \
|
||||
' + costsAndCaloriesPerServing + ' \
|
||||
<h5> \
|
||||
<a class="ml-1 btn btn-outline-danger btn-xs remove-product-button" href="#" data-toggle="tooltip" title="' + __t("Delete this item") + '"><i class="fas fa-trash"></i></a> \
|
||||
@@ -252,11 +251,11 @@ var calendar = $("#calendar").fullCalendar({
|
||||
var costsAndCaloriesPerDay = ""
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||
{
|
||||
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-currency">' + dayRecipeResolved.costs + '</span> / <span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '<h5>';
|
||||
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-currency">' + dayRecipeResolved.costs + '</span> / <span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '</h5>';
|
||||
}
|
||||
else
|
||||
{
|
||||
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '<h5>';
|
||||
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '</h5>';
|
||||
}
|
||||
$(".fc-day-header[data-date='" + dayRecipeName + "']").append('<h5 id="day-summary-' + dayRecipeName + '" class="small text-truncate border-top pt-1 pb-0">' + costsAndCaloriesPerDay + '</h5>');
|
||||
}
|
||||
@@ -266,7 +265,7 @@ var calendar = $("#calendar").fullCalendar({
|
||||
{
|
||||
element.html('\
|
||||
<div> \
|
||||
<h5 class="text-wrap text-break ' + additionalTitleCssClasses + '">' + mealPlanEntry.note + '<h5> \
|
||||
<h5 class="text-wrap text-break mb-1 ' + additionalTitleCssClasses + '">' + mealPlanEntry.note + '</h5> \
|
||||
<h5> \
|
||||
<a class="ml-1 btn btn-outline-danger btn-xs remove-note-button" href="#" data-toggle="tooltip" title="' + __t("Delete this item") + '"><i class="fas fa-trash"></i></a> \
|
||||
<a class="btn btn-outline-info btn-xs edit-meal-plan-entry-button" href="#" data-toggle="tooltip" title="' + __t("Delete this item") + '"><i class="fas fa-edit"></i></a> \
|
||||
@@ -831,7 +830,7 @@ $(document).on('click', '.recipe-consume-button', function(e)
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("click", ".recipe-popup-button", function(e)
|
||||
$(document).on("click", ".display-recipe-button", function(e)
|
||||
{
|
||||
// Remove the focus from the current button
|
||||
// to prevent that the tooltip stays until clicked anywhere else
|
||||
@@ -868,6 +867,16 @@ $(document).on("click", ".recipe-popup-button", function(e)
|
||||
);
|
||||
});
|
||||
|
||||
$(document).on("click", ".display-product-button", function(e)
|
||||
{
|
||||
// Remove the focus from the current button
|
||||
// to prevent that the tooltip stays until clicked anywhere else
|
||||
document.activeElement.blur();
|
||||
|
||||
Grocy.Components.ProductCard.Refresh($(e.currentTarget).attr('data-product-id'));
|
||||
$("#mealplan-productcard-modal").modal("show");
|
||||
});
|
||||
|
||||
$(document).on("click", ".mealplan-entry-done-button", function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
@@ -21,7 +21,8 @@ const swaggerUi = SwaggerUIBundle({
|
||||
],
|
||||
layout: 'StandaloneLayout',
|
||||
docExpansion: "list",
|
||||
defaultModelsExpandDepth: -1
|
||||
defaultModelsExpandDepth: -1,
|
||||
validatorUrl: false
|
||||
});
|
||||
|
||||
window.ui = swaggerUi;
|
||||
|
@@ -269,7 +269,6 @@ $("#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 },
|
||||
@@ -285,7 +284,6 @@ quConversionsTable.columns.adjust().draw();
|
||||
|
||||
var barcodeTable = $('#barcode-table').DataTable({
|
||||
'order': [[1, 'asc']],
|
||||
"orderFixed": [[1, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
{ 'searchable': false, "targets": 0 },
|
||||
@@ -302,7 +300,7 @@ $('#name').focus();
|
||||
$('.input-group-qu').trigger('change');
|
||||
Grocy.FrontendHelpers.ValidateForm('product-form');
|
||||
|
||||
$(document).on('click', '.stockentry-grocycode-product-label-print', function(e)
|
||||
$(document).on('click', '.product-grocycode-label-print', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
document.activeElement.blur();
|
||||
|
@@ -117,7 +117,7 @@ $('#save-purchase-button').on('click', function(e)
|
||||
}
|
||||
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), 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_LABELPRINTER)
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
{
|
||||
if (Grocy.Webhooks.labelprinter !== undefined)
|
||||
{
|
||||
@@ -126,7 +126,7 @@ $('#save-purchase-button').on('click', function(e)
|
||||
post_data.grocycode = 'grcy:p:' + jsonForm.product_id + ":" + result[0].stock_id
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
post_data.duedate = __t('DD') + ': ' + result[0].best_before_date
|
||||
post_data.due_date = __t('DD') + ': ' + result[0].best_before_date
|
||||
}
|
||||
|
||||
if (jsonForm.print_stock_label > 0)
|
||||
@@ -304,7 +304,7 @@ if (Grocy.Components.ProductPicker !== undefined)
|
||||
}
|
||||
}
|
||||
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABELPRINTER)
|
||||
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
{
|
||||
$("#print_stock_label").val(productDetails.product.default_print_stock_label);
|
||||
if (productDetails.product.allow_label_per_unit)
|
||||
|
@@ -77,7 +77,6 @@ $('.save-recipe').on('click', function(e)
|
||||
|
||||
var recipesPosTables = $('#recipes-pos-table').DataTable({
|
||||
'order': [[1, 'asc']],
|
||||
"orderFixed": [[4, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
{ 'searchable': false, "targets": 0 },
|
||||
|
@@ -61,6 +61,7 @@ $("a[data-toggle='tab']").on("shown.bs.tab", function(e)
|
||||
{
|
||||
var tabId = $(e.target).attr("id");
|
||||
window.localStorage.setItem("recipes_last_tab_id", tabId);
|
||||
LoadImagesLazy();
|
||||
});
|
||||
|
||||
$("#search").on("keyup", Delay(function()
|
||||
@@ -166,6 +167,24 @@ $(".recipe-delete").on('click', function(e)
|
||||
});
|
||||
});
|
||||
|
||||
$(".recipe-copy").on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
var objectId = $(e.currentTarget).attr('data-recipe-id');
|
||||
|
||||
Grocy.Api.Post("recipes/" + objectId.toString() + "/copy", {},
|
||||
function(result)
|
||||
{
|
||||
window.location.href = U('/recipes?recipe=' + result.created_object_id.toString());
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$(document).on('click', '.recipe-shopping-list', function(e)
|
||||
{
|
||||
var objectName = $(e.currentTarget).attr('data-recipe-name');
|
||||
@@ -346,3 +365,5 @@ if (window.location.hash === "#fullscreen")
|
||||
{
|
||||
$("#selectedRecipeToggleFullscreenButton").click();
|
||||
}
|
||||
|
||||
LoadImagesLazy();
|
||||
|
@@ -1,6 +1,5 @@
|
||||
var shoppingListTable = $('#shoppinglist-table').DataTable({
|
||||
'order': [[1, 'asc']],
|
||||
"orderFixed": [[3, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
{ 'searchable': false, "targets": 0 },
|
||||
|
@@ -125,7 +125,7 @@ $(document).on("click", ".stock-name-cell", function(e)
|
||||
$("#stockentry-productcard-modal").modal("show");
|
||||
});
|
||||
|
||||
$(document).on('click', '.stockentry-grocycode-stockentry-label-print', function(e)
|
||||
$(document).on('click', '.stockentry-grocycode-label-print', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
document.activeElement.blur();
|
||||
|
@@ -1,5 +1,4 @@
|
||||
var stockJournalTable = $('#stock-journal-table').DataTable({
|
||||
'paginate': true,
|
||||
'order': [[3, 'desc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
@@ -12,15 +11,16 @@ stockJournalTable.columns.adjust().draw();
|
||||
$("#product-filter").on("change", function()
|
||||
{
|
||||
var value = $(this).val();
|
||||
var text = $("#product-filter option:selected").text();
|
||||
if (value === "all")
|
||||
{
|
||||
stockJournalTable.column(1).search("").draw();
|
||||
RemoveUriParam("product");
|
||||
}
|
||||
else
|
||||
{
|
||||
stockJournalTable.column(1).search(">" + text + "<").draw();
|
||||
UpdateUriParam("product", value);
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
$("#transaction-type-filter").on("change", function()
|
||||
@@ -59,6 +59,12 @@ $("#user-filter").on("change", function()
|
||||
stockJournalTable.column(6).search(text).draw();
|
||||
});
|
||||
|
||||
$("#daterange-filter").on("change", function()
|
||||
{
|
||||
UpdateUriParam("months", $(this).val());
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
$("#search").on("keyup", Delay(function()
|
||||
{
|
||||
var value = $(this).val();
|
||||
@@ -77,17 +83,21 @@ $("#clear-filter-button").on("click", function()
|
||||
$("#location-filter").val("all");
|
||||
$("#user-filter").val("all");
|
||||
$("#product-filter").val("all");
|
||||
stockJournalTable.column(1).search("").draw();
|
||||
stockJournalTable.column(4).search("").draw();
|
||||
stockJournalTable.column(5).search("").draw();
|
||||
stockJournalTable.column(6).search("").draw();
|
||||
stockJournalTable.search("").draw();
|
||||
$("#daterange-filter").val("6");
|
||||
|
||||
RemoveUriParam("months");
|
||||
RemoveUriParam("product");
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
if (typeof GetUriParam("product") !== "undefined")
|
||||
{
|
||||
$("#product-filter").val(GetUriParam("product"));
|
||||
$("#product-filter").trigger("change");
|
||||
}
|
||||
|
||||
if (typeof GetUriParam("months") !== "undefined")
|
||||
{
|
||||
$("#daterange-filter").val(GetUriParam("months"));
|
||||
}
|
||||
|
||||
$(document).on('click', '.undo-stock-booking-button', function(e)
|
||||
|
@@ -1,5 +1,4 @@
|
||||
var journalSummaryTable = $('#stock-journal-summary-table').DataTable({
|
||||
'paginate': true,
|
||||
'order': [[1, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 },
|
||||
|
@@ -17,6 +17,7 @@
|
||||
{ 'visible': false, 'targets': 14 },
|
||||
{ 'visible': false, 'targets': 15 },
|
||||
{ 'visible': false, 'targets': 16 },
|
||||
{ 'visible': false, 'targets': 17 },
|
||||
{ "type": "num", "targets": 3 },
|
||||
{ "type": "html-num-fmt", "targets": 9 },
|
||||
{ "type": "html-num-fmt", "targets": 10 },
|
||||
@@ -29,6 +30,7 @@
|
||||
|
||||
$('#stock-overview-table tbody').removeClass("d-none");
|
||||
stockOverviewTable.columns.adjust().draw();
|
||||
LoadImagesLazy();
|
||||
|
||||
$("#location-filter").on("change", function()
|
||||
{
|
||||
@@ -104,7 +106,7 @@ $("#search").on("keyup", Delay(function()
|
||||
stockOverviewTable.search(value).draw();
|
||||
}, 200));
|
||||
|
||||
$(document).on('click', '.stockentry-grocycode-product-label-print', function(e)
|
||||
$(document).on('click', '.product-grocycode-label-print', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
document.activeElement.blur();
|
||||
|
@@ -27,6 +27,11 @@ if (BoolVal(Grocy.UserSettings.stock_default_consume_amount_use_quick_consume_am
|
||||
$("#stock_default_consume_amount").attr("disabled", "");
|
||||
}
|
||||
|
||||
if (BoolVal(Grocy.UserSettings.stock_auto_decimal_separator_prices))
|
||||
{
|
||||
$("#stock_auto_decimal_separator_prices").prop("checked", true);
|
||||
}
|
||||
|
||||
RefreshLocaleNumberInput();
|
||||
|
||||
$("#stock_default_consume_amount_use_quick_consume_amount").on("click", function()
|
||||
|
@@ -99,6 +99,7 @@ $app->group('', function (RouteCollectorProxy $group) {
|
||||
$group->get('/chores', '\Grocy\Controllers\ChoresController:ChoresList');
|
||||
$group->get('/chore/{choreId}', '\Grocy\Controllers\ChoresController:ChoreEditForm');
|
||||
$group->get('/choressettings', '\Grocy\Controllers\ChoresController:ChoresSettings');
|
||||
$group->get('/chore/{choreId}/grocycode', '\Grocy\Controllers\ChoresController:ChoreGrocycodeImage');
|
||||
}
|
||||
|
||||
// Battery routes
|
||||
@@ -110,6 +111,7 @@ $app->group('', function (RouteCollectorProxy $group) {
|
||||
$group->get('/batteries', '\Grocy\Controllers\BatteriesController:BatteriesList');
|
||||
$group->get('/battery/{batteryId}', '\Grocy\Controllers\BatteriesController:BatteryEditForm');
|
||||
$group->get('/batteriessettings', '\Grocy\Controllers\BatteriesController:BatteriesSettings');
|
||||
$group->get('/battery/{batteryId}/grocycode', '\Grocy\Controllers\BatteriesController:BatteryGrocycodeImage');
|
||||
}
|
||||
|
||||
// Task routes
|
||||
@@ -225,6 +227,7 @@ $app->group('/api', function (RouteCollectorProxy $group) {
|
||||
$group->get('/recipes/{recipeId}/fulfillment', '\Grocy\Controllers\RecipesApiController:GetRecipeFulfillment');
|
||||
$group->post('/recipes/{recipeId}/consume', '\Grocy\Controllers\RecipesApiController:ConsumeRecipe');
|
||||
$group->get('/recipes/fulfillment', '\Grocy\Controllers\RecipesApiController:GetRecipeFulfillment');
|
||||
$group->Post('/recipes/{recipeId}/copy', '\Grocy\Controllers\RecipesApiController:CopyRecipe');
|
||||
|
||||
// Chores
|
||||
$group->get('/chores', '\Grocy\Controllers\ChoresApiController:Current');
|
||||
@@ -232,6 +235,7 @@ $app->group('/api', function (RouteCollectorProxy $group) {
|
||||
$group->post('/chores/{choreId}/execute', '\Grocy\Controllers\ChoresApiController:TrackChoreExecution');
|
||||
$group->post('/chores/executions/{executionId}/undo', '\Grocy\Controllers\ChoresApiController:UndoChoreExecution');
|
||||
$group->post('/chores/executions/calculate-next-assignments', '\Grocy\Controllers\ChoresApiController:CalculateNextExecutionAssignments');
|
||||
$group->get('/chores/{choreId}/printlabel', '\Grocy\Controllers\ChoresApiController:ChorePrintLabel');
|
||||
|
||||
//Printing
|
||||
$group->get('/print/shoppinglist/thermal', '\Grocy\Controllers\PrintApiController:PrintShoppingListThermal');
|
||||
@@ -241,6 +245,7 @@ $app->group('/api', function (RouteCollectorProxy $group) {
|
||||
$group->get('/batteries/{batteryId}', '\Grocy\Controllers\BatteriesApiController:BatteryDetails');
|
||||
$group->post('/batteries/{batteryId}/charge', '\Grocy\Controllers\BatteriesApiController:TrackChargeCycle');
|
||||
$group->post('/batteries/charge-cycles/{chargeCycleId}/undo', '\Grocy\Controllers\BatteriesApiController:UndoChargeCycle');
|
||||
$group->get('/batteries/{batteryId}/printlabel', '\Grocy\Controllers\BatteriesApiController:BatteryPrintLabel');
|
||||
|
||||
// Tasks
|
||||
$group->get('/tasks', '\Grocy\Controllers\TasksApiController:Current');
|
||||
|
@@ -145,6 +145,7 @@ class DemoDataGeneratorService extends BaseService
|
||||
INSERT INTO meal_plan(day, recipe_id) VALUES ('{$saturdayThisWeek}', 2);
|
||||
INSERT INTO meal_plan(day, recipe_id) VALUES ('{$sundayThisWeek}', 4);
|
||||
INSERT INTO meal_plan(day, type, note) VALUES ('{$tuesdayThisWeek}', 'note', '{$this->__t_sql('This is a note')}');
|
||||
INSERT INTO meal_plan(day, type, product_id, product_amount) VALUES (DATE('{$mondayThisWeek}', '-1 days'), 'product', 3, 1);
|
||||
|
||||
INSERT INTO chores (name, period_type, period_days) VALUES ('{$this->__t_sql('Changed towels in the bathroom')}', 'manually', 5); --1
|
||||
INSERT INTO chores (name, period_type, period_days, assignment_type, assignment_config, next_execution_assigned_to_user_id) VALUES ('{$this->__t_sql('Cleaned the kitchen floor')}', 'dynamic-regular', 7, 'random', '1,2,3,4', 1); --2
|
||||
|
@@ -112,6 +112,23 @@ class RecipesService extends BaseService
|
||||
}
|
||||
}
|
||||
|
||||
public function CopyRecipe($recipeId)
|
||||
{
|
||||
if (!$this->RecipeExists($recipeId))
|
||||
{
|
||||
throw new \Exception('Recipe does not exist');
|
||||
}
|
||||
|
||||
$newName = $this->getLocalizationService()->__t('Copy of %s', $this->getDataBase()->recipes($recipeId)->name);
|
||||
|
||||
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO recipes (name, description, picture_file_name, base_servings, desired_servings, not_check_shoppinglist, type, product_id) SELECT \'' . $newName . '\', description, picture_file_name, base_servings, desired_servings, not_check_shoppinglist, type, product_id FROM recipes WHERE id = ' . $recipeId);
|
||||
$lastInsertId = $this->getDatabase()->lastInsertId();
|
||||
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO recipes_pos (recipe_id, product_id, amount, note, qu_id, only_check_single_unit_in_stock, ingredient_group, not_check_stock_fulfillment, variable_amount, price_factor) SELECT ' . $lastInsertId . ', product_id, amount, note, qu_id, only_check_single_unit_in_stock, ingredient_group, not_check_stock_fulfillment, variable_amount, price_factor FROM recipes_pos WHERE recipe_id = ' . $recipeId);
|
||||
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO recipes_nestings (recipe_id, includes_recipe_id, servings) SELECT ' . $lastInsertId . ', includes_recipe_id, servings FROM recipes_nestings WHERE recipe_id = ' . $recipeId);
|
||||
|
||||
return $lastInsertId;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
@@ -193,7 +193,7 @@ class StockService extends BaseService
|
||||
]);
|
||||
$stockRow->save();
|
||||
|
||||
if (GROCY_FEATURE_FLAG_LABELPRINTER && GROCY_LABEL_PRINTER_RUN_SERVER && $runWebhook)
|
||||
if (GROCY_FEATURE_FLAG_LABEL_PRINTER && GROCY_LABEL_PRINTER_RUN_SERVER && $runWebhook)
|
||||
{
|
||||
$reps = 1;
|
||||
if ($runWebhook == 2)
|
||||
@@ -208,7 +208,7 @@ class StockService extends BaseService
|
||||
|
||||
if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
$webhookData['duedate'] = $this->getLocalizationService()->__t('DD') . ': ' . $bestBeforeDate;
|
||||
$webhookData['due_date'] = $this->getLocalizationService()->__t('DD') . ': ' . $bestBeforeDate;
|
||||
}
|
||||
|
||||
$runner = new WebhookRunner();
|
||||
|
@@ -48,6 +48,22 @@
|
||||
</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-clock"></i> {{ $__t('Date range') }}</span>
|
||||
</div>
|
||||
<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="9999">{{ $__t('All') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right">
|
||||
<a id="clear-filter-button"
|
||||
@@ -81,7 +97,7 @@
|
||||
<tr id="charge-cycle-{{ $chargeCycleEntry->id }}-row"
|
||||
class="@if($chargeCycleEntry->undone == 1) text-muted @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-secondary btn-sm undo-battery-execution-button @if($chargeCycleEntry->undone == 1) disabled @endif permission-BATTERIES_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"
|
||||
|
@@ -145,6 +145,20 @@
|
||||
href="{{ $U('/battery/') }}{{ $currentBatteryEntry->battery_id }}?embedded">
|
||||
<span class="dropdown-item-text">{{ $__t('Edit battery') }}</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item stockentry-grocycode-link"
|
||||
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)
|
||||
<a class="dropdown-item battery-grocycode-label-print"
|
||||
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>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
@@ -95,4 +95,35 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2 border-top">
|
||||
<div class="col clearfix mt-2">
|
||||
<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('Battery')) }}"></i>
|
||||
</h4>
|
||||
<p>
|
||||
@if($mode == 'edit')
|
||||
<img src="{{ $U('/battery/' . $battery->id . '/grocycode?size=60') }}"
|
||||
class="float-lg-left">
|
||||
@endif
|
||||
</p>
|
||||
<p>
|
||||
<a class="btn btn-outline-primary btn-sm"
|
||||
href="{{ $U('/battery/' . $battery->id . '/grocycode?download=true') }}">{{ $__t('Download') }}</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<a class="btn btn-outline-primary btn-sm battery-grocycode-label-print"
|
||||
data-battery-id="{{ $battery->id }}"
|
||||
href="#">
|
||||
{{ $__t('Print on label printer') }}
|
||||
</a>
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
@@ -15,11 +15,17 @@
|
||||
novalidate>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="battery_id">{{ $__t('Battery') }}</label>
|
||||
<select class="form-control combobox"
|
||||
<label class="w-100"
|
||||
for="battery_id">
|
||||
{{ $__t('Battery') }}
|
||||
<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>
|
||||
required
|
||||
data-target="@batterypicker">
|
||||
<option value=""></option>
|
||||
@foreach($batteries as $battery)
|
||||
<option value="{{ $battery->id }}">{{ $battery->name }}</option>
|
||||
@@ -48,4 +54,6 @@
|
||||
@include('components.batterycard')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('components.barcodescanner')
|
||||
@stop
|
||||
|
@@ -289,5 +289,38 @@
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6 col-12 @if($mode == 'create') d-none @endif">
|
||||
<div class="row">
|
||||
<div class="col clearfix">
|
||||
<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('Chore')) }}"></i>
|
||||
</h4>
|
||||
<p>
|
||||
@if($mode == 'edit')
|
||||
<img src="{{ $U('/chore/' . $chore->id . '/grocycode?size=60') }}"
|
||||
class="float-lg-left">
|
||||
@endif
|
||||
</p>
|
||||
<p>
|
||||
<a class="btn btn-outline-primary btn-sm"
|
||||
href="{{ $U('/chore/' . $chore->id . '/grocycode?download=true') }}">{{ $__t('Download') }}</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<a class="btn btn-outline-primary btn-sm chore-grocycode-label-print"
|
||||
data-chore-id="{{ $chore->id }}"
|
||||
href="#">
|
||||
{{ $__t('Print on label printer') }}
|
||||
</a>
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
@@ -48,6 +48,22 @@
|
||||
</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-clock"></i> {{ $__t('Date range') }}</span>
|
||||
</div>
|
||||
<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="24">{{ $__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"
|
||||
@@ -88,7 +104,7 @@
|
||||
<tr id="chore-execution-{{ $choreLogEntry->id }}-row"
|
||||
class="@if($choreLogEntry->undone == 1) text-muted @endif">
|
||||
<td class="fit-content border-right">
|
||||
<a class="btn btn-secondary btn-sm undo-chore-execution-button permission-CHORE_UNDO_EXECUTION @if($choreLogEntry->undone == 1) disabled @endif"
|
||||
<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"
|
||||
|
@@ -168,6 +168,20 @@
|
||||
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 stockentry-grocycode-link"
|
||||
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)
|
||||
<a class="dropdown-item chore-grocycode-label-print"
|
||||
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>
|
||||
|
@@ -15,11 +15,17 @@
|
||||
novalidate>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="chore_id">{{ $__t('Chore') }}</label>
|
||||
<select class="form-control combobox"
|
||||
<label class="w-100"
|
||||
for="chore_id">
|
||||
{{ $__t('Chore') }}
|
||||
<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>
|
||||
required
|
||||
data-target="@chorepicker">
|
||||
<option value=""></option>
|
||||
@foreach($chores as $chore)
|
||||
<option value="{{ $chore->id }}">{{ $chore->name }}</option>
|
||||
@@ -67,4 +73,6 @@
|
||||
@include('components.chorecard')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('components.barcodescanner')
|
||||
@stop
|
||||
|
@@ -18,8 +18,7 @@
|
||||
@php if(empty($additionalGroupCssClasses)) { $additionalGroupCssClasses = ''; } @endphp
|
||||
@php if(empty($activateNumberPad)) { $activateNumberPad = false; } @endphp
|
||||
|
||||
<div class="datetimepicker-wrapper"
|
||||
class="form-group {{ $additionalGroupCssClasses }}">
|
||||
<div class="datetimepicker-wrapper form-group {{ $additionalGroupCssClasses }}">
|
||||
<label for="{{ $id }}">{{ $__t($label) }}
|
||||
@if(!empty($hint))
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
|
@@ -17,8 +17,7 @@
|
||||
@php if(empty($additionalAttributes)) { $additionalAttributes = ''; } @endphp
|
||||
@php if(empty($additionalGroupCssClasses)) { $additionalGroupCssClasses = ''; } @endphp
|
||||
|
||||
<div class="datetimepicker2-wrapper"
|
||||
class="form-group {{ $additionalGroupCssClasses }}">
|
||||
<div class="datetimepicker2-wrapper form-group {{ $additionalGroupCssClasses }}">
|
||||
<label for="{{ $id }}">{{ $__t($label) }}
|
||||
@if(!empty($hint))
|
||||
<i class="fas fa-question-circle text-muted"
|
||||
|
@@ -85,14 +85,22 @@
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-danger hide-when-embedded hide-on-fullscreen-card equipment-delete-button"
|
||||
<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 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 }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
data-equipment-name="{{ $equipmentItem->name }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Delete this item') }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ $equipmentItem->name }}
|
||||
|
@@ -97,7 +97,7 @@
|
||||
Grocy.LocalizationStrings = {!! $LocalizationStrings !!};
|
||||
Grocy.FeatureFlags = {!! json_encode($featureFlags) !!};
|
||||
Grocy.Webhooks = {
|
||||
@if(GROCY_FEATURE_FLAG_LABELPRINTER && !GROCY_LABEL_PRINTER_RUN_SERVER)
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER && !GROCY_LABEL_PRINTER_RUN_SERVER)
|
||||
"labelprinter" : {
|
||||
"hook" : "{{ GROCY_LABEL_PRINTER_WEBHOOK}}",
|
||||
"extra_data" : {!! json_encode(GROCY_LABEL_PRINTER_PARAMS) !!}
|
||||
|
@@ -21,6 +21,13 @@
|
||||
|
||||
.img-fluid {
|
||||
max-width: 90%;
|
||||
max-height: 140px;
|
||||
}
|
||||
|
||||
@media (min-width: 400px) {
|
||||
.table-inline-menu.dropdown-menu {
|
||||
width: 200px !important;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -220,4 +227,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
@@ -425,7 +425,7 @@
|
||||
'entity' => 'products'
|
||||
))
|
||||
|
||||
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input @if($mode=='edit'
|
||||
@@ -616,11 +616,11 @@
|
||||
<div class="col clearfix">
|
||||
<div class="title-related-links">
|
||||
<h4>
|
||||
{{ $__t('grocycode') }}
|
||||
<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 product in your grocy instance - print it onto a label and scan it like any other barcode') }}"></i>
|
||||
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('Product')) }}"></i>
|
||||
</h4>
|
||||
<p>
|
||||
@if($mode == 'edit')
|
||||
@@ -631,8 +631,8 @@
|
||||
<p>
|
||||
<a class="btn btn-outline-primary btn-sm"
|
||||
href="{{ $U('/product/' . $product->id . '/grocycode?download=true') }}">{{ $__t('Download') }}</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
|
||||
<a class="btn btn-outline-primary btn-sm stockentry-grocycode-product-label-print"
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<a class="btn btn-outline-primary btn-sm product-grocycode-label-print"
|
||||
data-product-id="{{ $product->id }}"
|
||||
href="#">
|
||||
{{ $__t('Print on label printer') }}
|
||||
|
@@ -148,7 +148,7 @@
|
||||
))
|
||||
@endif
|
||||
|
||||
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<div class="form-group">
|
||||
<label for="print_stock_label">{{ $__t('Stock entry label') }}</label>
|
||||
<select class="form-control"
|
||||
|
@@ -142,14 +142,28 @@
|
||||
title="{{ $__t('Edit this item') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-danger hide-when-embedded hide-on-fullscreen-card recipe-delete"
|
||||
<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 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 }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
data-recipe-name="{{ $recipe->name }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Delete this item') }}</span>
|
||||
</a>
|
||||
<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>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ $recipe->name }}
|
||||
|
@@ -21,6 +21,11 @@
|
||||
<h2 class="title mr-2 order-0">
|
||||
@yield('title')
|
||||
</h2>
|
||||
@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>
|
||||
@endif
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
@@ -313,7 +318,7 @@
|
||||
class="d-none mr-auto"></span>
|
||||
<button id="shopping-list-stock-add-workflow-skip-button"
|
||||
type="button"
|
||||
class="btn btn-primary"><i class="fas fa-angle-double-right"></i> {{ $__t('Skip') }}</button>
|
||||
class="btn btn-primary">{{ $__t('Skip') }}</button>
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Close') }}</button>
|
||||
|
@@ -202,21 +202,21 @@
|
||||
<a class="dropdown-item stockentry-grocycode-link"
|
||||
type="button"
|
||||
href="{{ $U('/stockentry/' . $stockEntry->id . '/grocycode?download=true') }}">
|
||||
{{ $__t('Download stock entry grocycode') }}
|
||||
{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Download %s grocycode', $__t('Stock entry'))) !!}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
|
||||
<a class="dropdown-item stockentry-grocycode-stockentry-label-print"
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<a class="dropdown-item stockentry-grocycode-label-print"
|
||||
data-stock-id="{{ $stockEntry->id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
{{ $__t('Print stock entry grocycode on label printer') }}
|
||||
{!! 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"
|
||||
href="{{ $U('/stockentry/' . $stockEntry->id . '/label') }}">
|
||||
{{ $__t('Open stock entry print label in new window') }}
|
||||
{{ $__t('Open stock entry label in new window') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -14,7 +14,7 @@
|
||||
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"
|
||||
<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">
|
||||
@@ -45,7 +45,7 @@
|
||||
placeholder="{{ $__t('Search') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-2">
|
||||
<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('Product') }}</span>
|
||||
@@ -73,7 +73,7 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-2">
|
||||
<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>
|
||||
@@ -87,7 +87,7 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-2">
|
||||
<div class="col-12 col-md-6 col-xl-2 mt-1">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fas fa-filter"></i> {{ $__t('User') }}</span>
|
||||
@@ -101,6 +101,22 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3 mt-1">
|
||||
<div class="input-group">
|
||||
<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">
|
||||
<option value="1">{{ $__n(1, '%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>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="float-right mt-1">
|
||||
<a id="clear-filter-button"
|
||||
@@ -139,7 +155,7 @@
|
||||
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-sm undo-stock-booking-button @if($stockLogEntry->undone == 1) disabled @endif"
|
||||
<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"
|
||||
|
@@ -173,6 +173,7 @@
|
||||
<th>{{ $__t('Product description') }}</th>
|
||||
<th>{{ $__t('Parent product') }}</th>
|
||||
<th>{{ $__t('Default location') }}</th>
|
||||
<th>{{ $__t('Product picture') }}</th>
|
||||
|
||||
@include('components.userfields_thead', array(
|
||||
'userfields' => $userfields
|
||||
@@ -306,14 +307,14 @@
|
||||
<a class="dropdown-item stockentry-grocycode-link"
|
||||
type="button"
|
||||
href="{{ $U('/product/' . $currentStockEntry->product_id . '/grocycode?download=true') }}">
|
||||
{{ $__t('Download product grocycode') }}
|
||||
{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Download %s grocycode', $__t('Product'))) !!}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
|
||||
<a class="dropdown-item stockentry-grocycode-product-label-print"
|
||||
@if(GROCY_FEATURE_FLAG_LABEL_PRINTER)
|
||||
<a class="dropdown-item product-grocycode-label-print"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
{{ $__t('Print product grocycode on label printer') }}
|
||||
{!! str_replace('grocycode', '<span class="ls-n1">grocycode</span>', $__t('Print %s grocycode on label printer', $__t('Product'))) !!}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
@@ -414,6 +415,12 @@
|
||||
<td>
|
||||
{{ $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') }}"
|
||||
class="lazy">
|
||||
@endif
|
||||
</td>
|
||||
|
||||
@include('components.userfields_tbody', array(
|
||||
'userfields' => $userfields,
|
||||
|
@@ -157,6 +157,23 @@
|
||||
'min' => 0,
|
||||
'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">
|
||||
{{ $__t('Add decimal separator automatically for price inputs') }}
|
||||
<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') }}"
|
||||
|
Reference in New Issue
Block a user