Grocycode, label printing (#1500)

* Grocycode: Productpicker, StockService

* Grocycode: Datamatrix generation

* Grocycode: Display in UI, make Images downloadable

* Grocycode: Do not show on product card

* Grocycode: Stockentry Label view

* Grocycode: Webhooks & Labelprinter Feature

* Grocycode: Manual Label printing

* Grocycode: Print Label from product form

* Quagga2: use zxing for DataMatrix recognition

* Grocycode: Default settings for label printing

* Prepare merge of master

* Grocycode: docs

* Docs: label printing webhook

* Review

- "grocy" is currently written lower-case everywhere, so let's do this also for "grocycode"
- Unified phrases / capitalization
- Minor UI adjustments (mainly context menu item ordering / ordering/spacing on product edit page)
- Documented API changes for Swagger UI (grocy.openapi.json)
- Reverted German localizations (those are managed via Transifex; would cause conflicts when manually edited - will import them later there)
- Reverted a somehow messed up localization string (productform/help text for `cumulate_min_stock_amount_of_sub_products`)
- Suppress deprecation warnings when generating Datamatrix PNG (otherwise the PNG is invalid, https://github.com/jucksearm/php-barcode/issues/3)
- Default `FEATURE_FLAG_LABELPRINTER` to disabled

Co-authored-by: Bernd Bestel <bernd@berrnd.de>
This commit is contained in:
Katharina Bogad
2021-06-12 17:21:12 +02:00
committed by GitHub
parent d23fda245e
commit 2471e78188
32 changed files with 27100 additions and 25 deletions

View File

@@ -6,6 +6,7 @@
@push('pageScripts')
<script src="{{ $U('/node_modules/@ericblade/quagga2/dist/quagga.min.js?v=', true) }}{{ $version }}"></script>
<script src="{{ $U('/components_unmanaged/quagga2-reader-datamatrix/index.js', true) }}?v={{ $version }}"></script>
@endpush
@push('pageStyles')
@@ -21,8 +22,7 @@
.combobox-container #barcodescanner-start-button {
margin-right: 36px !important;
}
</style>
@endpush
@endif
@endif

View File

@@ -68,4 +68,4 @@
class="font-italic d-none">{{ $__t('No price history available') }}</span>
@endif
</div>
</div>
</div>

View File

@@ -95,6 +95,14 @@
Grocy.CalendarShowWeekNumbers = {{ BoolToString(GROCY_CALENDAR_SHOW_WEEK_OF_YEAR) }};
Grocy.GettextPo = {!! $GettextPo !!};
Grocy.FeatureFlags = {!! json_encode($featureFlags) !!};
Grocy.Webhooks = {
@if(GROCY_FEATURE_FLAG_LABELPRINTER && !GROCY_LABEL_PRINTER_RUN_SERVER)
"labelprinter" : {
"hook" : "{{ GROCY_LABEL_PRINTER_WEBHOOK}}",
"extra_data" : {!! json_encode(GROCY_LABEL_PRINTER_PARAMS) !!}
}
@endif
};
@if (GROCY_AUTHENTICATED)
Grocy.UserSettings = {!! json_encode($userSettings) !!};

View File

@@ -400,6 +400,57 @@
'entity' => 'products'
))
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
<div class="form-group">
<div class="custom-control custom-checkbox">
<input @if($mode=='edit'
&&
$product->allow_label_per_unit == 1) checked @endif class="form-check-input custom-control-input" type="checkbox" id="allow_label_per_unit" name="allow_label_per_unit" value="1">
<label class="form-check-label custom-control-label"
for="allow_label_per_unit">{{ $__t('Allow label printing per unit') }}&nbsp;<i class="fas fa-question-circle text-muted"
data-toggle="tooltip"
title="{{ $__t('Allow printing of one label per unit on purchase (after conversion) - e.g. 1 purchased pack adding 10 pieces of stock would print 10 labels') }}"></i>
</label>
</div>
</div>
@php
$no_label = "";
$single_label = "";
$per_unit_label = "";
$disable_per_unit = "";
if($mode == 'edit') {
switch($product->default_print_stock_label) {
case 0: $no_label = "selected"; break;
case 1: $single_label = "selected"; break;
case 2: $per_unit_label = "selected"; break;
default: break; // yolo
}
if($product->allow_label_per_unit == 0) {
$disable_per_unit="disabled";
$per_unit_label = "";
}
}
@endphp
<div class="form-group">
<label for="default_print_stock_label">{{ $__t('Stock entry label') }}</label>
<select class="form-control"
id="default_print_stock_label"
name="default_print_stock_label">
<option value="0"
{{ $no_label }}>{{ $__t('No label') }}</option>
<option value="1"
{{ $single_label }}>{{ $__t('Single label') }}</option>
<option value="2"
{{ $per_unit_label }}
{{ $disable_per_unit }}
id="label-option-per-unit">{{ $__t('Label per unit') }}</option>
</select>
</div>
@endif
<div class="form-group">
<div class="custom-control custom-checkbox">
<input @if($mode=='edit'
@@ -426,6 +477,7 @@
</div>
<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="col">
<div class="title-related-links">
@@ -533,6 +585,34 @@
</div>
</div>
<div class="row mt-2">
<div class="col clearfix">
<div class="title-related-links">
<h4>
{{ $__t('grocycode') }}
<i class="fas fa-question-circle text-muted"
data-toggle="tooltip"
title="{{ $__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') }}"></i>
</h4>
<p>
<img src="{{ $U('/product/' . $product->id . '/grocycode?size=60') }}"
class="float-lg-left">
</p>
<p>
<a class="btn btn-outline-primary btn-sm"
href="{{ $U('/product/' . $product->id . '/grocycode?download=true') }}">{{ $__t('Download') }}</a>
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
<a class="btn btn-outline-primary btn-sm stockentry-grocycode-product-label-print"
data-product-id="{{ $product->id }}"
href="#">
{{ $__t('Print on label printer') }}
</a>
@endif
</p>
</div>
</div>
</div>
<div class="row @if(GROCY_FEATURE_FLAG_STOCK) mt-5 @endif">
<div class="col">
<div class="title-related-links">

View File

@@ -12,7 +12,7 @@
<script>
Grocy.QuantityUnits = {!! json_encode($quantityUnits) !!};
Grocy.QuantityUnitConversionsResolved = {!! json_encode($quantityUnitConversionsResolved) !!};
Grocy.DefaultMinAmount = '{{$DEFAULT_MIN_AMOUNT}}';
Grocy.DefaultMinAmount = '{{ $DEFAULT_MIN_AMOUNT }}';
</script>
<div class="row">
@@ -148,6 +148,21 @@
))
@endif
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
<div class="form-group">
<label for="print_stock_label">{{ $__t('Stock entry label') }}</label>
<select class="form-control"
id="print_stock_label"
name="print_stock_label">
<option value="0">{{ $__t('No label') }}</option>
<option value="1">{{ $__t('Single label') }}</option>
<option value="2"
id="label-option-per-unit">{{ $__t('Label per unit') }}</option>
</select>
<div class="invalid-feedback">{{ $__t('A quantity unit is required') }}</div>
</div>
@endif
<button id="save-purchase-button"
class="btn btn-success d-block">{{ $__t('OK') }}</button>

View File

@@ -200,6 +200,26 @@
href="{{ $U('/product/') }}{{ $stockEntry->product_id . '?returnto=/stockentries' }}">
{{ $__t('Edit product') }}
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item stockentry-grocycode-link"
type="button"
href="{{ $U('/stockentry/' . $stockEntry->id . '/grocycode?download=true') }}">
{{ $__t('Download stock entry grocycode') }}
</a>
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
<a class="dropdown-item stockentry-grocycode-stockentry-label-print"
data-stock-id="{{ $stockEntry->id }}"
type="button"
href="#">
{{ $__t('Print stock entry grocycode on label printer') }}
</a>
@endif
<a class="dropdown-item stockentry-label-link"
type="button"
target="_blank"
href="{{ $U('/stockentry/' . $stockEntry->id . '/label') }}">
{{ $__t('Open stock entry print label in new window') }}
</a>
</div>
</div>
</td>

View File

@@ -0,0 +1,40 @@
<html>
<head>
<title>{{ $product->name }}</title>
<link href="{{ $U('/components_unmanaged/noto-sans-v11-latin/noto-sans-v11-latin.min.css?v=', true) }}{{ $version }}"
rel="stylesheet">
<style>
body {
font-family: 'Noto Sans', sans-serif;
}
img {
float: left;
margin-right: .5rem;
max-height: 25px;
width: auto;
margin-top: 2px;
}
.productname {
font-size: 20px;
display: inline-block;
font-weight: bold;
}
</style>
</head>
<body>
<p>
<!-- Size gets determined by CSS, so printing works better (more pixels = sharper printed image).
Unfortunately, this also means the code is blurred on screen. -->
<img src="{{ $U('/stockentry/'. $stockEntry->id . '/grocycode?size=100') }}">
<span class="productname">{{ $product->name }}</span><br>
@if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
<span>{{ $__t('DD') }}: {{ $stockEntry->best_before_date }}</span>
@endif
</p>
</body>
</html>

View File

@@ -298,6 +298,20 @@
href="{{ $U('/product/') }}{{ $currentStockEntry->product_id . '?returnto=%2Fstockoverview' }}">
<span class="dropdown-item-text">{{ $__t('Edit product') }}</span>
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item stockentry-grocycode-link"
type="button"
href="{{ $U('/product/' . $currentStockEntry->product_id . '/grocycode?download=true') }}">
{{ $__t('Download product grocycode') }}
</a>
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
<a class="dropdown-item stockentry-grocycode-product-label-print"
data-product-id="{{ $currentStockEntry->product_id }}"
type="button"
href="#">
{{ $__t('Print product grocycode on label printer') }}
</a>
@endif
</div>
</div>
</td>
@@ -308,7 +322,8 @@
<td>
@if($currentStockEntry->product_group_name !== null){{ $currentStockEntry->product_group_name }}@endif
</td>
<td data-order={{ $currentStockEntry->amount }}>
<td data-order={{
$currentStockEntry->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>
<span id="product-{{ $currentStockEntry->product_id }}-opened-amount"