Grocycode: Display in UI, make Images downloadable

This commit is contained in:
Katharina Bogad 2021-06-07 01:41:09 +02:00
parent aa4675589c
commit 866e64acc0
9 changed files with 139 additions and 16 deletions

View File

@ -172,6 +172,34 @@ class StockController extends BaseController
} }
} }
public function ProductGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{
$product = $this->getDatabase()->products($args['productId']);
$gc = new Grocycode(Grocycode::PRODUCT, $product->id);
$png = (new DatamatrixFactory())->setCode((string) $gc)->getDatamatrixPngData();
$isDownload = $request->getQueryParam('download', false);
if ($isDownload)
{
$response = $response->withHeader('Content-Type', 'application/octet-stream')
->withHeader('Content-Disposition', 'attachment; filename=grocycode.png')
->withHeader('Content-Length', strlen($png))
->withHeader('Cache-Control', 'no-cache')
->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT');
}
else
{
$response = $response->withHeader('Content-Type', 'image/png')
->withHeader('Content-Length', strlen($png))
->withHeader('Cache-Control', 'no-cache')
->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT');
}
$response->getBody()->write($png);
return $response;
}
public function ProductGroupEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function ProductGroupEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
if ($args['productGroupId'] == 'new') if ($args['productGroupId'] == 'new')
@ -436,11 +464,24 @@ class StockController extends BaseController
$gc = new Grocycode(Grocycode::PRODUCT, $stockEntry->product_id, [$stockEntry->stock_id]); $gc = new Grocycode(Grocycode::PRODUCT, $stockEntry->product_id, [$stockEntry->stock_id]);
$png = (new DatamatrixFactory())->setCode((string) $gc)->getDatamatrixPngData(); $png = (new DatamatrixFactory())->setCode((string) $gc)->getDatamatrixPngData();
$isDownload = $request->getQueryParam('download', false);
if ($isDownload)
{
$response = $response->withHeader('Content-Type', 'application/octet-stream')
->withHeader('Content-Disposition', 'attachment; filename=grocycode.png')
->withHeader('Content-Length', strlen($png))
->withHeader('Cache-Control', 'no-cache')
->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT');
}
else
{
$response = $response->withHeader('Content-Type', 'image/png') $response = $response->withHeader('Content-Type', 'image/png')
->withHeader('Content-Length', strlen($png)) ->withHeader('Content-Length', strlen($png))
->withHeader('Cache-Control', 'no-cache') ->withHeader('Cache-Control', 'no-cache')
->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT'); ->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT');
$response->getBody()->write($png); $response->getBody()->write($png);
}
return $response; return $response;
} }

View File

@ -2374,3 +2374,18 @@ msgstr "Druckoptionen"
msgid "A product or a note is required" msgid "A product or a note is required"
msgstr "Ein Produkt oder eine Notiz ist erforderlich" msgstr "Ein Produkt oder eine Notiz ist erforderlich"
msgid "Grocycode"
msgstr "Grocycode"
msgid "Download"
msgstr "Herunterladen"
msgid "Download stock entry grocycode"
msgstr "Bestandseingrags-Grocycode herunterladen"
msgid "Download product grocycode"
msgstr "Produkt-Grocycode herunterladen"
msgid "Grocycode is a unique referer to this product in your grocy instance. Print it onto a label and scan it like any other barcode!"
msgstr "Grocycode ist eine eindeutige Referenz zu diesem Produkt in dieser Grocy-Instanz. Ausgedruckt kann es wie jeder andere Barcode gescannt werden!"

View File

@ -2077,3 +2077,26 @@ msgstr ""
msgid "A product or a note is required" msgid "A product or a note is required"
msgstr "" msgstr ""
msgid "Grocycode"
msgstr ""
msgid "Download"
msgstr ""
msgid "Download stock entry grocycode"
msgstr ""
msgid "Download product grocycode"
msgstr ""
msgid ""
"If enabled, the min. stock amount of sub products will be accumulated into this product, means the sub product will never be \"\n"
"\t\t\t\t\t\t\tmissing\",\n"
"\t\t\t\t\t\t\tonly\n"
"\t\t\t\t\t\t\tthis\n"
"\t\t\t\t\t\t\tproduct"
msgstr ""
msgid "Grocycode is a unique referer to this product in your grocy instance. Print it onto a label and scan it like any other barcode!"
msgstr ""

View File

@ -23,6 +23,10 @@ Grocy.Components.ProductCard.Refresh = function(productId)
} }
$('#productcard-product-spoil-rate').text((parseFloat(productDetails.spoil_rate_percent) / 100).toLocaleString(undefined, { style: "percent" })); $('#productcard-product-spoil-rate').text((parseFloat(productDetails.spoil_rate_percent) / 100).toLocaleString(undefined, { style: "percent" }));
// grocycode
$("#productcard-product-grocycode-image").attr("src", U('/product/' + productDetails.product.id + '/grocycode'));
$("#productcard-product-grocycode-image-link").attr("href", U('/product/' + productDetails.product.id + '/grocycode?download=true'));
if (productDetails.is_aggregated_amount == 1) if (productDetails.is_aggregated_amount == 1)
{ {
$('#productcard-product-stock-amount-aggregated').text(productDetails.stock_amount_aggregated); $('#productcard-product-stock-amount-aggregated').text(productDetails.stock_amount_aggregated);

View File

@ -5,8 +5,7 @@ use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Routing\RouteCollectorProxy; use Slim\Routing\RouteCollectorProxy;
$app->group('', function (RouteCollectorProxy $group) $app->group('', function (RouteCollectorProxy $group) {
{
// System routes // System routes
$group->get('/', '\Grocy\Controllers\SystemController:Root')->setName('root'); $group->get('/', '\Grocy\Controllers\SystemController:Root')->setName('root');
$group->get('/about', '\Grocy\Controllers\SystemController:About'); $group->get('/about', '\Grocy\Controllers\SystemController:About');
@ -41,6 +40,7 @@ $app->group('', function (RouteCollectorProxy $group)
$group->get('/quantityunitconversion/{quConversionId}', '\Grocy\Controllers\StockController:QuantityUnitConversionEditForm'); $group->get('/quantityunitconversion/{quConversionId}', '\Grocy\Controllers\StockController:QuantityUnitConversionEditForm');
$group->get('/productgroups', '\Grocy\Controllers\StockController:ProductGroupsList'); $group->get('/productgroups', '\Grocy\Controllers\StockController:ProductGroupsList');
$group->get('/productgroup/{productGroupId}', '\Grocy\Controllers\StockController:ProductGroupEditForm'); $group->get('/productgroup/{productGroupId}', '\Grocy\Controllers\StockController:ProductGroupEditForm');
$group->get('/product/{productId}/grocycode', '\Grocy\Controllers\StockController:ProductGrocycodeImage');
// Stock handling routes // Stock handling routes
if (GROCY_FEATURE_FLAG_STOCK) if (GROCY_FEATURE_FLAG_STOCK)
@ -140,8 +140,7 @@ $app->group('', function (RouteCollectorProxy $group)
$group->get('/manageapikeys/new', '\Grocy\Controllers\OpenApiController:CreateNewApiKey'); $group->get('/manageapikeys/new', '\Grocy\Controllers\OpenApiController:CreateNewApiKey');
}); });
$app->group('/api', function (RouteCollectorProxy $group) $app->group('/api', function (RouteCollectorProxy $group) {
{
// OpenAPI // OpenAPI
$group->get('/openapi/specification', '\Grocy\Controllers\OpenApiController:DocumentationSpec'); $group->get('/openapi/specification', '\Grocy\Controllers\OpenApiController:DocumentationSpec');
@ -247,7 +246,6 @@ $app->group('/api', function (RouteCollectorProxy $group)
})->add(JsonMiddleware::class); })->add(JsonMiddleware::class);
// Handle CORS preflight OPTIONS requests // Handle CORS preflight OPTIONS requests
$app->options('/api/{routes:.+}', function (Request $request, Response $response): Response $app->options('/api/{routes:.+}', function (Request $request, Response $response): Response {
{
return $response->withStatus(204); return $response->withStatus(204);
}); });

View File

@ -55,6 +55,11 @@
@if (GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)<strong>{{ $__t('Average price') }}:</strong> <span id="productcard-product-average-price"></span><br>@endif @if (GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)<strong>{{ $__t('Average price') }}:</strong> <span id="productcard-product-average-price"></span><br>@endif
@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)<strong>{{ $__t('Average shelf life') }}:</strong> <span id="productcard-product-average-shelf-life"></span><br>@endif @if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)<strong>{{ $__t('Average shelf life') }}:</strong> <span id="productcard-product-average-shelf-life"></span><br>@endif
<strong>{{ $__t('Spoil rate') }}:</strong> <span id="productcard-product-spoil-rate"></span> <strong>{{ $__t('Spoil rate') }}:</strong> <span id="productcard-product-spoil-rate"></span>
<p><strong>{{ $__t('Grocycode') }}:</strong><br><img id="productcard-product-grocycode-image"
data-src=""
class="lazy"><br><a href="#"
target="_blank"
id="productcard-product-grocycode-image-link">{{ $__t('Download') }}</a></p>
<p class="w-75 mt-3 mx-auto"><img id="productcard-product-picture" <p class="w-75 mt-3 mx-auto"><img id="productcard-product-picture"
data-src="" data-src=""

View File

@ -25,7 +25,11 @@
@if($mode == 'edit') @if($mode == 'edit')
<script> <script>
Grocy.EditObjectId = {{ $product->id }}; Grocy.EditObjectId = {
{
$product - > id
}
};
</script> </script>
@if(!empty($product->picture_file_name)) @if(!empty($product->picture_file_name))
@ -145,7 +149,12 @@
for="cumulate_min_stock_amount_of_sub_products">{{ $__t('Accumulate sub products min. stock amount') }} for="cumulate_min_stock_amount_of_sub_products">{{ $__t('Accumulate sub products min. stock amount') }}
&nbsp;<i class="fas fa-question-circle text-muted" &nbsp;<i class="fas fa-question-circle text-muted"
data-toggle="tooltip" data-toggle="tooltip"
title="{{ $__t('If enabled, the min. stock amount of sub products will be accumulated into this product, means the sub product will never be "missing", only this product') }}"></i> title="{{ $__t('If enabled, the min. stock amount of sub products will be accumulated into this product, means the sub product will never be "
missing",
only
this
product')
}}"></i>
</label> </label>
</div> </div>
</div> </div>
@ -426,7 +435,24 @@
</div> </div>
<div class="col-lg-6 col-xs-12 @if($mode == 'create') d-none @endif"> <div class="col-lg-6 col-xs-12 @if($mode == 'create') d-none @endif">
<div class="row @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif">
<div class="row">
<div class="col clearfix">
<div class="title-related-links">
<h4>
{{ $__t('Grocycode') }}
</h4>
<p>
<img src="{{ $U('/product/' . $product->id . '/grocycode') }}"
class="float-lg-left mr-2">
{{ $__t('Grocycode is a unique referer to this product in your grocy instance. Print it onto a label and scan it like any other barcode!') }}<br>
<a href="{{ $U('/product/' . $product->id . '/grocycode?download=true') }}">{{ $__t('Download') }}</a>
</p>
</div>
</div>
</div>
<div class="row @if(!GROCY_FEATURE_FLAG_STOCK) d-none @endif mt-5">
<div class="col"> <div class="col">
<div class="title-related-links"> <div class="title-related-links">
<h4> <h4>

View File

@ -185,6 +185,11 @@
href="#"> href="#">
{{ $__t('Product overview') }} {{ $__t('Product overview') }}
</a> </a>
<a class="dropdown-item stockentry-grocycode-link"
type="button"
href="{{ $U('/stockentry/' . $stockEntry->id . '/grocycode?download=true') }}">
{{ $__t('Download stock entry grocycode') }}
</a>
<a class="dropdown-item show-as-dialog-link" <a class="dropdown-item show-as-dialog-link"
type="button" type="button"
href="{{ $U('/stockjournal?embedded&product=') }}{{ $stockEntry->product_id }}"> href="{{ $U('/stockjournal?embedded&product=') }}{{ $stockEntry->product_id }}">

View File

@ -277,6 +277,11 @@
href="#"> href="#">
<span class="dropdown-item-text">{{ $__t('Product overview') }}</span> <span class="dropdown-item-text">{{ $__t('Product overview') }}</span>
</a> </a>
<a class="dropdown-item stockentry-grocycode-link"
type="button"
href="{{ $U('/product/' . $currentStockEntry->product_id . '/grocycode?download=true') }}">
{{ $__t('Download product grocycode') }}
</a>
<a class="dropdown-item show-as-dialog-link" <a class="dropdown-item show-as-dialog-link"
type="button" type="button"
href="{{ $U('/stockentries?embedded&product=') }}{{ $currentStockEntry->product_id }}" href="{{ $U('/stockentries?embedded&product=') }}{{ $currentStockEntry->product_id }}"
@ -308,7 +313,8 @@
<td> <td>
@if($currentStockEntry->product_group_name !== null){{ $currentStockEntry->product_group_name }}@endif @if($currentStockEntry->product_group_name !== null){{ $currentStockEntry->product_group_name }}@endif
</td> </td>
<td data-order={{ $currentStockEntry->amount }}> <td data-order={{
$currentStockEntry->amount }}>
<span id="product-{{ $currentStockEntry->product_id }}-amount" <span id="product-{{ $currentStockEntry->product_id }}-amount"
class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->amount }}</span> <span id="product-{{ $currentStockEntry->product_id }}-qu-name">{{ $__n($currentStockEntry->amount, $currentStockEntry->qu_unit_name, $currentStockEntry->qu_unit_name_plural) }}</span> class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->amount }}</span> <span id="product-{{ $currentStockEntry->product_id }}-qu-name">{{ $__n($currentStockEntry->amount, $currentStockEntry->qu_unit_name, $currentStockEntry->qu_unit_name_plural) }}</span>
<span id="product-{{ $currentStockEntry->product_id }}-opened-amount" <span id="product-{{ $currentStockEntry->product_id }}-opened-amount"