mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 01:32:38 +00:00
Automatically create/update/delete inverse QU conversions (closes #1844)
This commit is contained in:
parent
cefc1b7b9c
commit
6ecf94073d
@ -39,6 +39,9 @@
|
||||
- 1. The new default consume location, if the product currently has any stock there, otherwise
|
||||
- 2. The products default location, if the product currently has any stock there, otherwise
|
||||
- 3. The first location where the product currently has any stock
|
||||
- Optimized quantity unit conversion handling:
|
||||
- The option "Create inverse QU conversion" was removed when creating a QU conversion
|
||||
- => Instead the corresponding inverse conversion is now always created/updated/deleted automatically
|
||||
- New product option "Disable own stock" (defaults to disabled)
|
||||
- When enabled, the corresponding product can't have own stock, means it will not be selectable on purchase (useful for parent products which are just used as a summary/total view of the child products)
|
||||
- The location content sheet can now optionally list also out of stock products (at the products default location, new checkbox "Show only in-stock products " at the top of the page, defaults to enabled)
|
||||
|
@ -1601,9 +1601,6 @@ msgstr ""
|
||||
msgid "means %1$s per %2$s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create inverse QU conversion"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create recipe"
|
||||
msgstr ""
|
||||
|
||||
|
108
migrations/0188.sql
Normal file
108
migrations/0188.sql
Normal file
@ -0,0 +1,108 @@
|
||||
-- Remove QU conversions which are already defined by the products qu_factor_purchase_to_stock
|
||||
DELETE FROM quantity_unit_conversions
|
||||
WHERE id IN (
|
||||
SELECT quc.id
|
||||
FROM quantity_unit_conversions quc
|
||||
JOIN products p
|
||||
ON quc.product_id = p.id
|
||||
WHERE (p.qu_id_purchase = quc.to_qu_id AND p.qu_id_stock = quc.from_qu_id)
|
||||
OR (p.qu_id_purchase = quc.from_qu_id AND p.qu_id_stock = quc.to_qu_id)
|
||||
);
|
||||
|
||||
DROP TRIGGER quantity_unit_conversions_custom_unique_constraint_INS;
|
||||
CREATE TRIGGER qu_conversions_custom_constraint_INS BEFORE INSERT ON quantity_unit_conversions
|
||||
BEGIN
|
||||
/*
|
||||
Necessary because unique constraints don't include NULL values in SQLite,
|
||||
and also because the constraint should include the products default conversion factor
|
||||
*/
|
||||
SELECT CASE WHEN((
|
||||
SELECT 1
|
||||
FROM quantity_unit_conversions
|
||||
WHERE from_qu_id = NEW.from_qu_id
|
||||
AND to_qu_id = NEW.to_qu_id
|
||||
AND IFNULL(product_id, 0) = IFNULL(NEW.product_id, 0)
|
||||
UNION
|
||||
SELECT 1
|
||||
FROM products
|
||||
WHERE id = NEW.product_id
|
||||
AND qu_id_purchase = NEW.from_qu_id
|
||||
AND qu_id_stock = NEW.to_qu_id
|
||||
UNION
|
||||
SELECT 1
|
||||
FROM products
|
||||
WHERE id = NEW.product_id
|
||||
AND qu_id_purchase = NEW.to_qu_id
|
||||
AND qu_id_stock = NEW.from_qu_id
|
||||
)
|
||||
NOTNULL) THEN RAISE(ABORT, "QU constraint violation") END;
|
||||
END;
|
||||
|
||||
DROP TRIGGER quantity_unit_conversions_custom_unique_constraint_UPD;
|
||||
CREATE TRIGGER qu_conversions_custom_constraint_UPD BEFORE UPDATE ON quantity_unit_conversions
|
||||
BEGIN
|
||||
/* This contains practically the same logic as the trigger qu_conversions_custom_constraint_INS */
|
||||
|
||||
/*
|
||||
Necessary because unique constraints don't include NULL values in SQLite,
|
||||
and also because the constraint should include the products default conversion factor
|
||||
*/
|
||||
SELECT CASE WHEN((
|
||||
SELECT 1
|
||||
FROM quantity_unit_conversions
|
||||
WHERE from_qu_id = NEW.from_qu_id
|
||||
AND to_qu_id = NEW.to_qu_id
|
||||
AND IFNULL(product_id, 0) = IFNULL(NEW.product_id, 0)
|
||||
AND id != NEW.id
|
||||
UNION
|
||||
SELECT 1
|
||||
FROM products
|
||||
WHERE id = NEW.product_id
|
||||
AND qu_id_purchase = NEW.from_qu_id
|
||||
AND qu_id_stock = NEW.to_qu_id
|
||||
UNION
|
||||
SELECT 1
|
||||
FROM products
|
||||
WHERE id = NEW.product_id
|
||||
AND qu_id_purchase = NEW.to_qu_id
|
||||
AND qu_id_stock = NEW.from_qu_id
|
||||
)
|
||||
NOTNULL) THEN RAISE(ABORT, "QU constraint violation") END;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER qu_conversions_inverse_INS AFTER INSERT ON quantity_unit_conversions
|
||||
BEGIN
|
||||
/*
|
||||
Create the inverse QU conversion
|
||||
*/
|
||||
|
||||
INSERT OR REPLACE INTO quantity_unit_conversions
|
||||
(from_qu_id, to_qu_id, factor, product_id)
|
||||
VALUES
|
||||
(NEW.to_qu_id, NEW.from_qu_id, 1 / IFNULL(NEW.factor, 1), NEW.product_id);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER qu_conversions_inverse_UPD AFTER UPDATE ON quantity_unit_conversions
|
||||
BEGIN
|
||||
/*
|
||||
Update the inverse QU conversion
|
||||
*/
|
||||
|
||||
UPDATE quantity_unit_conversions
|
||||
SET factor = 1 / IFNULL(NEW.factor, 1)
|
||||
WHERE from_qu_id = NEW.to_qu_id
|
||||
AND to_qu_id = NEW.from_qu_id
|
||||
AND IFNULL(product_id, -1) = IFNULL(NEW.product_id, -1);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER qu_conversions_inverse_DEL AFTER DELETE ON quantity_unit_conversions
|
||||
BEGIN
|
||||
/*
|
||||
Delete the inverse QU conversion
|
||||
*/
|
||||
|
||||
DELETE FROM quantity_unit_conversions
|
||||
WHERE from_qu_id = OLD.to_qu_id
|
||||
AND to_qu_id = OLD.from_qu_id
|
||||
AND IFNULL(product_id, -1) = IFNULL(OLD.product_id, -1);
|
||||
END;
|
@ -15,27 +15,9 @@
|
||||
var jsonData = $('#quconversion-form').serializeJSON();
|
||||
jsonData.from_qu_id = $("#from_qu_id").val();
|
||||
Grocy.FrontendHelpers.BeginUiBusy("quconversion-form");
|
||||
if ($("#create_inverse").is(":checked"))
|
||||
{
|
||||
var inverse_to_qu_id = $("#from_qu_id").val();
|
||||
var inverse_from_qu_id = $("#to_qu_id").val();
|
||||
}
|
||||
|
||||
if (Grocy.EditMode === 'create')
|
||||
{
|
||||
Grocy.Api.Post('objects/quantity_unit_conversions', jsonData,
|
||||
function(result)
|
||||
{
|
||||
Grocy.EditObjectId = result.created_object_id;
|
||||
Grocy.Components.UserfieldsForm.Save(function()
|
||||
{
|
||||
if ($("#create_inverse").is(":checked"))
|
||||
{
|
||||
jsonData.to_qu_id = inverse_to_qu_id;
|
||||
jsonData.from_qu_id = inverse_from_qu_id;
|
||||
jsonData.factor = 1 / jsonData.factor;
|
||||
|
||||
//Create Inverse
|
||||
Grocy.Api.Post('objects/quantity_unit_conversions', jsonData,
|
||||
function(result)
|
||||
{
|
||||
@ -68,34 +50,6 @@
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (typeof GetUriParam("qu-unit") !== "undefined")
|
||||
{
|
||||
if (GetUriParam("embedded") !== undefined)
|
||||
{
|
||||
window.parent.postMessage(WindowMessageBag("Reload"), Grocy.BaseUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.location.href = U("/quantityunit/" + GetUriParam("qu-unit"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
window.parent.postMessage(WindowMessageBag("ProductQUConversionChanged"), U("/product/" + GetUriParam("product")));
|
||||
window.parent.postMessage(WindowMessageBag("CloseAllModals"), U("/product/" + GetUriParam("product")));
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
Grocy.FrontendHelpers.EndUiBusy("quconversion-form");
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
Grocy.Api.Put('objects/quantity_unit_conversions/' + Grocy.EditObjectId, jsonData,
|
||||
function(result)
|
||||
@ -152,20 +106,6 @@ $('#quconversion-form input').keydown(function(event)
|
||||
}
|
||||
});
|
||||
|
||||
$("#create_inverse").on("change", function()
|
||||
{
|
||||
var value = $(this).is(":checked");
|
||||
|
||||
if (value)
|
||||
{
|
||||
$('#qu-conversion-inverse-info').removeClass('d-none');
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#qu-conversion-inverse-info').addClass('d-none');
|
||||
}
|
||||
});
|
||||
|
||||
$('.input-group-qu').on('change', function(e)
|
||||
{
|
||||
var fromQuId = $("#from_qu_id").val();
|
||||
@ -186,12 +126,8 @@ $('.input-group-qu').on('change', function(e)
|
||||
{
|
||||
$('#qu-conversion-info').text(__t('This means 1 %1$s is the same as %2$s %3$s', $("#from_qu_id option:selected").text(), parseFloat((1 * factor)).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), __n((1 * factor).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), $("#to_qu_id option:selected").text(), $("#to_qu_id option:selected").data("plural-form"), true)));
|
||||
$('#qu-conversion-info').removeClass('d-none');
|
||||
|
||||
if (Grocy.EditMode === 'create')
|
||||
{
|
||||
$('#qu-conversion-inverse-info').text(__t('This means 1 %1$s is the same as %2$s %3$s', $("#to_qu_id option:selected").text(), parseFloat((1 / factor)).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), __n((1 / factor).toString(), $("#from_qu_id option:selected").text(), $("#from_qu_id option:selected").data("plural-form"), true)));
|
||||
$('#qu-conversion-inverse-info').removeClass('d-none');
|
||||
}
|
||||
$('#qu-conversion-inverse-info').text(__t('This means 1 %1$s is the same as %2$s %3$s', $("#to_qu_id option:selected").text(), parseFloat((1 / factor)).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), __n((1 / factor).toString(), $("#from_qu_id option:selected").text(), $("#from_qu_id option:selected").data("plural-form"), true)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -111,9 +111,7 @@ class DemoDataGeneratorService extends BaseService
|
||||
UPDATE products SET qu_id_purchase = (SELECT MIN(id) FROM quantity_units) WHERE id IN (SELECT id FROM products WHERE qu_id_purchase NOT IN (SELECT id FROM quantity_units));
|
||||
|
||||
INSERT INTO quantity_unit_conversions (from_qu_id, to_qu_id, factor, product_id) VALUES (3, 12, 10, 10);
|
||||
INSERT INTO quantity_unit_conversions (from_qu_id, to_qu_id, factor, product_id) VALUES (12, 3, 0.1, 10);
|
||||
INSERT INTO quantity_unit_conversions (from_qu_id, to_qu_id, factor, product_id) VALUES (3, 8, 1000, 22);
|
||||
INSERT INTO quantity_unit_conversions (from_qu_id, to_qu_id, factor, product_id) VALUES (8, 3, 0.001, 22);
|
||||
|
||||
INSERT INTO shopping_list (note, amount) VALUES ('{$this->__t_sql('Some good snacks')}', 1);
|
||||
INSERT INTO shopping_list (product_id, amount) VALUES (20, 1);
|
||||
|
@ -114,25 +114,12 @@
|
||||
'decimals' => $userSettings['stock_decimal_places_amounts'],
|
||||
'value' => $value,
|
||||
'additionalHtmlElements' => '<p id="qu-conversion-info"
|
||||
class="form-text text-info d-none mb-0"></p>
|
||||
<p id="qu-conversion-inverse-info"
|
||||
class="form-text text-info d-none"></p>',
|
||||
'additionalCssClasses' => 'input-group-qu locale-number-input locale-number-quantity-amount'
|
||||
))
|
||||
|
||||
<div class="form-group @if($mode == 'edit') d-none @endif">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input checked
|
||||
class="form-check-input custom-control-input"
|
||||
type="checkbox"
|
||||
id="create_inverse"
|
||||
name="create_inverse:skip"
|
||||
value="1">
|
||||
<label class="form-check-label custom-control-label"
|
||||
for="create_inverse">{{ $__t('Create inverse QU conversion') }}</label>
|
||||
</div>
|
||||
<span id="qu-conversion-inverse-info"
|
||||
class="form-text text-info d-none"></span>
|
||||
</div>
|
||||
|
||||
@include('components.userfieldsform', array(
|
||||
'userfields' => $userfields,
|
||||
'entity' => 'quantity_unit_conversions'
|
||||
|
Loading…
x
Reference in New Issue
Block a user