mirror of
https://github.com/grocy/grocy.git
synced 2026-03-27 23:29:25 +01:00
Optimized product definition quantity unit handling
This commit is contained in:
parent
cf7df4bdf8
commit
487631397c
|
|
@ -10,6 +10,11 @@
|
||||||
|
|
||||||
### Stock
|
### Stock
|
||||||
|
|
||||||
|
- Optimized product definition quantity unit handling:
|
||||||
|
- As long as a product was not once in stock, the product options "Default quantity unit purchase", "Default quantity unit consume" and "Quantity unit for prices" can now be changed to any other unit
|
||||||
|
- When necessary (means when no default quantity unit conversions apply), "1:1" product specific quantity unit conversion between the product's QU stock and the corresponding other unit will now be created automatically after editing a product (like already done when initially creating a product with different unit definitions)
|
||||||
|
- For convenience, when changing a product's QU stock and when all other unit properties ("Default quantity unit purchase" etc.) are the same, they will now be changed in tandem (like already done when creating a new product and initially setting the product's QU stock)
|
||||||
|
- (This should drastically improve the workflow of completing a product setup looked up via an external barcode lookup plugin)
|
||||||
- Optimized the line plot markers color of the price history chart (product card) (thanks @DeepCoreSystem)
|
- Optimized the line plot markers color of the price history chart (product card) (thanks @DeepCoreSystem)
|
||||||
- External barcode lookup plugin optimizations:
|
- External barcode lookup plugin optimizations:
|
||||||
- When an image URL without a file extension is returned, the file extension is now determined by the Content-Type header (if any) (thanks @jordy-u for the idea)
|
- When an image URL without a file extension is returned, the file extension is now determined by the Content-Type header (if any) (thanks @jordy-u for the idea)
|
||||||
|
|
|
||||||
|
|
@ -195,12 +195,13 @@ class StockController extends BaseController
|
||||||
{
|
{
|
||||||
if ($args['productId'] == 'new')
|
if ($args['productId'] == 'new')
|
||||||
{
|
{
|
||||||
|
$quantityunits = $this->getDatabase()->quantity_units()->where('active = 1')->orderBy('name', 'COLLATE NOCASE');
|
||||||
|
|
||||||
return $this->renderPage($response, 'productform', [
|
return $this->renderPage($response, 'productform', [
|
||||||
'locations' => $this->getDatabase()->locations()->where('active = 1')->orderBy('name'),
|
'locations' => $this->getDatabase()->locations()->where('active = 1')->orderBy('name'),
|
||||||
'barcodes' => $this->getDatabase()->product_barcodes()->orderBy('barcode'),
|
'barcodes' => $this->getDatabase()->product_barcodes()->orderBy('barcode'),
|
||||||
'quantityunits' => $this->getDatabase()->quantity_units()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
'quantityunitsAll' => $quantityunits,
|
||||||
'quantityunitsStock' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'),
|
'quantityunitsReferenced' => $quantityunits,
|
||||||
'referencedQuantityunits' => $this->getDatabase()->quantity_units()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
|
||||||
'shoppinglocations' => $this->getDatabase()->shopping_locations()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
'shoppinglocations' => $this->getDatabase()->shopping_locations()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||||
'productgroups' => $this->getDatabase()->product_groups()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
'productgroups' => $this->getDatabase()->product_groups()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||||
'userfields' => $this->getUserfieldsService()->GetFields('products'),
|
'userfields' => $this->getUserfieldsService()->GetFields('products'),
|
||||||
|
|
@ -217,9 +218,8 @@ class StockController extends BaseController
|
||||||
'product' => $product,
|
'product' => $product,
|
||||||
'locations' => $this->getDatabase()->locations()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
'locations' => $this->getDatabase()->locations()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||||
'barcodes' => $this->getDatabase()->product_barcodes()->orderBy('barcode'),
|
'barcodes' => $this->getDatabase()->product_barcodes()->orderBy('barcode'),
|
||||||
'quantityunits' => $this->getDatabase()->quantity_units()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
'quantityunitsAll' => $this->getDatabase()->quantity_units()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||||
'quantityunitsStock' => $this->getDatabase()->quantity_units()->where('id IN (SELECT to_qu_id FROM cache__quantity_unit_conversions_resolved WHERE product_id = :1) OR NOT EXISTS(SELECT 1 FROM stock_log WHERE product_id = :1)', $product->id)->orderBy('name', 'COLLATE NOCASE'),
|
'quantityunitsReferenced' => $this->getDatabase()->quantity_units()->where('id IN (SELECT to_qu_id FROM cache__quantity_unit_conversions_resolved WHERE product_id = :1) OR NOT EXISTS(SELECT 1 FROM stock_log WHERE product_id = :1)', $product->id)->orderBy('name', 'COLLATE NOCASE'),
|
||||||
'referencedQuantityunits' => $this->getDatabase()->quantity_units()->where('active = 1')->where('id IN (SELECT to_qu_id FROM cache__quantity_unit_conversions_resolved WHERE product_id = :1)', $product->id)->orderBy('name', 'COLLATE NOCASE'),
|
|
||||||
'shoppinglocations' => $this->getDatabase()->shopping_locations()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
'shoppinglocations' => $this->getDatabase()->shopping_locations()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||||
'productgroups' => $this->getDatabase()->product_groups()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
'productgroups' => $this->getDatabase()->product_groups()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
|
||||||
'userfields' => $this->getUserfieldsService()->GetFields('products'),
|
'userfields' => $this->getUserfieldsService()->GetFields('products'),
|
||||||
|
|
|
||||||
|
|
@ -2470,3 +2470,6 @@ msgstr ""
|
||||||
|
|
||||||
msgid "List actions"
|
msgid "List actions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "After this product was once in stock and when the desired quantity unit cannot be selected here, first create a corresponding unit conversion"
|
||||||
|
msgstr ""
|
||||||
|
|
|
||||||
66
migrations/0255.sql
Normal file
66
migrations/0255.sql
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
DROP TRIGGER products_default_qu_conversions;
|
||||||
|
CREATE TRIGGER products_default_qu_conversions_INS AFTER INSERT ON products
|
||||||
|
BEGIN
|
||||||
|
-- Create product specific 1:1 conversions when QU stock != QU purchase/consume/price
|
||||||
|
-- and when no default QU conversion apply
|
||||||
|
|
||||||
|
-- with qu_id_stock != qu_id_purchase
|
||||||
|
INSERT INTO quantity_unit_conversions
|
||||||
|
(from_qu_id, to_qu_id, factor, product_id)
|
||||||
|
SELECT p.qu_id_purchase, p.qu_id_stock, 1, p.id
|
||||||
|
FROM products p
|
||||||
|
WHERE p.id = NEW.id
|
||||||
|
AND p.qu_id_stock != qu_id_purchase
|
||||||
|
AND NOT EXISTS(SELECT 1 FROM quantity_unit_conversions_resolved WHERE product_id = p.id AND from_qu_id = p.qu_id_stock AND to_qu_id = p.qu_id_purchase);
|
||||||
|
|
||||||
|
-- with qu_id_stock != qu_id_consume
|
||||||
|
INSERT INTO quantity_unit_conversions
|
||||||
|
(from_qu_id, to_qu_id, factor, product_id)
|
||||||
|
SELECT p.qu_id_consume, p.qu_id_stock, 1, p.id
|
||||||
|
FROM products p
|
||||||
|
WHERE p.id = NEW.id
|
||||||
|
AND p.qu_id_stock != qu_id_consume
|
||||||
|
AND NOT EXISTS(SELECT 1 FROM quantity_unit_conversions_resolved WHERE product_id = p.id AND from_qu_id = p.qu_id_stock AND to_qu_id = p.qu_id_consume);
|
||||||
|
|
||||||
|
-- with qu_id_stock != qu_id_price
|
||||||
|
INSERT INTO quantity_unit_conversions
|
||||||
|
(from_qu_id, to_qu_id, factor, product_id)
|
||||||
|
SELECT p.qu_id_price, p.qu_id_stock, 1, p.id
|
||||||
|
FROM products p
|
||||||
|
WHERE p.id = NEW.id
|
||||||
|
AND p.qu_id_stock != qu_id_price
|
||||||
|
AND NOT EXISTS(SELECT 1 FROM quantity_unit_conversions_resolved WHERE product_id = p.id AND from_qu_id = p.qu_id_stock AND to_qu_id = p.qu_id_price);
|
||||||
|
END;
|
||||||
|
|
||||||
|
CREATE TRIGGER products_default_qu_conversions_UPD AFTER UPDATE ON products
|
||||||
|
BEGIN
|
||||||
|
-- Create product specific 1:1 conversions when QU stock != QU purchase/consume/price
|
||||||
|
-- and when no default QU conversion apply
|
||||||
|
|
||||||
|
-- with qu_id_stock != qu_id_purchase
|
||||||
|
INSERT INTO quantity_unit_conversions
|
||||||
|
(from_qu_id, to_qu_id, factor, product_id)
|
||||||
|
SELECT p.qu_id_purchase, p.qu_id_stock, 1, p.id
|
||||||
|
FROM products p
|
||||||
|
WHERE p.id = NEW.id
|
||||||
|
AND p.qu_id_stock != qu_id_purchase
|
||||||
|
AND NOT EXISTS(SELECT 1 FROM quantity_unit_conversions_resolved WHERE product_id = p.id AND from_qu_id = p.qu_id_stock AND to_qu_id = p.qu_id_purchase);
|
||||||
|
|
||||||
|
-- with qu_id_stock != qu_id_consume
|
||||||
|
INSERT INTO quantity_unit_conversions
|
||||||
|
(from_qu_id, to_qu_id, factor, product_id)
|
||||||
|
SELECT p.qu_id_consume, p.qu_id_stock, 1, p.id
|
||||||
|
FROM products p
|
||||||
|
WHERE p.id = NEW.id
|
||||||
|
AND p.qu_id_stock != qu_id_consume
|
||||||
|
AND NOT EXISTS(SELECT 1 FROM quantity_unit_conversions_resolved WHERE product_id = p.id AND from_qu_id = p.qu_id_stock AND to_qu_id = p.qu_id_consume);
|
||||||
|
|
||||||
|
-- with qu_id_stock != qu_id_price
|
||||||
|
INSERT INTO quantity_unit_conversions
|
||||||
|
(from_qu_id, to_qu_id, factor, product_id)
|
||||||
|
SELECT p.qu_id_price, p.qu_id_stock, 1, p.id
|
||||||
|
FROM products p
|
||||||
|
WHERE p.id = NEW.id
|
||||||
|
AND p.qu_id_stock != qu_id_price
|
||||||
|
AND NOT EXISTS(SELECT 1 FROM quantity_unit_conversions_resolved WHERE product_id = p.id AND from_qu_id = p.qu_id_stock AND to_qu_id = p.qu_id_price);
|
||||||
|
END;
|
||||||
|
|
@ -352,29 +352,33 @@ $(document).on('click', '.barcode-delete-button', function(e)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var quIdStockBefore = $("#qu_id_stock").val();
|
||||||
$('#qu_id_stock').change(function(e)
|
$('#qu_id_stock').change(function(e)
|
||||||
{
|
{
|
||||||
// Preset qu_id_purchase/qu_id_consume/qu_id_price by qu_id_stock if unset
|
// Preset qu_id_purchase / qu_id_consume / qu_id_price by qu_id_stock if unset or identical
|
||||||
|
|
||||||
var quIdStock = $('#qu_id_stock');
|
var quIdStock = $('#qu_id_stock');
|
||||||
var quIdPurchase = $('#qu_id_purchase');
|
var quIdPurchase = $('#qu_id_purchase');
|
||||||
var quIdConsume = $('#qu_id_consume');
|
var quIdConsume = $('#qu_id_consume');
|
||||||
var quIdPrice = $('#qu_id_price');
|
var quIdPrice = $('#qu_id_price');
|
||||||
|
|
||||||
if (quIdPurchase[0].selectedIndex === 0 && quIdStock[0].selectedIndex !== 0)
|
if (quIdPurchase[0].selectedIndex === 0 && quIdStock[0].selectedIndex !== 0 || quIdStockBefore == quIdPurchase.val())
|
||||||
{
|
{
|
||||||
quIdPurchase[0].selectedIndex = quIdStock[0].selectedIndex;
|
quIdPurchase[0].selectedIndex = quIdStock[0].selectedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quIdConsume[0].selectedIndex === 0 && quIdStock[0].selectedIndex !== 0)
|
if (quIdConsume[0].selectedIndex === 0 && quIdStock[0].selectedIndex !== 0 || quIdStockBefore == quIdConsume.val())
|
||||||
{
|
{
|
||||||
quIdConsume[0].selectedIndex = quIdStock[0].selectedIndex;
|
quIdConsume[0].selectedIndex = quIdStock[0].selectedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quIdPrice[0].selectedIndex === 0 && quIdStock[0].selectedIndex !== 0)
|
if (quIdPrice[0].selectedIndex === 0 && quIdStock[0].selectedIndex !== 0 || quIdStockBefore == quIdPrice.val())
|
||||||
{
|
{
|
||||||
quIdPrice[0].selectedIndex = quIdStock[0].selectedIndex;
|
quIdPrice[0].selectedIndex = quIdStock[0].selectedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quIdStockBefore = quIdStock.val();
|
||||||
|
|
||||||
Grocy.FrontendHelpers.ValidateForm('product-form');
|
Grocy.FrontendHelpers.ValidateForm('product-form');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -371,15 +371,19 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="qu_id_stock">{{ $__t('Quantity unit stock') }}</label>
|
<label for="qu_id_stock">{{ $__t('Quantity unit stock') }}</label>
|
||||||
|
<i class="fa-solid fa-question-circle text-muted"
|
||||||
|
data-toggle="tooltip"
|
||||||
|
data-trigger="hover click"
|
||||||
|
title="{{ $__t('After this product was once in stock and when the desired quantity unit cannot be selected here, first create a corresponding unit conversion') }}"></i>
|
||||||
<select required
|
<select required
|
||||||
class="custom-control custom-select input-group-qu"
|
class="custom-control custom-select input-group-qu"
|
||||||
id="qu_id_stock"
|
id="qu_id_stock"
|
||||||
name="qu_id_stock">
|
name="qu_id_stock">
|
||||||
<option></option>
|
<option></option>
|
||||||
@foreach($quantityunitsStock as $quantityunit)
|
@foreach($quantityunitsReferenced as $qu)
|
||||||
<option @if($mode=='edit'
|
<option @if($mode=='edit'
|
||||||
&&
|
&&
|
||||||
$quantityunit->id == $product->qu_id_stock) selected="selected" @endif value="{{ $quantityunit->id }}" data-plural-form="{{ $quantityunit->name_plural }}">{{ $quantityunit->name }}</option>
|
$qu->id == $product->qu_id_stock) selected="selected" @endif value="{{ $qu->id }}" data-plural-form="{{ $qu->name_plural }}">{{ $qu->name }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
|
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
|
||||||
|
|
@ -396,10 +400,10 @@
|
||||||
id="qu_id_purchase"
|
id="qu_id_purchase"
|
||||||
name="qu_id_purchase">
|
name="qu_id_purchase">
|
||||||
<option></option>
|
<option></option>
|
||||||
@foreach($referencedQuantityunits as $quantityunit)
|
@foreach($quantityunitsReferenced as $qu)
|
||||||
<option @if($mode=='edit'
|
<option @if($mode=='edit'
|
||||||
&&
|
&&
|
||||||
$quantityunit->id == $product->qu_id_purchase) selected="selected" @endif value="{{ $quantityunit->id }}">{{ $quantityunit->name }}</option>
|
$qu->id == $product->qu_id_purchase) selected="selected" @endif value="{{ $qu->id }}">{{ $qu->name }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
|
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
|
||||||
|
|
@ -416,10 +420,10 @@
|
||||||
id="qu_id_consume"
|
id="qu_id_consume"
|
||||||
name="qu_id_consume">
|
name="qu_id_consume">
|
||||||
<option></option>
|
<option></option>
|
||||||
@foreach($referencedQuantityunits as $quantityunit)
|
@foreach($quantityunitsReferenced as $qu)
|
||||||
<option @if($mode=='edit'
|
<option @if($mode=='edit'
|
||||||
&&
|
&&
|
||||||
$quantityunit->id == $product->qu_id_consume) selected="selected" @endif value="{{ $quantityunit->id }}">{{ $quantityunit->name }}</option>
|
$qu->id == $product->qu_id_consume) selected="selected" @endif value="{{ $qu->id }}">{{ $qu->name }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
|
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
|
||||||
|
|
@ -436,10 +440,10 @@
|
||||||
id="qu_id_price"
|
id="qu_id_price"
|
||||||
name="qu_id_price">
|
name="qu_id_price">
|
||||||
<option></option>
|
<option></option>
|
||||||
@foreach($referencedQuantityunits as $quantityunit)
|
@foreach($quantityunitsReferenced as $qu)
|
||||||
<option @if($mode=='edit'
|
<option @if($mode=='edit'
|
||||||
&&
|
&&
|
||||||
$quantityunit->id == $product->qu_id_price) selected="selected" @endif value="{{ $quantityunit->id }}">{{ $quantityunit->name }}</option>
|
$qu->id == $product->qu_id_price) selected="selected" @endif value="{{ $qu->id }}">{{ $qu->name }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
|
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
|
||||||
|
|
@ -762,7 +766,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if(!empty($barcode->qu_id))
|
@if(!empty($barcode->qu_id))
|
||||||
{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $barcode->qu_id)->name }}
|
{{ FindObjectInArrayByPropertyValue($quantityunitsAll, 'id', $barcode->qu_id)->name }}
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
@ -886,16 +890,16 @@
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->from_qu_id)->name }}
|
{{ FindObjectInArrayByPropertyValue($quantityunitsAll, 'id', $quConversion->from_qu_id)->name }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->to_qu_id)->name }}
|
{{ FindObjectInArrayByPropertyValue($quantityunitsAll, 'id', $quConversion->to_qu_id)->name }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="locale-number locale-number-quantity-amount">{{ $quConversion->factor }}</span>
|
<span class="locale-number locale-number-quantity-amount">{{ $quConversion->factor }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="font-italic">
|
<td class="font-italic">
|
||||||
{!! $__t('This means 1 %1$s is the same as %2$s %3$s', FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->from_qu_id)->name, '<span class="locale-number locale-number-quantity-amount">' . $quConversion->factor . '</span>', $__n($quConversion->factor, FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->to_qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->to_qu_id)->name_plural, true)) !!}
|
{!! $__t('This means 1 %1$s is the same as %2$s %3$s', FindObjectInArrayByPropertyValue($quantityunitsAll, 'id', $quConversion->from_qu_id)->name, '<span class="locale-number locale-number-quantity-amount">' . $quConversion->factor . '</span>', $__n($quConversion->factor, FindObjectInArrayByPropertyValue($quantityunitsAll, 'id', $quConversion->to_qu_id)->name, FindObjectInArrayByPropertyValue($quantityunitsAll, 'id', $quConversion->to_qu_id)->name_plural, true)) !!}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user