Started working on qu unit conversion handling (references #177)

This commit is contained in:
Bernd Bestel 2019-09-15 16:40:54 +02:00
parent 30d89f4529
commit 6094096675
No known key found for this signature in database
GPG Key ID: 71BD34C0D4891300
11 changed files with 685 additions and 20 deletions

View File

@ -156,7 +156,8 @@ class StockController extends BaseController
'userfields' => $this->UserfieldsService->GetFields('products'),
'products' => $this->Database->products()->where('id != :1 AND parent_product_id IS NULL', $product->id)->orderBy('name'),
'isSubProductOfOthers' => $this->Database->products()->where('parent_product_id = :1', $product->id)->count() !== 0,
'mode' => 'edit'
'mode' => 'edit',
'quConversions' => $this->Database->quantity_unit_conversions()
]);
}
}
@ -212,12 +213,16 @@ class StockController extends BaseController
}
else
{
$quantityUnit = $this->Database->quantity_units($args['quantityunitId']);
return $this->AppContainer->view->render($response, 'quantityunitform', [
'quantityunit' => $this->Database->quantity_units($args['quantityunitId']),
'quantityUnit' => $quantityUnit,
'mode' => 'edit',
'userfields' => $this->UserfieldsService->GetFields('quantity_units'),
'pluralCount' => $this->LocalizationService->GetPluralCount(),
'pluralRule' => $this->LocalizationService->GetPluralDefinition()
'pluralRule' => $this->LocalizationService->GetPluralDefinition(),
'defaultQuConversions' => $this->Database->quantity_unit_conversions()->where('from_qu_id = :1 AND product_id IS NULL', $quantityUnit->id),
'quantityUnits' => $this->Database->quantity_units()
]);
}
}
@ -278,4 +283,41 @@ class StockController extends BaseController
'currentStockLocationContent' => $this->StockService->GetCurrentStockLocationContent()
]);
}
public function QuantityUnitConversionEditForm(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{
$product = null;
if (isset($request->getQueryParams()['product']))
{
$product = $this->Database->products($request->getQueryParams()['product']);
}
$defaultQuUnit = null;
if (isset($request->getQueryParams()['qu-unit']))
{
$defaultQuUnit = $this->Database->quantity_units($request->getQueryParams()['qu-unit']);
}
if ($args['quConversionId'] == 'new')
{
return $this->AppContainer->view->render($response, 'quantityunitconversionform', [
'mode' => 'create',
'userfields' => $this->UserfieldsService->GetFields('quantity_unit_conversions'),
'quantityunits' => $this->Database->quantity_units()->orderBy('name'),
'product' => $product,
'defaultQuUnit' => $defaultQuUnit
]);
}
else
{
return $this->AppContainer->view->render($response, 'quantityunitconversionform', [
'quConversion' => $this->Database->quantity_unit_conversions($args['quConversionId']),
'mode' => 'edit',
'userfields' => $this->UserfieldsService->GetFields('quantity_unit_conversions'),
'quantityunits' => $this->Database->quantity_units()->orderBy('name'),
'product' => $product,
'defaultQuUnit' => $defaultQuUnit
]);
}
}
}

View File

@ -2430,6 +2430,7 @@
"batteries",
"locations",
"quantity_units",
"quantity_unit_conversions",
"shopping_list",
"shopping_lists",
"recipes",

View File

@ -1322,3 +1322,51 @@ msgstr ""
msgid "Not possible because this product is already used as a parent product in another product"
msgstr ""
msgid "Create QU conversion"
msgstr ""
msgid "Quantity unit from"
msgstr ""
msgid "Quantity unit to"
msgstr ""
msgid "The amount cannot be lower than %s and must be a valid number"
msgstr ""
msgid "Factor"
msgstr ""
msgid "This cannot be equal to %s"
msgstr ""
msgid "This means 1 %1$s is the same as %2$s %3$s"
msgstr ""
msgid "Edit QU conversion"
msgstr ""
msgid "For product"
msgstr ""
msgid "Default for QU unit"
msgstr ""
msgid "Override for product"
msgstr ""
msgid "Default conversions"
msgstr ""
msgid "1 %s is the same as..."
msgstr ""
msgid "Are you sure to remove this conversion?"
msgstr ""
msgid "QU conversions"
msgstr ""
msgid "Product overrides"
msgstr ""

82
migrations/0082.sql Normal file
View File

@ -0,0 +1,82 @@
CREATE TABLE quantity_unit_conversions (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
from_qu_id INT NOT NULL,
to_qu_id INT NOT NULL,
factor REAL NOT NULL,
product_id INT,
row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
);
CREATE TRIGGER quantity_unit_conversions_custom_unique_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
)
NOTNULL) THEN RAISE(ABORT, "Unique constraint violation") END;
END;
CREATE TRIGGER quantity_unit_conversions_custom_unique_constraint_UPD BEFORE UPDATE 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
)
NOTNULL) THEN RAISE(ABORT, "Unique constraint violation") END;
END;
CREATE VIEW quantity_unit_conversions_resolved
AS
-- First: Product "purchase to stock" conversion factor
SELECT
p.id AS product_id,
p.qu_id_purchase AS from_qu_id,
p.qu_id_stock AS to_qu_id,
p.qu_factor_purchase_to_stock AS factor
FROM products p
UNION
-- Second: Product specific overrides
SELECT
p.id AS product_id,
p.qu_id_stock AS from_qu_id,
quc.to_qu_id AS to_qu_id,
quc.factor AS factor
FROM products p
JOIN quantity_unit_conversions quc
ON p.qu_id_stock = quc.from_qu_id
AND p.id = quc.product_id
UNION
-- Third: Default quantity unit conversion factors
SELECT
p.id AS product_id,
p.qu_id_stock AS from_qu_id,
quc.to_qu_id AS to_qu_id,
quc.factor AS factor
FROM products p
JOIN quantity_unit_conversions quc
ON p.qu_id_stock = quc.from_qu_id
AND quc.product_id IS NULL;

View File

@ -9,6 +9,11 @@
redirectDestination = U(returnTo) + '?createdproduct=' + encodeURIComponent($('#name').val());
}
if (Grocy.ProductEditFormRedirectUri !== undefined)
{
redirectDestination = Grocy.ProductEditFormRedirectUri;
}
var jsonData = $('#product-form').serializeJSON({ checkboxUncheckedValue: "0" });
var parentProductId = jsonData.product_id;
delete jsonData.product_id;
@ -44,7 +49,14 @@
Grocy.Api.UploadFile($("#product-picture")[0].files[0], 'productpictures', jsonData.picture_file_name,
function(result)
{
window.location.href = redirectDestination;
if (redirectDestination == "reload")
{
window.location.reload();
}
else
{
window.location.href = redirectDestination;
}
},
function (xhr)
{
@ -55,7 +67,14 @@
}
else
{
window.location.href = redirectDestination;
if (redirectDestination == "reload")
{
window.location.reload();
}
else
{
window.location.href = redirectDestination;
}
}
});
},
@ -93,7 +112,14 @@
Grocy.Api.UploadFile($("#product-picture")[0].files[0], 'productpictures', jsonData.picture_file_name,
function(result)
{
window.location.href = redirectDestination;
if (redirectDestination == "reload")
{
window.location.reload();
}
else
{
window.location.href = redirectDestination;
}
},
function(xhr)
{
@ -104,7 +130,14 @@
}
else
{
window.location.href = redirectDestination;
if (redirectDestination == "reload")
{
window.location.reload();
}
else
{
window.location.href = redirectDestination;
}
}
});
},
@ -189,6 +222,9 @@ $('.input-group-qu').on('change', function(e)
$('#qu-conversion-info').addClass('d-none');
}
$("#qu-conversion-headline-info").text(__t('1 %s is the same as...', $("#qu_id_stock option:selected").text()));
quConversionsTable.column(4).search("from_qu_id xx" + $("#qu_id_stock").val().toString() + "xx").draw();
$("#tare_weight_qu_info").text($("#qu_id_stock option:selected").text());
Grocy.FrontendHelpers.ValidateForm('product-form');
@ -278,6 +314,34 @@ if (Grocy.EditMode === 'create')
}
}
var quConversionsTable = $('#qu-conversions-table').DataTable({
'paginate': false,
'order': [[1, 'asc']],
"orderFixed": [[3, 'asc']],
'columnDefs': [
{ 'orderable': false, 'targets': 0 },
{ 'visible': false, 'targets': 3 }
],
'language': JSON.parse(__t('datatables_localization')),
'scrollY': false,
'colReorder': true,
'stateSave': true,
'stateSaveParams': function(settings, data)
{
data.search.search = "";
data.columns.forEach(column =>
{
column.search.search = "";
});
},
'rowGroup': {
dataSrc: 3
}
});
$('#qu-conversions-table tbody').removeClass("d-none");
quConversionsTable.columns.adjust().draw();
Grocy.Components.UserfieldsForm.Load();
$('#name').focus();
$('.input-group-qu').trigger('change');
@ -286,3 +350,52 @@ Grocy.FrontendHelpers.ValidateForm('product-form');
// Click twice to trigger on-click but not change the actual checked state
$("#allow_partial_units_in_stock").click();
$("#allow_partial_units_in_stock").click();
$(document).on('click', '.qu-conversion-delete-button', function(e)
{
var objectId = $(e.currentTarget).attr('data-qu-conversion-id');
bootbox.confirm({
message: __t('Are you sure to remove this conversion?'),
buttons: {
confirm: {
label: __t('Yes'),
className: 'btn-success'
},
cancel: {
label: __t('No'),
className: 'btn-danger'
}
},
callback: function(result)
{
if (result === true)
{
Grocy.Api.Delete('objects/quantity_unit_conversions/' + objectId, { },
function(result)
{
Grocy.ProductEditFormRedirectUri = "reload";
$('#save-product-button').click();
},
function(xhr)
{
console.error(xhr);
}
);
}
}
});
});
$(document).on('click', '.qu-conversion-edit-button', function (e)
{
var id = $(e.currentTarget).attr('data-qu-conversion-id');
Grocy.ProductEditFormRedirectUri = U("/quantityunitconversion/" + id.toString() + "?product=" + Grocy.EditObjectId.toString());
$('#save-product-button').click();
});
$("#qu-conversion-add-button").on("click", function(e)
{
Grocy.ProductEditFormRedirectUri = U("/quantityunitconversion/new?product=" + Grocy.EditObjectId.toString());
$('#save-product-button').click();
});

View File

@ -0,0 +1,115 @@
$('#save-quconversion-button').on('click', function(e)
{
e.preventDefault();
var jsonData = $('#quconversion-form').serializeJSON();
jsonData.from_qu_id = $("#from_qu_id").val();
Grocy.FrontendHelpers.BeginUiBusy("quconversion-form");
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 (typeof GetUriParam("qu-unit") !== "undefined")
{
window.location.href = U("/quantityunit/" + GetUriParam("qu-unit"));
}
else
{
window.location.href = 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)
{
Grocy.Components.UserfieldsForm.Save(function()
{
if (typeof GetUriParam("qu-unit") !== "undefined")
{
window.location.href = U("/quantityunit/" + GetUriParam("qu-unit"));
}
else
{
window.location.href = U("/product/" + GetUriParam("product"));
}
});
},
function(xhr)
{
Grocy.FrontendHelpers.EndUiBusy("quconversion-form");
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
}
});
$('#quconversion-form input').keyup(function(event)
{
$('.input-group-qu').trigger('change');
Grocy.FrontendHelpers.ValidateForm('quconversion-form');
});
$('#quconversion-form input').keydown(function(event)
{
if (event.keyCode === 13) //Enter
{
event.preventDefault();
if (document.getElementById('quconversion-form').checkValidity() === false) //There is at least one validation error
{
return false;
}
else
{
$('#save-quconversion-button').click();
}
}
});
$('.input-group-qu').on('change', function(e)
{
var fromQuId = $("#from_qu_id").val();
var toQuId = $("#to_qu_id").val();
var factor = $('#factor').val();
if (fromQuId == toQuId)
{
$("#to_qu_id").parent().find(".invalid-feedback").text(__t('This cannot be equal to %s', $("#from_qu_id option:selected").text()));
$("#to_qu_id")[0].setCustomValidity("error");
}
else
{
$("#to_qu_id")[0].setCustomValidity("");
}
if (fromQuId && toQuId)
{
$('#qu-conversion-info').text(__t('This means 1 %1$s is the same as %2$s %3$s', $("#from_qu_id option:selected").text(), (1 * factor).toString(), $("#to_qu_id option:selected").text()));
$('#qu-conversion-info').removeClass('d-none');
}
else
{
$('#qu-conversion-info').addClass('d-none');
}
Grocy.FrontendHelpers.ValidateForm('quconversion-form');
});
Grocy.Components.UserfieldsForm.Load();
$('.input-group-qu').trigger('change');
$('#from_qu_id').focus();
Grocy.FrontendHelpers.ValidateForm('quconversion-form');

View File

@ -64,6 +64,94 @@ $('#quantityunit-form input').keydown(function(event)
}
});
var quConversionsTable = $('#qu-conversions-table').DataTable({
'paginate': false,
'order': [[1, 'asc']],
'columnDefs': [
{ 'orderable': false, 'targets': 0 }
],
'language': JSON.parse(__t('datatables_localization')),
'scrollY': false,
'colReorder': true,
'stateSave': true,
'stateSaveParams': function(settings, data)
{
data.search.search = "";
data.columns.forEach(column =>
{
column.search.search = "";
});
}
});
$('#qu-conversions-table tbody').removeClass("d-none");
quConversionsTable.columns.adjust().draw();
Grocy.Components.UserfieldsForm.Load();
$('#name').focus();
Grocy.FrontendHelpers.ValidateForm('quantityunit-form');
$(document).on('click', '.qu-conversion-delete-button', function(e)
{
var objectId = $(e.currentTarget).attr('data-qu-conversion-id');
bootbox.confirm({
message: __t('Are you sure to remove this conversion?'),
buttons: {
confirm: {
label: __t('Yes'),
className: 'btn-success'
},
cancel: {
label: __t('No'),
className: 'btn-danger'
}
},
callback: function(result)
{
if (result === true)
{
Grocy.Api.Delete('objects/quantity_unit_conversions/' + objectId, { },
function(result)
{
window.location.reload();
},
function(xhr)
{
console.error(xhr);
}
);
}
}
});
});
$(document).on('click', '.qu-conversion-edit-button', function (e)
{
var id = $(e.currentTarget).attr('data-qu-conversion-id');
Grocy.Api.Put('objects/quantity_units/' + Grocy.EditObjectId, $('#quantityunit-form').serializeJSON(),
function(result)
{
window.location.href = U("/quantityunitconversion/" + id.toString() + "?qu-unit=" + Grocy.EditObjectId.toString());
},
function(xhr)
{
console.error(xhr);
}
);
});
$("#qu-conversion-add-button").on("click", function(e)
{
Grocy.Api.Put('objects/quantity_units/' + Grocy.EditObjectId, $('#quantityunit-form').serializeJSON(),
function(result)
{
window.location.href = U("/quantityunitconversion/new?qu-unit=" + Grocy.EditObjectId.toString());
},
function(xhr)
{
console.error(xhr);
}
);
});

View File

@ -38,6 +38,7 @@ $app->group('', function()
$this->get('/location/{locationId}', '\Grocy\Controllers\StockController:LocationEditForm');
$this->get('/quantityunits', '\Grocy\Controllers\StockController:QuantityUnitsList');
$this->get('/quantityunit/{quantityunitId}', '\Grocy\Controllers\StockController:QuantityUnitEditForm');
$this->get('/quantityunitconversion/{quConversionId}', '\Grocy\Controllers\StockController:QuantityUnitConversionEditForm');
$this->get('/productgroups', '\Grocy\Controllers\StockController:ProductGroupsList');
$this->get('/productgroup/{productGroupId}', '\Grocy\Controllers\StockController:ProductGroupEditForm');
$this->get('/stockjournal', '\Grocy\Controllers\StockController:Journal');

View File

@ -10,10 +10,13 @@
@push('pageScripts')
<script src="{{ $U('/node_modules/TagManager/tagmanager.js?v=', true) }}{{ $version }}"></script>
<script src="{{ $U('/node_modules/datatables.net-rowgroup/js/dataTables.rowGroup.min.js?v=', true) }}{{ $version }}"></script>
<script src="{{ $U('/node_modules/datatables.net-rowgroup-bs4/js/rowGroup.bootstrap4.min.js?v=', true) }}{{ $version }}"></script>
@endpush
@push('pageStyles')
<link href="{{ $U('/node_modules/TagManager/tagmanager.css?v=', true) }}{{ $version }}" rel="stylesheet">
<link href="{{ $U('/node_modules/datatables.net-rowgroup-bs4/css/rowGroup.bootstrap4.min.css?v=', true) }}{{ $version }}" rel="stylesheet">
@endpush
@section('content')
@ -217,14 +220,67 @@
</div>
<div class="col-lg-6 col-xs-12">
<label class="mt-2">{{ $__t('Picture') }}</label>
<button id="delete-current-product-picture-button" class="btn btn-sm btn-danger @if(empty($product->picture_file_name)) disabled @endif"><i class="fas fa-trash"></i> {{ $__t('Delete') }}</button>
@if(!empty($product->picture_file_name))
<p><img id="current-product-picture" src="{{ $U('/api/files/productpictures/' . base64_encode($product->picture_file_name)) }}" class="img-fluid img-thumbnail mt-2"></p>
<p id="delete-current-product-picture-on-save-hint" class="form-text text-muted font-italic d-none">{{ $__t('The current picture will be deleted when you save the product') }}</p>
@else
<p id="no-current-product-picture-hint" class="form-text text-muted font-italic">{{ $__t('No picture available') }}</p>
@endif
<h2>
{{ $__t('QU conversions') }}
<a id="qu-conversion-add-button" class="btn btn-outline-dark" href="#">
<i class="fas fa-plus"></i> {{ $__t('Add') }}
</a>
</h2>
<h5 id="qu-conversion-headline-info" class="text-muted font-italic"></h5>
<table id="qu-conversions-table" class="table table-sm table-striped dt-responsive">
<thead>
<tr>
<th class="border-right"></th>
<th>{{ $__t('Factor') }}</th>
<th>{{ $__t('Unit') }}</th>
<th class="d-none">Hidden group</th>
<th class="d-none">Hidden from_qu_id</th>
</tr>
</thead>
<tbody class="d-none">
@if($mode == "edit")
@foreach($quConversions as $quConversion)
<tr>
<td class="fit-content border-right">
<a class="btn btn-sm btn-info qu-conversion-edit-button" href="#" data-qu-conversion-id="{{ $quConversion->id }}">
<i class="fas fa-edit"></i>
</a>
<a class="btn btn-sm btn-danger qu-conversion-delete-button" href="#" data-qu-conversion-id="{{ $quConversion->id }}">
<i class="fas fa-trash"></i>
</a>
</td>
<td>
{{ $quConversion->factor }}
</td>
<td>
{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', $quConversion->to_qu_id)->name }}
</td>
<td class="d-none">
@if($quConversion->product_id != null)
{{ $__t('Product overrides') }}
@else
{{ $__t('Default conversions') }}
@endif
</td>
<td class="d-none">
from_qu_id xx{{ $quConversion->from_qu_id }}xx
</td>
</tr>
@endforeach
@endif
</tbody>
</table>
<div class="pt-5">
<label class="mt-2">{{ $__t('Picture') }}</label>
<button id="delete-current-product-picture-button" class="btn btn-sm btn-danger @if(empty($product->picture_file_name)) disabled @endif"><i class="fas fa-trash"></i> {{ $__t('Delete') }}</button>
@if(!empty($product->picture_file_name))
<p><img id="current-product-picture" src="{{ $U('/api/files/productpictures/' . base64_encode($product->picture_file_name)) }}" class="img-fluid img-thumbnail mt-2"></p>
<p id="delete-current-product-picture-on-save-hint" class="form-text text-muted font-italic d-none">{{ $__t('The current picture will be deleted when you save the product') }}</p>
@else
<p id="no-current-product-picture-hint" class="form-text text-muted font-italic">{{ $__t('No picture available') }}</p>
@endif
</div>
</div>
</div>
@stop

View File

@ -0,0 +1,78 @@
@extends('layout.default')
@if($mode == 'edit')
@section('title', $__t('Edit QU conversion'))
@else
@section('title', $__t('Create QU conversion'))
@endif
@section('viewJsName', 'quantityunitconversionform')
@section('content')
<div class="row">
<div class="col-lg-6 col-xs-12">
<h1>@yield('title')</h1>
@if($product != null)
<h3 class="text-muted">{{ $__t('Override for product') }} <strong>{{ $product->name }}</strong></h3>
@else
<h3 class="text-muted">{{ $__t('Default for QU unit') }} <strong>{{ $defaultQuUnit->name }}</strong></h3>
@endif
<script>Grocy.EditMode = '{{ $mode }}';</script>
@if($mode == 'edit')
<script>Grocy.EditObjectId = {{ $quConversion->id }};</script>
@endif
<form id="quconversion-form" novalidate>
@if($product != null)
<input type="hidden" name="product_id" value="{{ $product->id }}">
@endif
<div class="form-group">
<label for="from_qu_id">{{ $__t('Quantity unit from') }}</label>
<select required disabled class="form-control input-group-qu" id="from_qu_id" name="from_qu_id">
<option></option>
@foreach($quantityunits as $quantityunit)
<option @if(($product != null && $quantityunit->id == $product->qu_id_stock) || ($defaultQuUnit != null && $quantityunit->id == $defaultQuUnit->id))) selected="selected" @endif value="{{ $quantityunit->id }}">{{ $quantityunit->name }}</option>
@endforeach
</select>
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
</div>
<div class="form-group">
<label for="to_qu_id">{{ $__t('Quantity unit to') }}</label>
<select required class="form-control input-group-qu" id="to_qu_id" name="to_qu_id">
<option></option>
@foreach($quantityunits as $quantityunit)
<option @if($mode == 'edit' && $quantityunit->id == $quConversion->to_qu_id) selected="selected" @endif value="{{ $quantityunit->id }}">{{ $quantityunit->name }}</option>
@endforeach
</select>
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
</div>
@php if($mode == 'edit') { $value = $quConversion->factor; } else { $value = 1; } @endphp
@include('components.numberpicker', array(
'id' => 'factor',
'label' => 'Factor',
'min' => 0,
'step' => 0.01,
'value' => $value,
'invalidFeedback' => $__t('The amount cannot be lower than %s and must be a valid number', '0'),
'additionalHtmlElements' => '<p id="qu-conversion-info" class="form-text text-info d-none"></p>',
'additionalCssClasses' => 'input-group-qu'
))
@include('components.userfieldsform', array(
'userfields' => $userfields,
'entity' => 'quantity_unit_conversions'
))
<button id="save-quconversion-button" class="btn btn-success">{{ $__t('Save') }}</button>
</form>
</div>
</div>
@stop

View File

@ -16,20 +16,20 @@
<script>Grocy.EditMode = '{{ $mode }}';</script>
@if($mode == 'edit')
<script>Grocy.EditObjectId = {{ $quantityunit->id }};</script>
<script>Grocy.EditObjectId = {{ $quantityUnit->id }};</script>
@endif
<form id="quantityunit-form" novalidate>
<div class="form-group">
<label for="name">{{ $__t('Name') }} <span class="small text-muted">{{ $__t('in singular form') }}</span></label>
<input type="text" class="form-control" required id="name" name="name" value="@if($mode == 'edit'){{ $quantityunit->name }}@endif">
<input type="text" class="form-control" required id="name" name="name" value="@if($mode == 'edit'){{ $quantityUnit->name }}@endif">
<div class="invalid-feedback">{{ $__t('A name is required') }}</div>
</div>
<div class="form-group">
<label for="name_plural">{{ $__t('Name') }} <span class="small text-muted">{{ $__t('in plural form') }}</span></label>
<input type="text" class="form-control" id="name_plural" name="name_plural" value="@if($mode == 'edit'){{ $quantityunit->name_plural }}@endif">
<input type="text" class="form-control" id="name_plural" name="name_plural" value="@if($mode == 'edit'){{ $quantityUnit->name_plural }}@endif">
</div>
@if($pluralCount > 2)
@ -42,13 +42,13 @@
{{ $__t('Plural rule') }}: {{ $pluralRule }}
</span>
</label>
<textarea class="form-control" rows="3" id="plural_forms" name="plural_forms">@if($mode == 'edit'){{ $quantityunit->plural_forms }}@endif</textarea>
<textarea class="form-control" rows="3" id="plural_forms" name="plural_forms">@if($mode == 'edit'){{ $quantityUnit->plural_forms }}@endif</textarea>
</div>
@endif
<div class="form-group">
<label for="description">{{ $__t('Description') }}</label>
<textarea class="form-control" rows="2" id="description" name="description">@if($mode == 'edit'){{ $quantityunit->description }}@endif</textarea>
<textarea class="form-control" rows="2" id="description" name="description">@if($mode == 'edit'){{ $quantityUnit->description }}@endif</textarea>
</div>
@include('components.userfieldsform', array(
@ -60,5 +60,46 @@
</form>
</div>
<div class="col-lg-6 col-xs-12">
<h2>
{{ $__t('Default conversions') }}
<a id="qu-conversion-add-button" class="btn btn-outline-dark" href="#">
<i class="fas fa-plus"></i> {{ $__t('Add') }}
</a>
</h2>
<h5 class="text-muted font-italic">{{ $__t('1 %s is the same as...', $quantityUnit->name) }}</h5>
<table id="qu-conversions-table" class="table table-sm table-striped dt-responsive">
<thead>
<tr>
<th class="border-right"></th>
<th>{{ $__t('Factor') }}</th>
<th>{{ $__t('Unit') }}</th>
</tr>
</thead>
<tbody class="d-none">
@if($mode == "edit")
@foreach($defaultQuConversions as $defaultQuConversion)
<tr>
<td class="fit-content border-right">
<a class="btn btn-sm btn-info qu-conversion-edit-button" href="#" data-qu-conversion-id="{{ $defaultQuConversion->id }}">
<i class="fas fa-edit"></i>
</a>
<a class="btn btn-sm btn-danger qu-conversion-delete-button" href="#" data-qu-conversion-id="{{ $defaultQuConversion->id }}">
<i class="fas fa-trash"></i>
</a>
</td>
<td>
{{ $defaultQuConversion->factor }}
</td>
<td>
{{ FindObjectInArrayByPropertyValue($quantityUnits, 'id', $defaultQuConversion->to_qu_id)->name }}
</td>
</tr>
@endforeach
@endif
</tbody>
</table>
</div>
</div>
@stop