From b393998601b61a6696727efe36b012e9a9eed6d4 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Sun, 15 Nov 2020 19:53:44 +0100 Subject: [PATCH] Distinguish expiry/best before dates (closes #851) --- README.md | 2 +- changelog/60_UNRELEASED_2020-xx-xx.md | 27 ++++-- config-dist.php | 8 +- controllers/StockApiController.php | 16 ++-- controllers/StockController.php | 4 +- grocy.openapi.json | 38 ++++---- localization/strings.pot | 128 +++++++++++++++++--------- migrations/0103.sql | 1 + migrations/0105.sql | 22 +++-- migrations/0108.sql | 9 +- migrations/0115.sql | 2 +- public/css/grocy.css | 9 +- public/viewjs/consume.js | 2 +- public/viewjs/purchase.js | 6 +- public/viewjs/shoppinglist.js | 4 +- public/viewjs/stockentries.js | 17 +++- public/viewjs/stockoverview.js | 35 ++++--- public/viewjs/stocksettings.js | 6 +- public/viewjs/transfer.js | 2 +- routes.php | 2 +- services/CalendarService.php | 2 +- services/DemoDataGeneratorService.php | 3 +- services/StockService.php | 35 ++++--- views/batteriessettings.blade.php | 2 +- views/choressettings.blade.php | 2 +- views/consume.blade.php | 2 +- views/inventory.blade.php | 6 +- views/locationform.blade.php | 2 +- views/productform.blade.php | 63 +++++++++++-- views/purchase.blade.php | 18 ++-- views/recipes.blade.php | 2 +- views/shoppinglist.blade.php | 4 +- views/shoppinglistsettings.blade.php | 2 +- views/stockentries.blade.php | 9 +- views/stockentryform.blade.php | 6 +- views/stockoverview.blade.php | 24 +++-- views/stocksettings.blade.php | 14 +-- views/taskssettings.blade.php | 2 +- views/transfer.blade.php | 2 +- 39 files changed, 348 insertions(+), 192 deletions(-) diff --git a/README.md b/README.md index cd2c2cd3..ef34c586 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ The following shorthands are available: - Example: `20190417` will be converted to `2019-04-17` - `YYYYMMe` or `YYYYMM+` gets expanded to the end of the given month in the given year in proper notation - Example: `201807e` will be converted to `2018-07-31` -- `x` gets expanded to `2999-12-31` (which I use for products which never expire) +- `x` gets expanded to `2999-12-31` (which I use for products which are never overdue) - Down/up arrow keys will increase/decrease the date by 1 day - Right/left arrow keys will increase/decrease the date by 1 week - Shift + down/up arrow keys will increase/decrease the date by 1 month diff --git a/changelog/60_UNRELEASED_2020-xx-xx.md b/changelog/60_UNRELEASED_2020-xx-xx.md index 823f1ce7..5865287f 100644 --- a/changelog/60_UNRELEASED_2020-xx-xx.md +++ b/changelog/60_UNRELEASED_2020-xx-xx.md @@ -29,13 +29,23 @@ _- (Because the stock quantity unit is now the base for everything, it cannot be - (Thanks @fipwmaqzufheoxq92ebc for the initial work on this) ### Stock improvements/fixes +- Changes about best before dates: It's now possible to distinguish between best before dates and expiration dates: + - New product option "Due date type" (defaults to "Best before date") + - Wording changes: + - All current places where "Best before date" was used now use "Due date" + - Products with `Due date type = Best before date` (so all existing products) are "due" or "overdue" (they don't "expire" or are "expired") + - Products with `Due date type = Expiration date` (new option) can "expire" or are "expired" + - Color changes: + - Prducts which are due soon or expire soon are (still) highlighted in yellow + - Products which are overdue are highlighted in grey (there is is also a new filter button on the stock overview page for them) + - Products which are expired (new option) are highlighted in red - When creating a quantity unit conversion it's now possible to automatically create the inverse conversion (thanks @kriddles) -- On purchase there is now a warning shown, when the best before date of the purchased product is earlier than the next best before date in stock (enabled by default, can be disabled by a new stock setting (top right corner settings menu)) +- On purchase there is now a warning shown, when the due date of the purchased product is earlier than the next due date in stock (enabled by default, can be disabled by a new stock setting (top right corner settings menu)) - The amount to be used for the "quick consume/open buttons" on the stock overview page can now be configured per product (new product option "Quick consume amount", defaults to 1) - Products can now be duplicated (new button on the products list page, all fields will be preset from the copied product, except the name) - Optimized/clarified what the total/unit price is on the purchase page (thanks @kriddles) -- On the purchase page the amount field is now displayed above/before the best before date for better `TAB` handling (thanks @kriddles) -- Changed that when `FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING` is disabled, products now get internally a best before of "never expires" (aka `2999-12-31`) instead of today (thanks @kriddles) +- On the purchase page the amount field is now displayed above/before the due date for better `TAB` handling (thanks @kriddles) +- Changed that when `FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING` is disabled, products now get internally a due date of "never overdue" (aka `2999-12-31`) instead of today (thanks @kriddles) - Products can now be hidden instead of deleted to prevent problems / missing information on existing references (new checkbox on the product edit page) (thanks @kriddles) - On the stock journal page, it's now visible if a consume-booking was spoiled - It's now tracked who made a stock change (currently logged in user, visible on the stock journal page) (thanks @fipwmaqzufheoxq92ebc) @@ -53,7 +63,7 @@ _- (Because the stock quantity unit is now the base for everything, it cannot be - Fixed that when adding products through a product picker workflow and when the created products contains special characters, the product was not preselected on the previous page (thanks @Forceu) - Fixed that when editing a product the default store was not visible / always empty regardless if the product had one set (thanks @kriddles) - Fixed that `FEATURE_SETTING_STOCK_COUNT_OPENED_PRODUCTS_AGAINST_MINIMUM_STOCK_AMOUNT` (option to configure if opened products should be considered for minimum stock amounts) was not handled correctly (thanks @teddybeermaniac) -- Fixed that the "Expiring soon" sum (yellow filter button) on the stock overview page didn't include products which expire today (thanks @fipwmaqzufheoxq92ebc) +- Fixed that the "Due soon" sum (yellow filter button) on the stock overview page didn't include products which are due today (thanks @fipwmaqzufheoxq92ebc) - Fixed that the shopping cart icon on the stock overview page was also shown if the product was on an already deleted shopping list (if enabled) (thanks @fipwmaqzufheoxq92ebc) - Fixed that when editing a stock entry without a price, the price field was prefilled with `1` - Fixed that the location & product groups filter on the stock overview page used a contains search instead of an exact search @@ -73,7 +83,7 @@ _- (Because the stock quantity unit is now the base for everything, it cannot be ### Recipe improvements/fixes - It's now possible to print recipes (button next to the recipe title) (thanks @zsarnett) -- Changed that recipe costs are now based on the costs of the products picked by the default consume rule "First expiring first, then first in first out" (thanks @kriddles) +- Changed that recipe costs are now based on the costs of the products picked by the default consume rule "First due first, then first in first out" (thanks @kriddles) - Recipe costs were based on the last purchase price per product before, so this now better reflects the current real costs - Improved the recipe add workflow (a recipe called "New recipe" is now not automatically created when starting to add a recipe) (thanks @zsarnett) - Fixed that images on the recipe gallery view were not scaled correctly on larger screens (thanks @zsarnett) @@ -145,6 +155,11 @@ _- (Because the stock quantity unit is now the base for everything, it cannot be - The product object no longer has a field `barcodes` with a comma separated barcode list, instead barcodes are now stored in a separate table/entity `product_barcodes` (use the existing "Generic entity interactions" endpoints to access them) - The endpoint `/objects/{entity}/search` was removed (use the existing `/objects/{entity}` endpoint with new new filter capabilities mentioned below) - The output / field names of `ProductDetailsResponse` have slightly changed (endpoint `/stock/products/{productId}`) + - Endpoint `/stock/volatile` + - The query parameter `expring_days` was renamed to `due_soon_days` + - The field `expiring_products` was renamed to `due_products` + - The field `expired_products` now only contains expired products (so them with `Due date type = Expiration date`) + - The new field `overdue_products` contains only overdue products (so them with `Due date type = Best before date`) - For better integration (apps), it's now possible to show a QR-Code for API keys (thanks @fipwmaqzufheoxq92ebc) - New QR-Code button on the "Manage API keys"-page (top right corner settings menu), the QR-Codes contains `|` - And on the calendar page when using the button "Share/Integrate calendar (iCal)", there the QR-Codes contains the Share-URL (which is displayed in the textbox above) @@ -174,7 +189,7 @@ _- (Because the stock quantity unit is now the base for everything, it cannot be - `<=` less or equal - `` is the value to search for - New endpoints `/stock/journal` and `/stock/journal/summary` to get the stock journal (thanks @fipwmaqzufheoxq92ebc) -- New endpoint `/stock/shoppinglist/add-expired-products` to add all currently in-stock but expired products to a shopping list (thanks @m-byte) +- New endpoint `/stock/shoppinglist/add-overdue-products` to add all currently in-stock but overdue products to a shopping list (thanks @m-byte) - New endpoints GET/POST/PUT `/users/{userId}/permissions` for the new user permissions feature mentioned above - Performance improvements of the `/stock/products/*` endpoints (thanks @fipwmaqzufheoxq92ebc) - Fixed that the endpoint `/objects/{entity}/{objectId}` always returned successfully, even when the given object not exists (now returns `404` when the object is not found) (thanks @fipwmaqzufheoxq92ebc) diff --git a/config-dist.php b/config-dist.php index ffc69de2..1d6fab2a 100644 --- a/config-dist.php +++ b/config-dist.php @@ -113,17 +113,17 @@ 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_expiring_soon_days', 5); +DefaultUserSetting('stock_due_soon_days', 5); DefaultUserSetting('stock_default_purchase_amount', 0); DefaultUserSetting('stock_default_consume_amount', 1); DefaultUserSetting('scan_mode_consume_enabled', false); DefaultUserSetting('scan_mode_purchase_enabled', false); DefaultUserSetting('show_icon_on_stock_overview_page_when_product_is_on_shopping_list', true); DefaultUserSetting('show_purchased_date_on_purchase', false); // Wheter the purchased date should be editable on purchase (defaults to today otherwise) -DefaultUserSetting('show_warning_on_purchase_when_best_before_date_is_earlier_than_next', true); // Show a warning on purchase when the best before date of the purchased product is earlier than the next best before date in stock +DefaultUserSetting('show_warning_on_purchase_when_due_date_is_earlier_than_next', true); // Show a warning on purchase when the due date of the purchased product is earlier than the next due date in stock // Shopping list settings -DefaultUserSetting('shopping_list_to_stock_workflow_auto_submit_when_prefilled', false); // Automatically do the booking using the last price and the amount of the shopping list item, if the product has "Default best before days" set +DefaultUserSetting('shopping_list_to_stock_workflow_auto_submit_when_prefilled', false); // Automatically do the booking using the last price and the amount of the shopping list item, if the product has "Default due days" set DefaultUserSetting('shopping_list_show_calendar', false); // Recipe settings @@ -174,7 +174,7 @@ Setting('FEATURE_FLAG_STOCK_LOCATION_TRACKING', true); Setting('FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING', true); Setting('FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING', true); Setting('FEATURE_FLAG_STOCK_PRODUCT_FREEZING', true); -Setting('FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_FIELD_NUMBER_PAD', true); // Activate the number pad in best-before-date fields on (supported) mobile browsers +Setting('FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_FIELD_NUMBER_PAD', true); // Activate the number pad in due date fields on (supported) mobile browsers Setting('FEATURE_FLAG_SHOPPINGLIST_MULTIPLE_LISTS', true); Setting('FEATURE_FLAG_CHORES_ASSIGNMENTS', true); diff --git a/controllers/StockApiController.php b/controllers/StockApiController.php index 7bbd0c51..a18b20e9 100644 --- a/controllers/StockApiController.php +++ b/controllers/StockApiController.php @@ -31,7 +31,7 @@ class StockApiController extends BaseApiController } } - public function AddExpiredProductsToShoppingList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) + public function AddOverdueProductsToShoppingList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { User::checkPermission($request, User::PERMISSION_SHOPPINGLIST_ITEMS_ADD); @@ -46,7 +46,7 @@ class StockApiController extends BaseApiController $listId = intval($requestBody['list_id']); } - $this->getStockService()->AddExpiredProductsToShoppingList($listId); + $this->getStockService()->AddOverdueProductsToShoppingList($listId); return $this->EmptyApiResponse($response); } catch (\Exception $ex) @@ -304,16 +304,18 @@ class StockApiController extends BaseApiController { $nextXDays = 5; - if (isset($request->getQueryParams()['expiring_days']) && !empty($request->getQueryParams()['expiring_days']) && is_numeric($request->getQueryParams()['expiring_days'])) + if (isset($request->getQueryParams()['due_soon_days']) && !empty($request->getQueryParams()['due_soon_days']) && is_numeric($request->getQueryParams()['due_soon_days'])) { - $nextXDays = $request->getQueryParams()['expiring_days']; + $nextXDays = $request->getQueryParams()['due_soon_days']; } - $expiringProducts = $this->getStockService()->GetExpiringProducts($nextXDays, true); - $expiredProducts = $this->getStockService()->GetExpiringProducts(-1); + $dueProducts = $this->getStockService()->GetDueProducts($nextXDays, true); + $overdueProducts = $this->getStockService()->GetDueProducts(-1); + $expiredProducts = $this->getStockService()->GetExpiredProducts(); $missingProducts = $this->getStockService()->GetMissingProducts(); return $this->ApiResponse($response, [ - 'expiring_products' => $expiringProducts, + 'due_products' => $dueProducts, + 'overdue_products' => $overdueProducts, 'expired_products' => $expiredProducts, 'missing_products' => $missingProducts ]); diff --git a/controllers/StockController.php b/controllers/StockController.php index 89ed2a76..e347c58c 100644 --- a/controllers/StockController.php +++ b/controllers/StockController.php @@ -85,7 +85,7 @@ class StockController extends BaseController public function Overview(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { $usersService = $this->getUsersService(); - $nextXDays = $usersService->GetUserSettings(GROCY_USER_ID)['stock_expiring_soon_days']; + $nextXDays = $usersService->GetUserSettings(GROCY_USER_ID)['stock_due_soon_days']; return $this->renderPage($response, 'stockoverview', [ 'currentStock' => $this->getStockService()->GetCurrentStockOverview(), @@ -425,7 +425,7 @@ class StockController extends BaseController public function Stockentries(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { $usersService = $this->getUsersService(); - $nextXDays = $usersService->GetUserSettings(GROCY_USER_ID)['stock_expiring_soon_days']; + $nextXDays = $usersService->GetUserSettings(GROCY_USER_ID)['stock_due_soon_days']; return $this->renderPage($response, 'stockentries', [ 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), diff --git a/grocy.openapi.json b/grocy.openapi.json index 7c391602..20d7b73a 100644 --- a/grocy.openapi.json +++ b/grocy.openapi.json @@ -1258,7 +1258,7 @@ }, "/stock": { "get": { - "summary": "Returns all products which are currently in stock incl. the next expiring date per product", + "summary": "Returns all products which are currently in stock incl. the next due date per product", "tags": [ "Stock" ], @@ -1350,7 +1350,7 @@ "best_before_date": { "type": "string", "format": "date", - "description": "The best before date of the product to add, when omitted, the current date is used" + "description": "The due date of the product to add, when omitted, the current date is used" }, "price": { "type": "number", @@ -1416,16 +1416,16 @@ }, "/stock/volatile": { "get": { - "summary": "Returns all products which are expiring soon, are already expired or currently missing", + "summary": "Returns all products which are due soon, overdue, expired or currently missing", "tags": [ "Stock" ], "parameters": [ { "in": "query", - "name": "expiring_days", + "name": "due_soon_days", "required": false, - "description": "The number of days in which products are considered expiring soon", + "description": "The number of days in which products are considered to be due soon", "schema": { "type": "integer", "default": 5 @@ -1558,7 +1558,7 @@ }, "/stock/products/{productId}/entries": { "get": { - "summary": "Returns all stock entries of the given product in order of next use (first expiring first, then first in first out)", + "summary": "Returns all stock entries of the given product in order of next use (first due first, then first in first out)", "tags": [ "Stock" ], @@ -1707,7 +1707,7 @@ "best_before_date": { "type": "string", "format": "date", - "description": "The best before date of the product to add, when omitted, the current date is used" + "description": "The due date of the product to add, when omitted, the current date is used" }, "transaction_type": { "$ref": "#/components/internalSchemas/StockTransactionType" @@ -1956,7 +1956,7 @@ "best_before_date": { "type": "string", "format": "date", - "description": "The best before date which applies to added products" + "description": "The due date which applies to added products" }, "shopping_location_id": { "type": "number", @@ -2139,7 +2139,7 @@ "best_before_date": { "type": "string", "format": "date", - "description": "The best before date of the product to add, when omitted, the current date is used" + "description": "The due date of the product to add, when omitted, the current date is used" }, "transaction_type": { "$ref": "#/components/internalSchemas/StockTransactionType" @@ -2383,7 +2383,7 @@ "best_before_date": { "type": "string", "format": "date", - "description": "The best before date which applies to added products" + "description": "The due date which applies to added products" }, "location_id": { "type": "number", @@ -2530,9 +2530,9 @@ } } }, - "/stock/shoppinglist/add-expired-products": { + "/stock/shoppinglist/add-overdue-products": { "post": { - "summary": "Adds expired products to the given shopping list", + "summary": "Adds overdue products to the given shopping list", "tags": [ "Stock" ], @@ -4195,7 +4195,7 @@ "stock_amount_opened": { "type": "integer" }, - "next_best_before_date": { + "next_due_date": { "type": "string", "format": "date-time" }, @@ -4282,7 +4282,7 @@ "avg_price": null, "oldest_price": null, "last_shopping_location_id": null, - "next_best_before_date": "2019-07-07", + "next_due_date": "2019-07-07", "location": { "id": "4", "name": "Candy cupboard", @@ -4893,7 +4893,7 @@ "best_before_date": { "type": "string", "format": "date", - "description": "The next best before date for this product" + "description": "The next due date for this product" }, "is_aggregated_amount": { "type": "boolean", @@ -4941,7 +4941,13 @@ "CurrentVolatilStockResponse": { "type": "object", "properties": { - "expiring_products": { + "due_products": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CurrentStockResponse" + } + }, + "overdue_products": { "type": "array", "items": { "$ref": "#/components/schemas/CurrentStockResponse" diff --git a/localization/strings.pot b/localization/strings.pot index 5d1ad3e9..d75050df 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -20,6 +20,11 @@ msgid_plural "%s products expiring" msgstr[0] "" msgstr[1] "" +msgid "%s is due" +msgid_plural "%s are due" +msgstr[0] "" +msgstr[1] "" + msgid "within the next day" msgid_plural "within the next %s days" msgstr[0] "" @@ -30,6 +35,11 @@ msgid_plural "%s products are already expired" msgstr[0] "" msgstr[1] "" +msgid "%s product is overdue" +msgid_plural "%s products are overdue" +msgstr[0] "" +msgstr[1] "" + msgid "%s product is below defined min. stock amount" msgid_plural "%s products are below defined min. stock amount" msgstr[0] "" @@ -46,9 +56,6 @@ msgstr[1] "" msgid "Amount" msgstr "" -msgid "Next best before date" -msgstr "" - msgid "Logout" msgstr "" @@ -262,9 +269,6 @@ msgstr "" msgid "Add products that are below defined min. stock amount" msgstr "" -msgid "For purchases this amount of days will be added to today for the best before date suggestion" -msgstr "" - msgid "This means 1 %1$s purchased will be converted into %2$s %3$s in stock" msgstr "" @@ -598,15 +602,9 @@ msgstr[1] "" msgid "in singular form" msgstr "" -msgid "Never expires" -msgstr "" - msgid "This cannot be lower than %s" msgstr "" -msgid "-1 means that this product never expires" -msgstr "" - msgid "Quantity unit" msgstr "" @@ -869,18 +867,12 @@ msgstr "" msgid "Use a specific stock item" msgstr "" -msgid "The first item in this list would be picked by the default rule which is \"First expiring first, then first in first out\"" +msgid "The first item in this list would be picked by the default rule which is \"First due first, then first in first out\"" msgstr "" msgid "Mark %1$s of %2$s as open" msgstr "" -msgid "When this product was marked as opened, the best before date will be replaced by today + this amount of days (a value of 0 disables this)" -msgstr "" - -msgid "Default best before days after opened" -msgstr "" - msgid "Marked %1$s of %2$s as opened" msgstr "" @@ -896,7 +888,7 @@ msgstr "" msgid "%s opened" msgstr "" -msgid "Product expires" +msgid "Product due" msgstr "" msgid "Task due" @@ -1194,15 +1186,6 @@ msgstr "" msgid "A predefined list of values, one per line" msgstr "" -msgid "Chores due soon days" -msgstr "" - -msgid "Batteries due to be charged soon days" -msgstr "" - -msgid "Tasks due soon days" -msgstr "" - msgid "Products" msgstr "" @@ -1520,7 +1503,7 @@ msgstr "" msgid "There are no units available at this location" msgstr "" -msgid "Amount: %1$s; Expires on %2$s; Bought on %3$s" +msgid "Amount: %1$s; Due on %2$s; Bought on %3$s" msgstr "" msgid "Transfered %1$s of %2$s from %3$s to %4$s" @@ -1625,18 +1608,6 @@ msgstr "" msgid "When moving products from/to a freezer location, the products best before date is automatically adjusted according to the product settings" msgstr "" -msgid "On moving this product to a freezer location (so when freezing it), the best before date will be replaced by today + this amount of days" -msgstr "" - -msgid "Default best before days after freezing" -msgstr "" - -msgid "On moving this product from a freezer location (so when thawing it), the best before date will be replaced by today + this amount of days" -msgstr "" - -msgid "Default best before days after thawing" -msgstr "" - msgid "This cannot be the same as the \"From\" location" msgstr "" @@ -1757,7 +1728,7 @@ msgstr "" msgid "Not enough in stock (not included in costs), %s ingredient missing" msgstr "" -msgid "Based on the prices of the default consume rule which is \"First expiring first, then first in first out\"" +msgid "Based on the prices of the default consume rule which is \"First due first, then first in first out\"" msgstr "" msgid "Not enough in stock (not included in costs), %1$s missing, %2$s already on shopping list" @@ -1928,10 +1899,10 @@ msgstr "" msgid "This is the default quantity unit used when adding this product to the shopping list" msgstr "" -msgid "Show a warning when the best before date of the purchased product is earlier than the next best before date in stock" +msgid "Show a warning when the due date of the purchased product is earlier than the next due date in stock" msgstr "" -msgid "There are items in stock which expire earlier" +msgid "There are items in stock which are due earlier" msgstr "" msgid "When enabled, the amount will always be filled with 1 after changing/scanning a product and if all fields could be automatically populated (by product defaults), the transaction is automatically submitted" @@ -1948,3 +1919,70 @@ msgstr "" msgid "Are you sure to remove this barcode?" msgstr "" + +msgid "Due date type" +msgstr "" + +msgid "Based on the selected type, the highlighting on the stock overview page will be different" +msgstr "" + +msgid "Means that the product is maybe still safe to be consumed after its due date is reached" +msgstr "" + +msgid "Expiration date" +msgstr "" + +msgid "Means that the product is not safe to be consumed after its due date is reached" +msgstr "" + +msgid "For purchases this amount of days will be added to today for the due date suggestion" +msgstr "" + +msgid "-1 means that this product wille be never overdue" +msgstr "" + +msgid "Default due days" +msgstr "" + +msgid "When this product was marked as opened, the expiry date will be replaced by today + this amount of days (a value of 0 disables this)" +msgstr "" + +msgid "Default due days after opened" +msgstr "" + +msgid "On moving this product to a freezer location (so when freezing it), the expiry date will be replaced by today + this amount of days" +msgstr "" + +msgid "Default due days after freezing" +msgstr "" + +msgid "On moving this product from a freezer location (so when thawing it), the due date will be replaced by today + this amount of days" +msgstr "" + +msgid "Default due days after thawing" +msgstr "" + +msgid "Next due date" +msgstr "" + +msgid "%s product is due" +msgid_plural "%s products are due" +msgstr[0] "" +msgstr[1] "" + +msgid "Due date" +msgstr "" + +msgid "Never overdue" +msgstr "" + +msgid "%s product is expired" +msgid_plural "%s products are expired" +msgstr[0] "" +msgstr[1] "" + +msgid "Expired" +msgstr "" + +msgid "Due soon days" +msgstr "" diff --git a/migrations/0103.sql b/migrations/0103.sql index 6dcd2ab9..62291bbf 100644 --- a/migrations/0103.sql +++ b/migrations/0103.sql @@ -68,6 +68,7 @@ CREATE TABLE products ( parent_product_id INT, calories INTEGER, cumulate_min_stock_amount_of_sub_products TINYINT DEFAULT 0, + due_type TINYINT NOT NULL DEFAULT 1 CHECK(due_type IN (1, 2)), quick_consume_amount REAL NOT NULL DEFAULT 1, row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime')) ); diff --git a/migrations/0105.sql b/migrations/0105.sql index a67d670f..e51fbdf0 100644 --- a/migrations/0105.sql +++ b/migrations/0105.sql @@ -23,15 +23,18 @@ SELECT p.calories AS product_calories, sc.amount * p.calories AS calories, sc.amount_aggregated * p.calories AS calories_aggregated, - p.quick_consume_amount + p.quick_consume_amount, + p.due_type FROM ( SELECT * FROM stock_current WHERE best_before_date IS NOT NULL UNION - SELECT id, 0, 0, 0, null, 0, 0, 0 - FROM stock_missing_products_including_opened - WHERE id NOT IN (SELECT product_id FROM stock_current) + SELECT m.id, 0, 0, 0, null, 0, 0, 0, p.due_type + FROM stock_missing_products_including_opened m + JOIN products p + ON m.id = p.id + WHERE m.id NOT IN (SELECT product_id FROM stock_current) ) sc LEFT JOIN products p ON sc.product_id = p.id; @@ -61,15 +64,18 @@ SELECT p.calories AS product_calories, sc.amount * p.calories AS calories, sc.amount_aggregated * p.calories AS calories_aggregated, - p.quick_consume_amount + p.quick_consume_amount, + p.due_type FROM ( SELECT * FROM stock_current WHERE best_before_date IS NOT NULL UNION - SELECT id, 0, 0, 0, null, 0, 0, 0 - FROM stock_missing_products - WHERE id NOT IN (SELECT product_id FROM stock_current) + SELECT m.id, 0, 0, 0, null, 0, 0, 0, p.due_type + FROM stock_missing_products m + JOIN products p + ON m.id = p.id + WHERE m.id NOT IN (SELECT product_id FROM stock_current) ) sc LEFT JOIN products p ON sc.product_id = p.id; diff --git a/migrations/0108.sql b/migrations/0108.sql index 1e6d7ef6..9c23a661 100644 --- a/migrations/0108.sql +++ b/migrations/0108.sql @@ -9,7 +9,8 @@ SELECT MIN(s.best_before_date) AS best_before_date, IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = pr.parent_product_id AND open = 1), 0) AS amount_opened, IFNULL((SELECT SUM(amount) FROM stock WHERE product_id IN (SELECT sub_product_id FROM products_resolved WHERE parent_product_id = pr.parent_product_id) AND open = 1), 0) * IFNULL(qucr.factor, 1) AS amount_opened_aggregated, - CASE WHEN COUNT(p_sub.parent_product_id) > 0 THEN 1 ELSE 0 END AS is_aggregated_amount + CASE WHEN COUNT(p_sub.parent_product_id) > 0 THEN 1 ELSE 0 END AS is_aggregated_amount, + MAX(p_parent.due_type) AS due_type FROM products_resolved pr JOIN stock s ON pr.sub_product_id = s.product_id @@ -37,10 +38,14 @@ SELECT MIN(s.best_before_date) AS best_before_date, IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = s.product_id AND open = 1), 0) AS amount_opened, IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = s.product_id AND open = 1), 0) AS amount_opened_aggregated, - 0 AS is_aggregated_amount + 0 AS is_aggregated_amount, + MAX(p_sub.due_type) AS due_type FROM products_resolved pr JOIN stock s ON pr.sub_product_id = s.product_id +JOIN products p_sub + ON pr.sub_product_id = p_sub.id + AND p_sub.active = 1 WHERE pr.parent_product_id != pr.sub_product_id GROUP BY pr.sub_product_id HAVING SUM(s.amount) > 0; diff --git a/migrations/0115.sql b/migrations/0115.sql index b92a2f52..15636c61 100644 --- a/migrations/0115.sql +++ b/migrations/0115.sql @@ -1 +1 @@ -update user_settings set key = "stock_expiring_soon_days" where key = "stock_expring_soon_days"; +update user_settings set key = "stock_due_soon_days" where key = "stock_expring_soon_days"; diff --git a/public/css/grocy.css b/public/css/grocy.css index d089e20d..e57a3f71 100755 --- a/public/css/grocy.css +++ b/public/css/grocy.css @@ -460,7 +460,8 @@ canvas.drawingBuffer { .warning-message, .error-message, -.normal-message { +.normal-message, +.secondary-message { padding: 12px; font-weight: bold; width: fit-content; @@ -485,6 +486,12 @@ canvas.drawingBuffer { border-top-color: #4c63b6; } +.secondary-message { + background-color: #e1e4e8; + color: #4e575f; + border-top-color: #68696b; +} + .status-filter-message, .user-filter-message { display: inline-block; diff --git a/public/viewjs/consume.js b/public/viewjs/consume.js index 85f8ee40..e606f760 100644 --- a/public/viewjs/consume.js +++ b/public/viewjs/consume.js @@ -219,7 +219,7 @@ $("#location_id").on('change', function(e) $("#specific_stock_entry").append($("