From 3df44697bf902f0d79dac645303315ab20f244d5 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Wed, 18 Sep 2019 10:02:52 +0200 Subject: [PATCH] Implemented the option to automatically consume a product on tracking a chore execution (closes #279) --- changelog/52_UNRELEASED_2019-xx-xx.md | 72 +++++++++++++++------------ controllers/ChoresController.php | 6 ++- localization/strings.pot | 3 ++ migrations/0084.sql | 8 +++ public/viewjs/choreform.js | 20 ++++++++ services/ChoresService.php | 18 ++++++- views/choreform.blade.php | 31 ++++++++++++ 7 files changed, 122 insertions(+), 36 deletions(-) create mode 100644 migrations/0084.sql diff --git a/changelog/52_UNRELEASED_2019-xx-xx.md b/changelog/52_UNRELEASED_2019-xx-xx.md index 47069bb6..91a504ec 100644 --- a/changelog/52_UNRELEASED_2019-xx-xx.md +++ b/changelog/52_UNRELEASED_2019-xx-xx.md @@ -1,41 +1,47 @@ -- Stock improvements - - Products can now have variations (nested products) - - Define the parent product for a product on the product edit page (only one level is possible, means a product which is used as a parent product in another product, cannot have a parent product itself) - - Parent and sub products can have stock (both are regular products, no difference from that side) - - On the stock overview page the aggregated amount is displayed next to the amount (sigma sign) - - When a recipe needs a parent product, the need is also fulfilled when enough sub product(s) are in stock - - Quantity units can now be linked (related measurements / unit conversion) - - On the quantity unit edit page default conversion can be defined for each unit - - Products "inherit" the default conversion and additionally can have their own / override the default ones - - It's now possible to print a "Location Content Sheet" with the current stock per location - new button at the top of the stock overview page (thought to hang it at the location, note used amounts on paper and track it in grocy later) - - The product description now can have formattings (HTML/WYSIWYG editor like for recipes) - - "Factor purchase to stock quantity unit" (product option) can now also be a decimal number when "Allow partial units in stock" is enabled -- Recipe improvements - - Based on the new linked quantity units, recipe ingredients can now use any product related unit, the amount is calculated according to the cnoversion factor of the unit relation -- Chores improvements - - Chores can now be assigned to users - - Option per chore, different "assignment types" like "Random", "Who least did first", etc. - - On the chores overview page the list can be filterd to only show chores assigned to the currently logged in user (or to any other user) - - New option "Due date rollover" per chore which means the chore can never be overdue, the due date will shift forward each day when due - - When tracking an execution from the chores overview page, filters are re-applied afterwards (means when have filtered the page to only show overdue chores and after the execution the chore is not overdue anymore, it will now immediately hide id) -- Equipment improvements/fixes - - Fixed that the delete button not always deleted the currently selected equipment item -- Userfield improvements/fixes - - New Userfield type "Select list" for a list of predefined values where a single or also multiple values can then be selected on the entity object - - New Userfield type "Link" - a single-line-textbox where the content will be rendered as a clickable link - - Userfields of type "checkbox" are rendered as a checkmark in tables when checked (instead of "1" as till now) - - Product Userfields are now also rendered on the shopping list (for items which have a product referenced) - - Fixed that the Userfield type "Preset list" had always the caption "Product group" instead of the configured one (thanks @oncleben31) -- General improvements/fixes - - Improved the handling which entry page to use with disabled feature flags (thanks @nielstholenaar) - - Boolean settings provided via environment variables (so the strings `true` and `false`) are now parsed correctly (thanks @mduret) -- API improvements & non-breaking changes +### Stock improvements +- Products can now have variations (nested products) + - Define the parent product for a product on the product edit page (only one level is possible, means a product which is used as a parent product in another product, cannot have a parent product itself) + - Parent and sub products can have stock (both are regular products, no difference from that side) + - On the stock overview page the aggregated amount is displayed next to the amount (sigma sign) + - When a recipe needs a parent product, the need is also fulfilled when enough sub product(s) are in stock +- Quantity units can now be linked (related measurements / unit conversion) + - On the quantity unit edit page default conversion can be defined for each unit + - Products "inherit" the default conversion and additionally can have their own / override the default ones +- It's now possible to print a "Location Content Sheet" with the current stock per location - new button at the top of the stock overview page (thought to hang it at the location, note used amounts on paper and track it in grocy later) +- The product description now can have formattings (HTML/WYSIWYG editor like for recipes) +- "Factor purchase to stock quantity unit" (product option) can now also be a decimal number when "Allow partial units in stock" is enabled + +### Recipe improvements +- Based on the new linked quantity units, recipe ingredients can now use any product related unit, the amount is calculated according to the cnoversion factor of the unit relation + +### Chores improvements +- Chores can now be assigned to users + - Option per chore, different "assignment types" like "Random", "Who least did first", etc. + - On the chores overview page the list can be filterd to only show chores assigned to the currently logged in user (or to any other user) +- New option "Due date rollover" per chore which means the chore can never be overdue, the due date will shift forward each day when due +- New option "Consume product on chore execution" per chore to automatically consume a product when a chore execution is tracked +- When tracking an execution from the chores overview page, filters are re-applied afterwards (means when have filtered the page to only show overdue chores and after the execution the chore is not overdue anymore, it will now immediately hide id) + +### Equipment improvements/fixes +- Fixed that the delete button not always deleted the currently selected equipment item + +### Userfield improvements/fixes +- New Userfield type "Select list" for a list of predefined values where a single or also multiple values can then be selected on the entity object +- New Userfield type "Link" - a single-line-textbox where the content will be rendered as a clickable link +- Userfields of type "checkbox" are rendered as a checkmark in tables when checked (instead of "1" as till now) +- Product Userfields are now also rendered on the shopping list (for items which have a product referenced) +- Fixed that the Userfield type "Preset list" had always the caption "Product group" instead of the configured one (thanks @oncleben31) + +### General improvements/fixes +- Improved the handling which entry page to use with disabled feature flags (thanks @nielstholenaar) +- Boolean settings provided via environment variables (so the strings `true` and `false`) are now parsed correctly (thanks @mduret) + +### API improvements & non-breaking changes - New endpoint `/stock/shoppinglist/add-product` to add a product to a shopping list (thanks @Forceu) - New endpoint `/stock/shoppinglist/remove-product` to remove a product from a shopping list (thanks @Forceu) - New endpoint `/chores/executions/calculate-next-assignments` to (re)calculate next user assignments for a single or all chores - New endpoint `/objects/{entity}/search/{searchString}` search for objects by name (contains search) - When adding a product (through `stock/product/{productId}/add` or `stock/product/{productId}/inventory`) with omitted best before date and if the given product has "Default best before days" set, the best before date is calculated based on that (so far always today was used which is still the case when no date is supplied and also the product has no "Default best before days set) (thanks @Forceu) - Field `stock_amount` of endpoint `/stock/products/{productId}´ now returns `0` instead of `null` when the given product is not in stock (thanks @Forceu) - - `​/stock​/products​/{productId}` returns additional fields for the aggregated amount(s): `stock_amount_aggregated` and `stock_amount_opened_aggregated` - contains the same for "normal" products, `is_aggregated_amount` indicates if aggregation has happened - Fixed that `/system/db-changed-time` always returned the current time (more or less) due to that that time is the database file modification time and the database is effectively changed on each request because of session information tracking - which now explicitly does not change the database file modification time, so this should work again to determine if any data changes happened - It's now also possible to provide the API key via a query parameter (same name as the header, so `GROCY-API-KEY`) diff --git a/controllers/ChoresController.php b/controllers/ChoresController.php index 2c224e7d..fe71ff53 100644 --- a/controllers/ChoresController.php +++ b/controllers/ChoresController.php @@ -71,7 +71,8 @@ class ChoresController extends BaseController 'mode' => 'create', 'userfields' => $this->UserfieldsService->GetFields('chores'), 'assignmentTypes' => GetClassConstants('\Grocy\Services\ChoresService', 'CHORE_ASSIGNMENT_TYPE_'), - 'users' => $users + 'users' => $users, + 'products' => $this->Database->products()->orderBy('name') ]); } else @@ -82,7 +83,8 @@ class ChoresController extends BaseController 'mode' => 'edit', 'userfields' => $this->UserfieldsService->GetFields('chores'), 'assignmentTypes' => GetClassConstants('\Grocy\Services\ChoresService', 'CHORE_ASSIGNMENT_TYPE_'), - 'users' => $users + 'users' => $users, + 'products' => $this->Database->products()->orderBy('name') ]); } } diff --git a/localization/strings.pot b/localization/strings.pot index 66dd6af1..d702bea8 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -1417,3 +1417,6 @@ msgstr "" msgid "Filter by assignment" msgstr "" + +msgid "Consume product on chore execution" +msgstr "" diff --git a/migrations/0084.sql b/migrations/0084.sql new file mode 100644 index 00000000..917d6111 --- /dev/null +++ b/migrations/0084.sql @@ -0,0 +1,8 @@ +ALTER TABLE chores +ADD consume_product_on_execution TINYINT NOT NULL DEFAULT 0; + +ALTER TABLE chores +ADD product_id TINYINT; + +ALTER TABLE chores +ADD product_amount REAL; diff --git a/public/viewjs/choreform.js b/public/viewjs/choreform.js index cde3e161..6753785a 100644 --- a/public/viewjs/choreform.js +++ b/public/viewjs/choreform.js @@ -102,6 +102,10 @@ setTimeout(function() { $(".input-group-chore-period-type").trigger("change"); $(".input-group-chore-assignment-type").trigger("change"); + + // Click twice to trigger on-click but not change the actual checked state + $("#consume_product_on_execution").click(); + $("#consume_product_on_execution").click(); }, 100); $('.input-group-chore-period-type').on('change', function(e) @@ -180,3 +184,19 @@ $('.input-group-chore-assignment-type').on('change', function(e) Grocy.FrontendHelpers.ValidateForm('chore-form'); }); + +$("#consume_product_on_execution").on("click", function() +{ + if (this.checked) + { + Grocy.Components.ProductPicker.GetInputElement().removeAttr("disabled"); + $("#product_amount").removeAttr("disabled"); + } + else + { + Grocy.Components.ProductPicker.GetInputElement().attr("disabled", ""); + $("#product_amount").attr("disabled", ""); + } + + Grocy.FrontendHelpers.ValidateForm("chore-form"); +}); diff --git a/services/ChoresService.php b/services/ChoresService.php index e9ab72ab..c82cfe58 100644 --- a/services/ChoresService.php +++ b/services/ChoresService.php @@ -2,6 +2,8 @@ namespace Grocy\Services; +use \Grocy\Services\StockService; + class ChoresService extends BaseService { const CHORE_PERIOD_TYPE_MANUALLY = 'manually'; @@ -15,6 +17,14 @@ class ChoresService extends BaseService const CHORE_ASSIGNMENT_TYPE_RANDOM = 'random'; const CHORE_ASSIGNMENT_TYPE_IN_ALPHABETICAL_ORDER = 'in-alphabetical-order'; + public function __construct() + { + parent::__construct(); + $this->StockService = new StockService(); + } + + protected $StockService; + public function GetCurrent() { $sql = 'SELECT * from chores_current'; @@ -84,9 +94,15 @@ class ChoresService extends BaseService 'done_by_user_id' => $doneBy )); $logRow->save(); - $lastInsertId = $this->Database->lastInsertId(); + $this->CalculateNextExecutionAssignment($choreId); + + if ($chore->consume_product_on_execution == 1 && !empty($chore->product_id)) + { + $this->StockService->ConsumeProduct($chore->product_id, $chore->product_amount, false, StockService::TRANSACTION_TYPE_CONSUME); + } + return $lastInsertId; } diff --git a/views/choreform.blade.php b/views/choreform.blade.php index 9d6277b8..e965d1eb 100644 --- a/views/choreform.blade.php +++ b/views/choreform.blade.php @@ -126,6 +126,37 @@ + @if(GROCY_FEATURE_FLAG_STOCK) +
+
+ + consume_product_on_execution == 1) checked @endif class="form-check-input" type="checkbox" id="consume_product_on_execution" name="consume_product_on_execution" value="1"> + +
+
+ + @php $prefillById = ''; if($mode=='edit' && !empty($chore->product_id)) { $prefillById = $chore->product_id; } @endphp + @include('components.productpicker', array( + 'products' => $products, + 'nextInputSelector' => '#product_amount', + 'isRequired' => false, + 'disallowAllProductWorkflows' => true, + 'prefillById' => $prefillById + )) + + @php if($mode == 'edit') { $value = $chore->product_amount; } else { $value = ''; } @endphp + @include('components.numberpicker', array( + 'id' => 'product_amount', + 'label' => 'Amount', + 'hintId' => 'amount_qu_unit', + 'min' => 0.0001, + 'step' => 0.0001, + 'invalidFeedback' => $__t('The amount cannot be lower than %s', '1'), + 'isRequired' => false, + 'value' => $value + )) + @endif + @include('components.userfieldsform', array( 'userfields' => $userfields, 'entity' => 'chores'