mirror of
https://github.com/grocy/grocy.git
synced 2026-04-05 12:26:15 +02:00
Merge remote-tracking branch 'upstream/master' into modernjs
This commit is contained in:
commit
93b2c00600
|
|
@ -13,6 +13,14 @@
|
|||
- https://github.com/grocy/grocy/blob/master/docs/label-printing.md
|
||||
- (Thanks a lot @mistressofjellyfish)
|
||||
|
||||
### New feature: Shopping list thermal printer support
|
||||
- The shopping list can now be printed on a thermal printer
|
||||
- The printer must compatible to the `ESC/POS` protocol and needs to be locally attached or network reachable to/by the machine hosting grocy (so the server)
|
||||
- See the new `TPRINTER*` `config.php` options to configure the printer connection and other options
|
||||
- => New button on the shopping list print dialog
|
||||
- Can be enabled via the new feature flag `FEATURE_FLAG_THERMAL_PRINTER` (defaults to disabled)
|
||||
- (Thanks a lot @Forceu)
|
||||
|
||||
### Stock improvements/fixes
|
||||
- Product barcodes are now enforced to be unique across products
|
||||
- Fixed that editing stock entries was not possible
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
"gumlet/php-image-resize": "^1.9",
|
||||
"ezyang/htmlpurifier": "^4.13",
|
||||
"jucksearm/php-barcode": "^1.0",
|
||||
"guzzlehttp/guzzle": "^7.0"
|
||||
"guzzlehttp/guzzle": "^7.0",
|
||||
"mike42/escpos-php": "^3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
|
|||
110
composer.lock
generated
110
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "b8b8e77618038c44c21edac2c31c4b67",
|
||||
"content-hash": "9c4580f416241994ddaffb569998b7f8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "doctrine/inflector",
|
||||
|
|
@ -1113,6 +1113,112 @@
|
|||
},
|
||||
"time": "2017-06-05T04:41:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mike42/escpos-php",
|
||||
"version": "v3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mike42/escpos-php.git",
|
||||
"reference": "dcb569a123d75f9f6a4a927aae7625ca6b7fdcf3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mike42/escpos-php/zipball/dcb569a123d75f9f6a4a927aae7625ca6b7fdcf3",
|
||||
"reference": "dcb569a123d75f9f6a4a927aae7625ca6b7fdcf3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-zlib": "*",
|
||||
"mike42/gfx-php": "^0.6",
|
||||
"php": ">=7.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.5",
|
||||
"squizlabs/php_codesniffer": "^3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "Used for image printing if present.",
|
||||
"ext-imagick": "Will be used for image printing if present. Required for PDF printing or use of custom fonts."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mike42\\": "src/Mike42"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Billington",
|
||||
"email": "michael.billington@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP receipt printer library for use with ESC/POS-compatible thermal and impact printers",
|
||||
"homepage": "https://github.com/mike42/escpos-php",
|
||||
"keywords": [
|
||||
"Epson",
|
||||
"barcode",
|
||||
"escpos",
|
||||
"printer",
|
||||
"receipt-printer"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/mike42/escpos-php/issues",
|
||||
"source": "https://github.com/mike42/escpos-php/tree/v3.0"
|
||||
},
|
||||
"time": "2019-10-13T06:27:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mike42/gfx-php",
|
||||
"version": "v0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mike42/gfx-php.git",
|
||||
"reference": "ed9ded2a9298e4084a9c557ab74a89b71e43dbdb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mike42/gfx-php/zipball/ed9ded2a9298e4084a9c557ab74a89b71e43dbdb",
|
||||
"reference": "ed9ded2a9298e4084a9c557ab74a89b71e43dbdb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpbench/phpbench": "@dev",
|
||||
"phpunit/phpunit": "^6.5",
|
||||
"squizlabs/php_codesniffer": "^3.3.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mike42\\": "src/Mike42"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Billington",
|
||||
"email": "michael.billington@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "The pure PHP graphics library",
|
||||
"homepage": "https://github.com/mike42/gfx-php",
|
||||
"support": {
|
||||
"issues": "https://github.com/mike42/gfx-php/issues",
|
||||
"source": "https://github.com/mike42/gfx-php/tree/v0.6"
|
||||
},
|
||||
"time": "2019-10-05T02:44:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "morris/lessql",
|
||||
"version": "v0.4.1",
|
||||
|
|
@ -2865,5 +2971,5 @@
|
|||
"php": ">=7.4"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.1.0"
|
||||
"plugin-api-version": "2.0.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,22 @@ Setting('MEAL_PLAN_FIRST_DAY_OF_WEEK', '');
|
|||
// see the file controllers/Users/User.php for possible values
|
||||
Setting('DEFAULT_PERMISSIONS', ['ADMIN']);
|
||||
|
||||
// When using a thermal printer (thermal printers are receipt printers, not regular printers)
|
||||
// The printer must support the ESC/POS protocol, see https://github.com/mike42/escpos-php
|
||||
Setting('TPRINTER_IS_NETWORK_PRINTER', false); // Set to true if it is a network printer
|
||||
Setting('TPRINTER_PRINT_QUANTITY_NAME', true); // Set to false if you do not want to print the quantity names
|
||||
Setting('TPRINTER_PRINT_NOTES', true); // Set to false if you do not want to print notes
|
||||
|
||||
//Configuration below for network printers. If you are using a USB/serial printer, skip to next section
|
||||
Setting('TPRINTER_IP', '127.0.0.1'); // IP of the network printer
|
||||
Setting('TPRINTER_PORT', 9100); // Port of printer, eg. 9100
|
||||
//Configuration below if you are using a USB or serial printer
|
||||
Setting('TPRINTER_CONNECTOR', '/dev/usb/lp0'); // Location of printer. For USB on Linux this is often '/dev/usb/lp0',
|
||||
// for serial printers it could be similar to '/dev/ttyS0'
|
||||
// Make sure that the user that runs the webserver has permissions to write to the printer!
|
||||
// On Linux add your webserver user to the LP group with usermod -a -G lp www-data
|
||||
|
||||
|
||||
// Default user settings
|
||||
// These settings can be changed per user, here the defaults
|
||||
// are defined which are used when the user has not changed the setting so far
|
||||
|
|
@ -198,6 +214,7 @@ Setting('FEATURE_FLAG_STOCK_PRODUCT_FREEZING', true);
|
|||
Setting('FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_FIELD_NUMBER_PAD', true); // Activate the number pad in due date fields on (supported) mobile browsers
|
||||
Setting('FEATURE_FLAG_SHOPPINGLIST_MULTIPLE_LISTS', true);
|
||||
Setting('FEATURE_FLAG_CHORES_ASSIGNMENTS', true);
|
||||
Setting('FEATURE_FLAG_THERMAL_PRINTER', false);
|
||||
|
||||
// Feature settings
|
||||
Setting('FEATURE_SETTING_STOCK_COUNT_OPENED_PRODUCTS_AGAINST_MINIMUM_STOCK_AMOUNT', true); // When set to true, opened items will be counted as missing for calculating if a product is below its minimum stock amount
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use Grocy\Services\ChoresService;
|
|||
use Grocy\Services\DatabaseService;
|
||||
use Grocy\Services\FilesService;
|
||||
use Grocy\Services\LocalizationService;
|
||||
use Grocy\Services\PrintService;
|
||||
use Grocy\Services\RecipesService;
|
||||
use Grocy\Services\SessionService;
|
||||
use Grocy\Services\StockService;
|
||||
|
|
@ -93,6 +94,12 @@ class BaseController
|
|||
return StockService::getInstance();
|
||||
}
|
||||
|
||||
protected function getPrintService()
|
||||
{
|
||||
return PrintService::getInstance();
|
||||
}
|
||||
|
||||
|
||||
protected function getTasksService()
|
||||
{
|
||||
return TasksService::getInstance();
|
||||
|
|
|
|||
41
controllers/PrintApiController.php
Normal file
41
controllers/PrintApiController.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace Grocy\Controllers;
|
||||
|
||||
use Grocy\Controllers\Users\User;
|
||||
use Grocy\Services\StockService;
|
||||
|
||||
class PrintApiController extends BaseApiController
|
||||
{
|
||||
|
||||
public function PrintShoppingListThermal(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) {
|
||||
|
||||
try
|
||||
{
|
||||
User::checkPermission($request, User::PERMISSION_SHOPPINGLIST);
|
||||
|
||||
$params = $request->getQueryParams();
|
||||
|
||||
$listId = 1;
|
||||
if (isset($params['list'])) {
|
||||
$listId = $params['list'];
|
||||
}
|
||||
|
||||
$printHeader = true;
|
||||
if (isset($params['printHeader'])) {
|
||||
$printHeader = ($params['printHeader'] === "true");
|
||||
}
|
||||
$items = $this->getStockService()->GetShoppinglistInPrintableStrings($listId);
|
||||
return $this->ApiResponse($response, $this->getPrintService()->printShoppingList($printHeader, $items));
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct(\DI\Container $container)
|
||||
{
|
||||
parent::__construct($container);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
|
||||
namespace Grocy\Controllers;
|
||||
|
||||
use Grocy\Controllers\Users\User;
|
||||
|
|
|
|||
|
|
@ -512,6 +512,7 @@ class StockController extends BaseController
|
|||
]);
|
||||
}
|
||||
|
||||
|
||||
public function Stockentries(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
$usersService = $this->getUsersService();
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@
|
|||
},
|
||||
{
|
||||
"name": "Files"
|
||||
},
|
||||
{
|
||||
"name": "Print"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
|
|
@ -4030,7 +4033,64 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/print/shoppinglist/thermal": {
|
||||
"get": {
|
||||
"summary": "Prints the shoppinglist with a thermal printer",
|
||||
"tags": [
|
||||
"Print"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "list",
|
||||
"required": false,
|
||||
"description": "Shopping list id",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"default": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "printHeader",
|
||||
"required": false,
|
||||
"description": "Prints grocy logo if true",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Returns OK if the printing was successful",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "The operation was not successful",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error400"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"internalSchemas": {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ class ERequirementNotMet extends Exception
|
|||
{
|
||||
}
|
||||
|
||||
const REQUIRED_PHP_EXTENSIONS = ['fileinfo', 'pdo_sqlite', 'gd', 'ctype'];
|
||||
const REQUIRED_PHP_EXTENSIONS = ['fileinfo', 'pdo_sqlite', 'gd', 'ctype', 'json', 'intl', 'zlib'];
|
||||
const REQUIRED_SQLITE_VERSION = '3.9.0';
|
||||
|
||||
class PrerequisiteChecker
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// this needs to be explicitly imported for some reason,
|
||||
// this needs to be explicitly imported for some reason,
|
||||
// otherwise rollup complains.
|
||||
import bwipjs from '../../node_modules/bwip-js/dist/bwip-js.mjs';
|
||||
import { WindowMessageBag } from '../helpers/messagebag';
|
||||
|
|
@ -433,56 +433,106 @@ $(document).on("click", "#print-shopping-list-button", function(e)
|
|||
</label> \
|
||||
</div>';
|
||||
|
||||
var sizePrintDialog = 'medium';
|
||||
var printButtons = {
|
||||
cancel: {
|
||||
label: __t('Cancel'),
|
||||
className: 'btn-secondary',
|
||||
callback: function()
|
||||
{
|
||||
bootbox.hideAll();
|
||||
}
|
||||
},
|
||||
printtp: {
|
||||
label: __t('Thermal printer'),
|
||||
className: 'btn-secondary',
|
||||
callback: function()
|
||||
{
|
||||
bootbox.hideAll();
|
||||
var printHeader = $("#print-show-header").prop("checked");
|
||||
var thermalPrintDialog = bootbox.dialog({
|
||||
title: __t('Printing'),
|
||||
message: '<p><i class="fa fa-spin fa-spinner"></i> ' + __t('Connecting to printer...') + '</p>'
|
||||
});
|
||||
//Delaying for one second so that the alert can be closed
|
||||
setTimeout(function()
|
||||
{
|
||||
Grocy.Api.Get('print/shoppinglist/thermal?list=' + $("#selected-shopping-list").val() + '&printHeader=' + printHeader,
|
||||
function(result)
|
||||
{
|
||||
bootbox.hideAll();
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
var validResponse = true;
|
||||
try
|
||||
{
|
||||
var jsonError = JSON.parse(xhr.responseText);
|
||||
} catch (e)
|
||||
{
|
||||
validResponse = false;
|
||||
}
|
||||
if (validResponse)
|
||||
{
|
||||
thermalPrintDialog.find('.bootbox-body').html(__t('Unable to print') + '<br><pre><code>' + jsonError.error_message + '</pre></code>');
|
||||
} else
|
||||
{
|
||||
thermalPrintDialog.find('.bootbox-body').html(__t('Unable to print') + '<br><pre><code>' + xhr.responseText + '</pre></code>');
|
||||
}
|
||||
}
|
||||
);
|
||||
}, 1000);
|
||||
}
|
||||
},
|
||||
ok: {
|
||||
label: __t('Print'),
|
||||
className: 'btn-primary responsive-button',
|
||||
callback: function()
|
||||
{
|
||||
bootbox.hideAll();
|
||||
$('.modal-backdrop').remove();
|
||||
$(".print-timestamp").text(moment().format("l LT"));
|
||||
|
||||
$("#description-for-print").html($("#description").val());
|
||||
if ($("#description").text().isEmpty())
|
||||
{
|
||||
$("#description-for-print").parent().addClass("d-print-none");
|
||||
}
|
||||
|
||||
if (!$("#print-show-header").prop("checked"))
|
||||
{
|
||||
$("#print-header").addClass("d-none");
|
||||
}
|
||||
|
||||
if (!$("#print-group-by-product-group").prop("checked"))
|
||||
{
|
||||
shoppingListPrintShadowTable.rowGroup().enable(false);
|
||||
shoppingListPrintShadowTable.order.fixed({});
|
||||
shoppingListPrintShadowTable.draw();
|
||||
}
|
||||
|
||||
$(".print-layout-container").addClass("d-none");
|
||||
$("." + $("input[name='print-layout-type']:checked").val()).removeClass("d-none");
|
||||
|
||||
window.print();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Grocy.FeatureFlags["GROCY_FEATURE_FLAG_THERMAL_PRINTER"])
|
||||
{
|
||||
delete printButtons['printtp'];
|
||||
sizePrintDialog = 'small';
|
||||
}
|
||||
|
||||
bootbox.dialog({
|
||||
message: dialogHtml,
|
||||
size: 'small',
|
||||
size: sizePrintDialog,
|
||||
backdrop: true,
|
||||
closeButton: false,
|
||||
className: "d-print-none",
|
||||
buttons: {
|
||||
cancel: {
|
||||
label: __t('Cancel'),
|
||||
className: 'btn-secondary',
|
||||
callback: function()
|
||||
{
|
||||
bootbox.hideAll();
|
||||
}
|
||||
},
|
||||
ok: {
|
||||
label: __t('Print'),
|
||||
className: 'btn-primary responsive-button',
|
||||
callback: function()
|
||||
{
|
||||
bootbox.hideAll();
|
||||
$('.modal-backdrop').remove();
|
||||
|
||||
$(".print-timestamp").text(moment().format("l LT"));
|
||||
|
||||
$("#description-for-print").html($("#description").val());
|
||||
if ($("#description").text().isEmpty())
|
||||
{
|
||||
$("#description-for-print").parent().addClass("d-print-none");
|
||||
}
|
||||
|
||||
if (!$("#print-show-header").prop("checked"))
|
||||
{
|
||||
$("#print-header").addClass("d-none");
|
||||
}
|
||||
|
||||
if (!$("#print-group-by-product-group").prop("checked"))
|
||||
{
|
||||
shoppingListPrintShadowTable.rowGroup().enable(false);
|
||||
shoppingListPrintShadowTable.order.fixed({});
|
||||
shoppingListPrintShadowTable.draw();
|
||||
}
|
||||
|
||||
$(".print-layout-container").addClass("d-none");
|
||||
$("." + $("input[name='print-layout-type']:checked").val()).removeClass("d-none");
|
||||
|
||||
window.print();
|
||||
}
|
||||
}
|
||||
}
|
||||
buttons: printButtons
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2138,3 +2138,15 @@ msgstr ""
|
|||
|
||||
msgid "Day"
|
||||
msgstr ""
|
||||
|
||||
msgid "Thermal printer"
|
||||
msgstr ""
|
||||
|
||||
msgid "Printing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Connecting to printer..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to print"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -232,6 +232,9 @@ $app->group('/api', function (RouteCollectorProxy $group) {
|
|||
$group->post('/chores/executions/{executionId}/undo', '\Grocy\Controllers\ChoresApiController:UndoChoreExecution');
|
||||
$group->post('/chores/executions/calculate-next-assignments', '\Grocy\Controllers\ChoresApiController:CalculateNextExecutionAssignments');
|
||||
|
||||
//Printing
|
||||
$group->get('/print/shoppinglist/thermal', '\Grocy\Controllers\PrintApiController:PrintShoppingListThermal');
|
||||
|
||||
// Batteries
|
||||
$group->get('/batteries', '\Grocy\Controllers\BatteriesApiController:Current');
|
||||
$group->get('/batteries/{batteryId}', '\Grocy\Controllers\BatteriesApiController:BatteryDetails');
|
||||
|
|
|
|||
|
|
@ -66,4 +66,10 @@ class BaseService
|
|||
{
|
||||
return UsersService::getInstance();
|
||||
}
|
||||
|
||||
protected function getPrintService()
|
||||
{
|
||||
return PrintService::getInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
84
services/PrintService.php
Normal file
84
services/PrintService.php
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Grocy\Services;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use Mike42\Escpos\PrintConnectors\NetworkPrintConnector;
|
||||
use Mike42\Escpos\PrintConnectors\FilePrintConnector;
|
||||
use Mike42\Escpos\Printer;
|
||||
|
||||
class PrintService extends BaseService
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Initialises the printer
|
||||
* @return Printer Printer handle
|
||||
* @throws Exception If unable to connect to printer, an exception is thrown
|
||||
*/
|
||||
private static function getPrinterHandle()
|
||||
{
|
||||
if (GROCY_TPRINTER_IS_NETWORK_PRINTER) {
|
||||
$connector = new NetworkPrintConnector(GROCY_TPRINTER_IP, GROCY_TPRINTER_PORT);
|
||||
} else {
|
||||
$connector = new FilePrintConnector(GROCY_TPRINTER_CONNECTOR);
|
||||
}
|
||||
return new Printer($connector);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prints the grocy logo and date
|
||||
* @param Printer $printer Printer handle
|
||||
*/
|
||||
private static function printHeader(Printer $printer)
|
||||
{
|
||||
$date = new DateTime();
|
||||
$dateFormatted = $date->format('d/m/Y H:i');
|
||||
|
||||
$printer->setJustification(Printer::JUSTIFY_CENTER);
|
||||
$printer->selectPrintMode(Printer::MODE_DOUBLE_WIDTH);
|
||||
$printer->setTextSize(4, 4);
|
||||
$printer->setReverseColors(true);
|
||||
$printer->text("grocy");
|
||||
$printer->setJustification();
|
||||
$printer->setTextSize(1, 1);
|
||||
$printer->setReverseColors(false);
|
||||
$printer->feed(2);
|
||||
$printer->text($dateFormatted);
|
||||
$printer->selectPrintMode();
|
||||
$printer->feed(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $printHeader Printing of Grocy logo
|
||||
* @param string[] $lines Items to print
|
||||
* @return string[] Returns array with result OK if no exception
|
||||
* @throws Exception If unable to print, an exception is thrown
|
||||
*/
|
||||
public function printShoppingList(bool $printHeader, array $lines): array
|
||||
{
|
||||
$printer = self::getPrinterHandle();
|
||||
if ($printer === false)
|
||||
throw new Exception("Unable to connect to printer");
|
||||
|
||||
if ($printHeader)
|
||||
{
|
||||
self::printHeader($printer);
|
||||
}
|
||||
|
||||
foreach ($lines as $line)
|
||||
{
|
||||
$printer->text($line);
|
||||
$printer->feed();
|
||||
}
|
||||
|
||||
$printer->feed(3);
|
||||
$printer->cut();
|
||||
$printer->close();
|
||||
return [
|
||||
'result' => "OK"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -970,6 +970,88 @@ class StockService extends BaseService
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shoppinglist as an array with lines for a printer
|
||||
* @param int $listId ID of shopping list
|
||||
* @return string[] Returns an array in the format "[amount] [name of product]"
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetShoppinglistInPrintableStrings($listId = 1): array
|
||||
{
|
||||
if (!$this->ShoppingListExists($listId))
|
||||
{
|
||||
throw new \Exception('Shopping list does not exist');
|
||||
}
|
||||
|
||||
$result_product = array();
|
||||
$result_quantity = array();
|
||||
$rowsShoppingListProducts = $this->getDatabase()->uihelper_shopping_list()->where('shopping_list_id = :1', $listId)->fetchAll();
|
||||
foreach ($rowsShoppingListProducts as $row)
|
||||
{
|
||||
$isValidProduct = ($row->product_id != null && $row->product_id != "");
|
||||
if ($isValidProduct)
|
||||
{
|
||||
$product = $this->getDatabase()->products()->where('id = :1', $row->product_id)->fetch();
|
||||
$conversion = $this->getDatabase()->quantity_unit_conversions_resolved()->where('product_id = :1 AND from_qu_id = :2 AND to_qu_id = :3', $product->id, $product->qu_id_stock, $row->qu_id)->fetch();
|
||||
$factor = 1.0;
|
||||
if ($conversion != null)
|
||||
{
|
||||
$factor = floatval($conversion->factor);
|
||||
}
|
||||
$amount = round($row->amount * $factor);
|
||||
$note = "";
|
||||
if (GROCY_TPRINTER_PRINT_NOTES)
|
||||
{
|
||||
if ($row->note != "") {
|
||||
$note = ' (' . $row->note . ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GROCY_TPRINTER_PRINT_QUANTITY_NAME && $isValidProduct)
|
||||
{
|
||||
$quantityname = $row->qu_name;
|
||||
if ($amount > 1)
|
||||
{
|
||||
$quantityname = $row->qu_name_plural;
|
||||
}
|
||||
array_push($result_quantity, $amount . ' ' . $quantityname);
|
||||
array_push($result_product, $row->product_name . $note);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($isValidProduct)
|
||||
{
|
||||
array_push($result_quantity, $amount);
|
||||
array_push($result_product, $row->product_name . $note);
|
||||
}
|
||||
else
|
||||
{
|
||||
array_push($result_quantity, round($row->amount));
|
||||
array_push($result_product, $row->note);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//Add padding to look nicer
|
||||
$maxlength = 1;
|
||||
foreach ($result_quantity as $quantity)
|
||||
{
|
||||
if (strlen($quantity) > $maxlength)
|
||||
{
|
||||
$maxlength = strlen($quantity);
|
||||
}
|
||||
}
|
||||
$result = array();
|
||||
$length = count($result_quantity);
|
||||
for ($i = 0; $i < $length; $i++)
|
||||
{
|
||||
$quantity = str_pad($result_quantity[$i], $maxlength);
|
||||
array_push($result, $quantity . ' ' . $result_product[$i]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
public function TransferProduct(int $productId, float $amount, int $locationIdFrom, int $locationIdTo, $specificStockEntryId = 'default', &$transactionId = null)
|
||||
{
|
||||
if (!$this->ProductExists($productId))
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user