Squashed commit

Always execute migration 9999 (can be used to fix things manually)
Optimized meal plan navigation / date range filtering
Prepared next release
Pulled translations from Transifex
Various code optimizations
This commit is contained in:
Bernd Bestel
2021-07-16 17:32:08 +02:00
parent 2d1d5d46f6
commit edfa404ed6
58 changed files with 312 additions and 513 deletions

View File

@@ -1,7 +1,7 @@
<div align="center"> <div align="center">
<img alt="Logo" height="50" src="https://raw.githubusercontent.com/grocy/grocy/master/public/img/grocy_logo.svg?sanitize=true"/> <img alt="Logo" height="50" src="https://raw.githubusercontent.com/grocy/grocy/master/public/img/grocy_logo.svg?sanitize=true" />
<h3>ERP beyond your fridge</h3> <h3>ERP beyond your fridge</h3>
<h5> grocy is a web-based self-hosted groceries & household management solution for your home</h5> <h4>grocy is a web-based self-hosted groceries & household management solution for your home<br>Created by <a href="https://github.com/berrnd">@berrnd</a></h4>
</div> </div>
----- -----

View File

@@ -2,7 +2,7 @@
> ⚠️ PHP 8 is now supported and from now on the only tested runtime version (although currently PHP 7.2 should still work). > ⚠️ PHP 8 is now supported and from now on the only tested runtime version (although currently PHP 7.2 should still work).
### New feature: grocycode ### New feature: grocycode / label printer support
#### (Own) Product/stock entry/chores/batteries labels/barcodes #### (Own) Product/stock entry/chores/batteries labels/barcodes
- Print own labels/barcodes for products/stock entries/chores/batteries and then scan that code on every place a product/stock entry/chore/battery can be selected - Print own labels/barcodes for products/stock entries/chores/batteries and then scan that code on every place a product/stock entry/chore/battery can be selected
- Can be printed (or downloaded) via - Can be printed (or downloaded) via
@@ -16,8 +16,8 @@
- Label printer communication happens via WebHooks - see the new `LABEL_PRINTER*` `config.php` options - Label printer communication happens via WebHooks - see the new `LABEL_PRINTER*` `config.php` options
- grocycodes can also be used without a label printer - you can view or download thm as pictures and print them manually - grocycodes can also be used without a label printer - you can view or download thm as pictures and print them manually
- More information: - More information:
- https://github.com/grocy/grocy/blob/master/docs/grocycode.md - https://github.com/grocy/grocy/tree/v3.1.0/docs/grocycode.md
- https://github.com/grocy/grocy/blob/master/docs/label-printing.md - https://github.com/grocy/grocy/tree/v3.1.0/docs/label-printing.md
- (Thanks a lot @mistressofjellyfish for the initial work on this) - (Thanks a lot @mistressofjellyfish for the initial work on this)
### New feature: Meal plan sections ### New feature: Meal plan sections

View File

@@ -175,9 +175,8 @@ DefaultUserSetting('batteries_due_soon_days', 5);
// Tasks settings // Tasks settings
DefaultUserSetting('tasks_due_soon_days', 5); DefaultUserSetting('tasks_due_soon_days', 5);
// If the page should be automatically reloaded when there was // If the page should be automatically reloaded when there was an external change
// an external change DefaultUserSetting('auto_reload_on_db_change', false);
DefaultUserSetting('auto_reload_on_db_change', true);
// Show a clock in the header next to the logo or not // Show a clock in the header next to the logo or not
DefaultUserSetting('show_clock_in_header', false); DefaultUserSetting('show_clock_in_header', false);

View File

@@ -6,18 +6,13 @@ use LessQL\Result;
class BaseApiController extends BaseController class BaseApiController extends BaseController
{ {
protected $OpenApiSpec = null;
const PATTERN_FIELD = '[A-Za-z_][A-Za-z0-9_]+'; const PATTERN_FIELD = '[A-Za-z_][A-Za-z0-9_]+';
const PATTERN_OPERATOR = '!?(=|~|<|>|(>=)|(<=)|(§))'; const PATTERN_OPERATOR = '!?(=|~|<|>|(>=)|(<=)|(§))';
const PATTERN_VALUE = '[A-Za-z\x{0400}-\x{04FF}_0-9.$#^|-]+'; const PATTERN_VALUE = '[A-Za-z\x{0400}-\x{04FF}_0-9.$#^|-]+';
public function __construct(\DI\Container $container) protected $OpenApiSpec = null;
{
parent::__construct($container);
}
protected function ApiResponse(\Psr\Http\Message\ResponseInterface $response, $data, $cache = false) protected function ApiResponse(\Psr\Http\Message\ResponseInterface $response, $data, $cache = false)
{ {

View File

@@ -21,14 +21,14 @@ use Grocy\Services\UsersService;
class BaseController class BaseController
{ {
protected $AppContainer;
public function __construct(\DI\Container $container) public function __construct(\DI\Container $container)
{ {
$this->AppContainer = $container; $this->AppContainer = $container;
$this->View = $container->get('view'); $this->View = $container->get('view');
} }
protected $AppContainer;
protected function getApiKeyService() protected function getApiKeyService()
{ {
return ApiKeyService::getInstance(); return ApiKeyService::getInstance();

View File

@@ -34,7 +34,6 @@ class BatteriesApiController extends BaseApiController
try try
{ {
$trackedTime = date('Y-m-d H:i:s'); $trackedTime = date('Y-m-d H:i:s');
if (array_key_exists('tracked_time', $requestBody) && IsIsoDateTime($requestBody['tracked_time'])) if (array_key_exists('tracked_time', $requestBody) && IsIsoDateTime($requestBody['tracked_time']))
{ {
$trackedTime = $requestBody['tracked_time']; $trackedTime = $requestBody['tracked_time'];
@@ -87,9 +86,4 @@ class BatteriesApiController extends BaseApiController
return $this->GenericErrorResponse($response, $ex->getMessage()); return $this->GenericErrorResponse($response, $ex->getMessage());
} }
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -3,11 +3,11 @@
namespace Grocy\Controllers; namespace Grocy\Controllers;
use Grocy\Helpers\Grocycode; use Grocy\Helpers\Grocycode;
use jucksearm\barcode\lib\BarcodeFactory;
use jucksearm\barcode\lib\DatamatrixFactory;
class BatteriesController extends BaseController class BatteriesController extends BaseController
{ {
use GrocycodeTrait;
public function BatteriesList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function BatteriesList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
if (isset($request->getQueryParams()['include_disabled'])) if (isset($request->getQueryParams()['include_disabled']))
@@ -98,40 +98,7 @@ class BatteriesController extends BaseController
public function BatteryGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function BatteryGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
$size = $request->getQueryParam('size', null);
$gc = new Grocycode(Grocycode::BATTERY, $args['batteryId']); $gc = new Grocycode(Grocycode::BATTERY, $args['batteryId']);
return $this->ServeGrocycodeImage($request, $response, $gc);
if (GROCY_GROCYCODE_TYPE == '2D')
{
$png = (new DatamatrixFactory())->setCode((string) $gc)->setSize($size)->getDatamatrixPngData();
}
else
{
$png = (new BarcodeFactory())->setType('C128')->setCode((string) $gc)->setHeight($size)->getBarcodePngData();
}
$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 __construct(\DI\Container $container)
{
parent::__construct($container);
} }
} }

View File

@@ -72,9 +72,4 @@ class CalendarApiController extends BaseApiController
return $this->GenericErrorResponse($response, $ex->getMessage()); return $this->GenericErrorResponse($response, $ex->getMessage());
} }
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -10,9 +10,4 @@ class CalendarController extends BaseController
'fullcalendarEventSources' => $this->getCalendarService()->GetEvents() 'fullcalendarEventSources' => $this->getCalendarService()->GetEvents()
]); ]);
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -68,14 +68,12 @@ class ChoresApiController extends BaseApiController
User::checkPermission($request, User::PERMISSION_CHORE_TRACK_EXECUTION); User::checkPermission($request, User::PERMISSION_CHORE_TRACK_EXECUTION);
$trackedTime = date('Y-m-d H:i:s'); $trackedTime = date('Y-m-d H:i:s');
if (array_key_exists('tracked_time', $requestBody) && (IsIsoDateTime($requestBody['tracked_time']) || IsIsoDate($requestBody['tracked_time']))) if (array_key_exists('tracked_time', $requestBody) && (IsIsoDateTime($requestBody['tracked_time']) || IsIsoDate($requestBody['tracked_time'])))
{ {
$trackedTime = $requestBody['tracked_time']; $trackedTime = $requestBody['tracked_time'];
} }
$doneBy = GROCY_USER_ID; $doneBy = GROCY_USER_ID;
if (array_key_exists('done_by', $requestBody) && !empty($requestBody['done_by'])) if (array_key_exists('done_by', $requestBody) && !empty($requestBody['done_by']))
{ {
$doneBy = $requestBody['done_by']; $doneBy = $requestBody['done_by'];
@@ -133,9 +131,4 @@ class ChoresApiController extends BaseApiController
return $this->GenericErrorResponse($response, $ex->getMessage()); return $this->GenericErrorResponse($response, $ex->getMessage());
} }
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -3,11 +3,11 @@
namespace Grocy\Controllers; namespace Grocy\Controllers;
use Grocy\Helpers\Grocycode; use Grocy\Helpers\Grocycode;
use jucksearm\barcode\lib\BarcodeFactory;
use jucksearm\barcode\lib\DatamatrixFactory;
class ChoresController extends BaseController class ChoresController extends BaseController
{ {
use GrocycodeTrait;
public function ChoreEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function ChoreEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
$usersService = $this->getUsersService(); $usersService = $this->getUsersService();
@@ -115,40 +115,7 @@ class ChoresController extends BaseController
public function ChoreGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function ChoreGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
$size = $request->getQueryParam('size', null);
$gc = new Grocycode(Grocycode::CHORE, $args['choreId']); $gc = new Grocycode(Grocycode::CHORE, $args['choreId']);
return $this->ServeGrocycodeImage($request, $response, $gc);
if (GROCY_GROCYCODE_TYPE == '2D')
{
$png = (new DatamatrixFactory())->setCode((string) $gc)->setSize($size)->getDatamatrixPngData();
}
else
{
$png = (new BarcodeFactory())->setType('C128')->setCode((string) $gc)->setHeight($size)->getBarcodePngData();
}
$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 __construct(\DI\Container $container)
{
parent::__construct($container);
} }
} }

View File

@@ -33,9 +33,4 @@ class EquipmentController extends BaseController
'userfieldValues' => $this->getUserfieldsService()->GetAllValues('equipment') 'userfieldValues' => $this->getUserfieldsService()->GetAllValues('equipment')
]); ]);
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -11,17 +11,17 @@ use Throwable;
class ExceptionController extends BaseApiController class ExceptionController extends BaseApiController
{ {
/**
* @var \Slim\App
*/
private $app;
public function __construct(\Slim\App $app, \DI\Container $container) public function __construct(\Slim\App $app, \DI\Container $container)
{ {
parent::__construct($container); parent::__construct($container);
$this->app = $app; $this->app = $app;
} }
/**
* @var \Slim\App
*/
private $app;
public function __invoke(ServerRequestInterface $request, Throwable $exception, bool $displayErrorDetails, bool $logErrors, bool $logErrorDetails, ?LoggerInterface $logger = null) public function __invoke(ServerRequestInterface $request, Throwable $exception, bool $displayErrorDetails, bool $logErrors, bool $logErrorDetails, ?LoggerInterface $logger = null)
{ {
$response = $this->app->getResponseFactory()->createResponse(); $response = $this->app->getResponseFactory()->createResponse();
@@ -59,7 +59,10 @@ class ExceptionController extends BaseApiController
if ($exception instanceof HttpNotFoundException) if ($exception instanceof HttpNotFoundException)
{ {
define('GROCY_AUTHENTICATED', false); if (!defined('GROCY_AUTHENTICATED'))
{
define('GROCY_AUTHENTICATED', false);
}
return $this->renderPage($response->withStatus(404), 'errors/404', [ return $this->renderPage($response->withStatus(404), 'errors/404', [
'exception' => $exception 'exception' => $exception

View File

@@ -118,11 +118,6 @@ class FilesApiController extends BaseApiController
} }
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
/** /**
* @param string $fileName base64-encoded file-name * @param string $fileName base64-encoded file-name
* @return false|string the decoded file-name * @return false|string the decoded file-name
@@ -151,7 +146,6 @@ class FilesApiController extends BaseApiController
protected function getFilePath(string $group, string $fileName, array $queryParams = []) protected function getFilePath(string $group, string $fileName, array $queryParams = [])
{ {
$forceServeAs = null; $forceServeAs = null;
if (isset($queryParams['force_serve_as']) && !empty($queryParams['force_serve_as'])) if (isset($queryParams['force_serve_as']) && !empty($queryParams['force_serve_as']))
{ {
$forceServeAs = $queryParams['force_serve_as']; $forceServeAs = $queryParams['force_serve_as'];
@@ -160,14 +154,12 @@ class FilesApiController extends BaseApiController
if ($forceServeAs == FilesService::FILE_SERVE_TYPE_PICTURE) if ($forceServeAs == FilesService::FILE_SERVE_TYPE_PICTURE)
{ {
$bestFitHeight = null; $bestFitHeight = null;
if (isset($queryParams['best_fit_height']) && !empty($queryParams['best_fit_height']) && is_numeric($queryParams['best_fit_height'])) if (isset($queryParams['best_fit_height']) && !empty($queryParams['best_fit_height']) && is_numeric($queryParams['best_fit_height']))
{ {
$bestFitHeight = $queryParams['best_fit_height']; $bestFitHeight = $queryParams['best_fit_height'];
} }
$bestFitWidth = null; $bestFitWidth = null;
if (isset($queryParams['best_fit_width']) && !empty($queryParams['best_fit_width']) && is_numeric($queryParams['best_fit_width'])) if (isset($queryParams['best_fit_width']) && !empty($queryParams['best_fit_width']) && is_numeric($queryParams['best_fit_width']))
{ {
$bestFitWidth = $queryParams['best_fit_width']; $bestFitWidth = $queryParams['best_fit_width'];

View File

@@ -213,11 +213,6 @@ class GenericEntityApiController extends BaseApiController
} }
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
private function IsEntityWithEditRequiresAdmin($entity) private function IsEntityWithEditRequiresAdmin($entity)
{ {
return in_array($entity, $this->getOpenApiSpec()->components->schemas->ExposedEntityEditRequiresAdmin->enum); return in_array($entity, $this->getOpenApiSpec()->components->schemas->ExposedEntityEditRequiresAdmin->enum);

View File

@@ -91,9 +91,4 @@ class GenericEntityController extends BaseController
'userfieldValues' => $this->getUserfieldsService()->GetAllValues('userentity-' . $args['userentityName']) 'userfieldValues' => $this->getUserfieldsService()->GetAllValues('userentity-' . $args['userentityName'])
]); ]);
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -0,0 +1,45 @@
<?php
namespace Grocy\Controllers;
use Grocy\Helpers\Grocycode;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use jucksearm\barcode\lib\BarcodeFactory;
use jucksearm\barcode\lib\DatamatrixFactory;
trait GrocycodeTrait
{
public function ServeGrocycodeImage(ServerRequestInterface $request, ResponseInterface $response, Grocycode $grocycode)
{
$size = $request->getQueryParam('size', null);
if (GROCY_GROCYCODE_TYPE == '2D')
{
$png = (new DatamatrixFactory())->setCode((string) $grocycode)->setSize($size)->getDatamatrixPngData();
}
else
{
$png = (new BarcodeFactory())->setType('C128')->setCode((string) $grocycode)->setHeight($size)->getBarcodePngData();
}
$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;
}
}

View File

@@ -6,11 +6,6 @@ use Grocy\Services\SessionService;
class LoginController extends BaseController class LoginController extends BaseController
{ {
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
public function LoginPage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function LoginPage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
return $this->renderPage($response, 'login'); return $this->renderPage($response, 'login');

View File

@@ -85,9 +85,4 @@ class OpenApiController extends BaseApiController
{ {
return $this->render($response, 'openapiui'); return $this->render($response, 'openapiui');
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -34,9 +34,4 @@ class PrintApiController extends BaseApiController
return $this->GenericErrorResponse($response, $ex->getMessage()); return $this->GenericErrorResponse($response, $ex->getMessage());
} }
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -76,9 +76,4 @@ class RecipesApiController extends BaseApiController
return $this->GenericErrorResponse($response, $ex->getMessage()); return $this->GenericErrorResponse($response, $ex->getMessage());
} }
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -8,19 +8,21 @@ class RecipesController extends BaseController
{ {
public function MealPlan(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function MealPlan(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
// Given date is always the first day of the week => load the coming week / 7 days $start = date('Y-m-d');
if (isset($request->getQueryParams()['week']) && IsIsoDate($request->getQueryParams()['week'])) if (isset($request->getQueryParams()['start']) && IsIsoDate($request->getQueryParams()['start']))
{ {
$week = $request->getQueryParams()['week']; $start = $request->getQueryParams()['start'];
$mealPlanWhereTimespan = "day BETWEEN DATE('$week') AND DATE('$week', '+7 days')";
} }
else
$days = 6;
if (isset($request->getQueryParams()['days']) && filter_var($request->getQueryParams()['days'], FILTER_VALIDATE_INT) !== false)
{ {
$mealPlanWhereTimespan = "day BETWEEN DATE('now', 'localtime', 'weekday 0', '-7 days') AND DATE(DATE('now', 'localtime', 'weekday 0', '-7 days'), '+7 days')"; $days = $request->getQueryParams()['days'];
} }
$mealPlanWhereTimespan = "day BETWEEN DATE('$start') AND DATE('$start', '+$days days')";
$recipes = $this->getDatabase()->recipes()->where('type', RecipesService::RECIPE_TYPE_NORMAL)->fetchAll(); $recipes = $this->getDatabase()->recipes()->where('type', RecipesService::RECIPE_TYPE_NORMAL)->fetchAll();
$events = []; $events = [];
foreach ($this->getDatabase()->meal_plan()->where($mealPlanWhereTimespan) as $mealPlanEntry) foreach ($this->getDatabase()->meal_plan()->where($mealPlanWhereTimespan) as $mealPlanEntry)
{ {
@@ -214,9 +216,4 @@ class RecipesController extends BaseController
'mealplanSections' => $this->getDatabase()->meal_plan_sections()->where('id > 0')->orderBy('sort_number') 'mealplanSections' => $this->getDatabase()->meal_plan_sections()->where('id > 0')->orderBy('sort_number')
]); ]);
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -862,9 +862,4 @@ class StockApiController extends BaseApiController
return $this->GenericErrorResponse($response, $ex->getMessage()); return $this->GenericErrorResponse($response, $ex->getMessage());
} }
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -4,11 +4,11 @@ namespace Grocy\Controllers;
use Grocy\Helpers\Grocycode; use Grocy\Helpers\Grocycode;
use Grocy\Services\RecipesService; use Grocy\Services\RecipesService;
use jucksearm\barcode\lib\BarcodeFactory;
use jucksearm\barcode\lib\DatamatrixFactory;
class StockController extends BaseController class StockController extends BaseController
{ {
use GrocycodeTrait;
public function Consume(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function Consume(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
return $this->renderPage($response, 'consume', [ return $this->renderPage($response, 'consume', [
@@ -192,36 +192,8 @@ class StockController extends BaseController
public function ProductGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function ProductGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
$size = $request->getQueryParam('size', null);
$gc = new Grocycode(Grocycode::PRODUCT, $args['productId']); $gc = new Grocycode(Grocycode::PRODUCT, $args['productId']);
return $this->ServeGrocycodeImage($request, $response, $gc);
if (GROCY_GROCYCODE_TYPE == '2D')
{
$png = (new DatamatrixFactory())->setCode((string) $gc)->setSize($size)->getDatamatrixPngData();
}
else
{
$png = (new BarcodeFactory())->setType('C128')->setCode((string) $gc)->setHeight($size)->getBarcodePngData();
}
$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)
@@ -488,38 +460,9 @@ class StockController extends BaseController
public function StockEntryGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function StockEntryGrocycodeImage(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
$size = $request->getQueryParam('size', null);
$stockEntry = $this->getDatabase()->stock()->where('id', $args['entryId'])->fetch(); $stockEntry = $this->getDatabase()->stock()->where('id', $args['entryId'])->fetch();
$gc = new Grocycode(Grocycode::PRODUCT, $stockEntry->product_id, [$stockEntry->stock_id]); $gc = new Grocycode(Grocycode::PRODUCT, $stockEntry->product_id, [$stockEntry->stock_id]);
return $this->ServeGrocycodeImage($request, $response, $gc);
if (GROCY_GROCYCODE_TYPE == '2D')
{
$png = (new DatamatrixFactory())->setCode((string) $gc)->setSize($size)->getDatamatrixPngData();
}
else
{
$png = (new BarcodeFactory())->setType('C128')->setCode((string) $gc)->setHeight($size)->getBarcodePngData();
}
$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 StockEntryGrocycodeLabel(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function StockEntryGrocycodeLabel(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
@@ -569,11 +512,6 @@ class StockController extends BaseController
]); ]);
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
public function JournalSummary(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function JournalSummary(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
$entries = $this->getDatabase()->uihelper_stock_journal_summary(); $entries = $this->getDatabase()->uihelper_stock_journal_summary();

View File

@@ -89,9 +89,4 @@ class SystemApiController extends BaseApiController
{ {
return $this->ApiResponse($response, json_decode($this->getLocalizationService()->GetPoAsJsonString()), true); return $this->ApiResponse($response, json_decode($this->getLocalizationService()->GetPoAsJsonString()), true);
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -35,11 +35,6 @@ class SystemController extends BaseController
return $response->withRedirect($this->AppContainer->get('UrlManager')->ConstructUrl($this->GetEntryPageRelative())); return $response->withRedirect($this->AppContainer->get('UrlManager')->ConstructUrl($this->GetEntryPageRelative()));
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
/** /**
* Get the entry page of the application based on the value of the entry page setting. * Get the entry page of the application based on the value of the entry page setting.
* *

View File

@@ -49,9 +49,4 @@ class TasksApiController extends BaseApiController
return $this->GenericErrorResponse($response, $ex->getMessage()); return $this->GenericErrorResponse($response, $ex->getMessage());
} }
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -83,9 +83,4 @@ class TasksController extends BaseController
{ {
return $this->renderPage($response, 'taskssettings'); return $this->renderPage($response, 'taskssettings');
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -67,6 +67,11 @@ class User
const PERMISSION_USERS_READ = 'USERS_READ'; const PERMISSION_USERS_READ = 'USERS_READ';
public function __construct()
{
$this->db = DatabaseService::getInstance()->GetDbConnection();
}
/** /**
* @var \LessQL\Database|null * @var \LessQL\Database|null
*/ */
@@ -78,11 +83,6 @@ class User
return $user->getPermissionList(); return $user->getPermissionList();
} }
public function __construct()
{
$this->db = DatabaseService::getInstance()->GetDbConnection();
}
public static function checkPermission($request, string ...$permissions): void public static function checkPermission($request, string ...$permissions): void
{ {
$user = new self(); $user = new self();

View File

@@ -231,9 +231,4 @@ class UsersApiController extends BaseApiController
return $this->GenericErrorResponse($response, $ex->getMessage()); return $this->GenericErrorResponse($response, $ex->getMessage());
} }
} }
public function __construct(\DI\Container $container)
{
parent::__construct($container);
}
} }

View File

@@ -4,6 +4,12 @@ namespace Grocy\Helpers;
abstract class BaseBarcodeLookupPlugin abstract class BaseBarcodeLookupPlugin
{ {
final public function __construct($locations, $quantityUnits)
{
$this->Locations = $locations;
$this->QuantityUnits = $quantityUnits;
}
protected $Locations; protected $Locations;
protected $QuantityUnits; protected $QuantityUnits;
@@ -50,28 +56,24 @@ abstract class BaseBarcodeLookupPlugin
// Check referenced entity ids are valid // Check referenced entity ids are valid
$locationId = $pluginOutput['location_id']; $locationId = $pluginOutput['location_id'];
if (FindObjectInArrayByPropertyValue($this->Locations, 'id', $locationId) === null) if (FindObjectInArrayByPropertyValue($this->Locations, 'id', $locationId) === null)
{ {
throw new \Exception("Location $locationId is not a valid location id"); throw new \Exception("Location $locationId is not a valid location id");
} }
$quIdPurchase = $pluginOutput['qu_id_purchase']; $quIdPurchase = $pluginOutput['qu_id_purchase'];
if (FindObjectInArrayByPropertyValue($this->QuantityUnits, 'id', $quIdPurchase) === null) if (FindObjectInArrayByPropertyValue($this->QuantityUnits, 'id', $quIdPurchase) === null)
{ {
throw new \Exception("Location $quIdPurchase is not a valid quantity unit id"); throw new \Exception("Location $quIdPurchase is not a valid quantity unit id");
} }
$quIdStock = $pluginOutput['qu_id_stock']; $quIdStock = $pluginOutput['qu_id_stock'];
if (FindObjectInArrayByPropertyValue($this->QuantityUnits, 'id', $quIdStock) === null) if (FindObjectInArrayByPropertyValue($this->QuantityUnits, 'id', $quIdStock) === null)
{ {
throw new \Exception("Location $quIdStock is not a valid quantity unit id"); throw new \Exception("Location $quIdStock is not a valid quantity unit id");
} }
$quFactor = $pluginOutput['qu_factor_purchase_to_stock']; $quFactor = $pluginOutput['qu_factor_purchase_to_stock'];
if (empty($quFactor) || !is_numeric($quFactor)) if (empty($quFactor) || !is_numeric($quFactor))
{ {
throw new \Exception('Quantity unit factor is empty or not a number'); throw new \Exception('Quantity unit factor is empty or not a number');
@@ -80,11 +82,5 @@ abstract class BaseBarcodeLookupPlugin
return $pluginOutput; return $pluginOutput;
} }
final public function __construct($locations, $quantityUnits)
{
$this->Locations = $locations;
$this->QuantityUnits = $quantityUnits;
}
abstract protected function ExecuteLookup($barcode); abstract protected function ExecuteLookup($barcode);
} }

View File

@@ -25,6 +25,33 @@ class Grocycode
public const MAGIC = 'grcy'; public const MAGIC = 'grcy';
/**
* Constructs a new instance of the Grocycode class.
*
* Because php doesn't support overloading, this is a proxy
* to either setFromCode($code) or setFromData($type, $id, $extra_data = []).
*/
public function __construct(...$args)
{
$argc = count($args);
if ($argc == 1)
{
$this->setFromCode($args[0]);
return;
}
elseif ($argc == 2 || $argc == 3)
{
if ($argc == 2)
{
$args[] = [];
}
$this->setFromData($args[0], $args[1], $args[2]);
return;
}
throw new \Exception('No suitable overload found.');
}
/** /**
* An array that registers all valid grocycode types. Register yours here by appending to this array. * An array that registers all valid grocycode types. Register yours here by appending to this array.
*/ */
@@ -56,31 +83,26 @@ class Grocycode
} }
} }
/** public function GetId()
* Constructs a new instance of the Grocycode class.
*
* Because php doesn't support overloading, this is a proxy
* to either setFromCode($code) or setFromData($type, $id, $extra_data = []).
*/
public function __construct(...$args)
{ {
$argc = count($args); return $this->id;
if ($argc == 1) }
{
$this->setFromCode($args[0]);
return;
}
elseif ($argc == 2 || $argc == 3)
{
if ($argc == 2)
{
$args[] = [];
}
$this->setFromData($args[0], $args[1], $args[2]);
return;
}
throw new \Exception('No suitable overload found.'); public function GetExtraData()
{
return $this->extra_data;
}
public function GetType()
{
return $this->type;
}
public function __toString(): string
{
$arr = array_merge([self::MAGIC, $this->type, $this->id], $this->extra_data);
return implode(':', $arr);
} }
/** /**
@@ -121,26 +143,4 @@ class Grocycode
$this->id = $id; $this->id = $id;
$this->extra_data = $extra_data; $this->extra_data = $extra_data;
} }
public function GetId()
{
return $this->id;
}
public function GetExtraData()
{
return $this->extra_data;
}
public function GetType()
{
return $this->type;
}
public function __toString(): string
{
$arr = array_merge([self::MAGIC, $this->type, $this->id], $this->extra_data);
return implode(':', $arr);
}
} }

View File

@@ -4,6 +4,18 @@ namespace Grocy\Helpers;
class UrlManager class UrlManager
{ {
public function __construct(string $basePath)
{
if ($basePath === '/')
{
$this->BasePath = $this->GetBaseUrl();
}
else
{
$this->BasePath = $basePath;
}
}
protected $BasePath; protected $BasePath;
public function ConstructUrl($relativePath, $isResource = false) public function ConstructUrl($relativePath, $isResource = false)
@@ -18,18 +30,6 @@ class UrlManager
} }
} }
public function __construct(string $basePath)
{
if ($basePath === '/')
{
$this->BasePath = $this->GetBaseUrl();
}
else
{
$this->BasePath = $basePath;
}
}
private function GetBaseUrl() private function GetBaseUrl()
{ {
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false)

View File

@@ -8,13 +8,13 @@ use Psr\Http\Message\ResponseInterface;
class WebhookRunner class WebhookRunner
{ {
private $client;
public function __construct() public function __construct()
{ {
$this->client = new Client(['timeout' => 2.0]); $this->client = new Client(['timeout' => 2.0]);
} }
private $client;
public function run($url, $args, $json = false) public function run($url, $args, $json = false)
{ {
$reqArgs = []; $reqArgs = [];

View File

@@ -4,8 +4,7 @@ function FindObjectInArrayByPropertyValue($array, $propertyName, $propertyValue)
{ {
foreach ($array as $object) foreach ($array as $object)
{ {
if ($object->{$propertyName} if ($object->{$propertyName} == $propertyValue)
== $propertyValue)
{ {
return $object; return $object;
} }
@@ -17,37 +16,28 @@ function FindObjectInArrayByPropertyValue($array, $propertyName, $propertyValue)
function FindAllObjectsInArrayByPropertyValue($array, $propertyName, $propertyValue, $operator = '==') function FindAllObjectsInArrayByPropertyValue($array, $propertyName, $propertyValue, $operator = '==')
{ {
$returnArray = []; $returnArray = [];
foreach ($array as $object) foreach ($array as $object)
{ {
switch ($operator) switch ($operator)
{ {
case '==': case '==':
if ($object->{$propertyName} == $propertyValue)
if ($object->{$propertyName}
== $propertyValue)
{ {
$returnArray[] = $object; $returnArray[] = $object;
} }
break; break;
case '>': case '>':
if ($object->{$propertyName} > $propertyValue)
if ($object->{$propertyName}
> $propertyValue)
{ {
$returnArray[] = $object; $returnArray[] = $object;
} }
break; break;
case '<': case '<':
if ($object->{$propertyName} if ($object->{$propertyName} < $propertyValue)
< $propertyValue)
{ {
$returnArray[] = $object; $returnArray[] = $object;
} }
break; break;
} }
} }
@@ -58,7 +48,6 @@ function FindAllObjectsInArrayByPropertyValue($array, $propertyName, $propertyVa
function FindAllItemsInArrayByValue($array, $value, $operator = '==') function FindAllItemsInArrayByValue($array, $value, $operator = '==')
{ {
$returnArray = []; $returnArray = [];
foreach ($array as $item) foreach ($array as $item)
{ {
switch ($operator) switch ($operator)
@@ -69,7 +58,6 @@ function FindAllItemsInArrayByValue($array, $value, $operator = '==')
{ {
$returnArray[] = $item; $returnArray[] = $item;
} }
break; break;
case '>': case '>':
@@ -77,7 +65,6 @@ function FindAllItemsInArrayByValue($array, $value, $operator = '==')
{ {
$returnArray[] = $item; $returnArray[] = $item;
} }
break; break;
case '<': case '<':
@@ -85,7 +72,6 @@ function FindAllItemsInArrayByValue($array, $value, $operator = '==')
{ {
$returnArray[] = $item; $returnArray[] = $item;
} }
break; break;
} }
} }
@@ -96,7 +82,6 @@ function FindAllItemsInArrayByValue($array, $value, $operator = '==')
function SumArrayValue($array, $propertyName) function SumArrayValue($array, $propertyName)
{ {
$sum = 0; $sum = 0;
foreach ($array as $object) foreach ($array as $object)
{ {
$sum += floatval($object->{$propertyName}); $sum += floatval($object->{$propertyName});
@@ -124,7 +109,6 @@ function GetClassConstants($className, $prefix = null)
function RandomString($length, $allowedChars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') function RandomString($length, $allowedChars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{ {
$randomString = ''; $randomString = '';
for ($i = 0; $i < $length; $i++) for ($i = 0; $i < $length; $i++)
{ {
$randomString .= $allowedChars[rand(0, strlen($allowedChars) - 1)]; $randomString .= $allowedChars[rand(0, strlen($allowedChars) - 1)];
@@ -190,7 +174,8 @@ function Setting(string $name, $value)
define('GROCY_' . $name, ExternalSettingValue(file_get_contents($settingOverrideFile))); define('GROCY_' . $name, ExternalSettingValue(file_get_contents($settingOverrideFile)));
} }
elseif (getenv('GROCY_' . $name) !== false) elseif (getenv('GROCY_' . $name) !== false)
{ // An environment variable with the same name and prefix GROCY_ overwrites the given setting {
// An environment variable with the same name and prefix GROCY_ overwrites the given setting
define('GROCY_' . $name, ExternalSettingValue(getenv('GROCY_' . $name))); define('GROCY_' . $name, ExternalSettingValue(getenv('GROCY_' . $name)));
} }
else else

View File

@@ -2,7 +2,7 @@
# Translators: # Translators:
# Adi Zarko <kapkapon@gmail.com>, 2020 # Adi Zarko <kapkapon@gmail.com>, 2020
# Netanel Lazarovich <natylaza89@gmail.com>, 2020 # Netanel Lazarovich <natylaza89@gmail.com>, 2020
# Yaron Shahrabani <sh.yaron@gmail.com>, 2020 # Yaron Shahrabani <sh.yaron@gmail.com>, 2021
# #
msgid "" msgid ""
msgstr "" msgstr ""
@@ -10,7 +10,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-01T17:59:17+00:00\n" "POT-Creation-Date: 2019-05-01T17:59:17+00:00\n"
"PO-Revision-Date: 2019-05-01 17:42+0000\n" "PO-Revision-Date: 2019-05-01 17:42+0000\n"
"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>, 2020\n" "Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>, 2021\n"
"Language-Team: Hebrew (Israel) (https://www.transifex.com/grocy/teams/93189/he_IL/)\n" "Language-Team: Hebrew (Israel) (https://www.transifex.com/grocy/teams/93189/he_IL/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@@ -402,10 +402,10 @@ msgid "Finnish"
msgstr "פינית" msgstr "פינית"
msgid "Breakfast" msgid "Breakfast"
msgstr "" msgstr "ארוחת בוקר"
msgid "Lunch" msgid "Lunch"
msgstr "" msgstr "ארוחת צהריים"
msgid "Dinner" msgid "Dinner"
msgstr "" msgstr "ארוחת ערב"

View File

@@ -2481,22 +2481,22 @@ msgid "Stock entry"
msgstr "רשומה במלאי" msgstr "רשומה במלאי"
msgid "Configure sections" msgid "Configure sections"
msgstr "" msgstr "הגדרת סעיפים"
msgid "Meal plan sections" msgid "Meal plan sections"
msgstr "" msgstr "סעיפי תכנית ארוחות"
msgid "Create meal plan section" msgid "Create meal plan section"
msgstr "" msgstr "יצירת סעיף תכנית ארוחות"
msgid "Sections will be ordered by that number on the meal plan" msgid "Sections will be ordered by that number on the meal plan"
msgstr "" msgstr "הסעיפים יסודרו לפי המספר על תכנית הארוחות"
msgid "Edit meal plan section" msgid "Edit meal plan section"
msgstr "" msgstr "עריכת סעיף תכנית ארוחות"
msgid "Are you sure to delete meal plan section \"%s\"?" msgid "Are you sure to delete meal plan section \"%s\"?"
msgstr "" msgstr "למחוק את הסעיף „%s” מתכנית הארוחות?"
msgid "Section" msgid "Section"
msgstr "" msgstr "סעיף"

View File

@@ -9,14 +9,14 @@ use Slim\Routing\RouteContext;
class ApiKeyAuthMiddleware extends AuthMiddleware class ApiKeyAuthMiddleware extends AuthMiddleware
{ {
protected $ApiKeyHeaderName;
public function __construct(\DI\Container $container, ResponseFactoryInterface $responseFactory) public function __construct(\DI\Container $container, ResponseFactoryInterface $responseFactory)
{ {
parent::__construct($container, $responseFactory); parent::__construct($container, $responseFactory);
$this->ApiKeyHeaderName = $this->AppContainer->get('ApiKeyHeaderName'); $this->ApiKeyHeaderName = $this->AppContainer->get('ApiKeyHeaderName');
} }
protected $ApiKeyHeaderName;
public function authenticate(Request $request) public function authenticate(Request $request)
{ {
$routeContext = RouteContext::fromRequest($request); $routeContext = RouteContext::fromRequest($request);

View File

@@ -11,14 +11,14 @@ use Slim\Routing\RouteContext;
abstract class AuthMiddleware extends BaseMiddleware abstract class AuthMiddleware extends BaseMiddleware
{ {
protected $ResponseFactory;
public function __construct(\DI\Container $container, ResponseFactoryInterface $responseFactory) public function __construct(\DI\Container $container, ResponseFactoryInterface $responseFactory)
{ {
parent::__construct($container); parent::__construct($container);
$this->ResponseFactory = $responseFactory; $this->ResponseFactory = $responseFactory;
} }
protected $ResponseFactory;
public function __invoke(Request $request, RequestHandler $handler): Response public function __invoke(Request $request, RequestHandler $handler): Response
{ {
$routeContext = RouteContext::fromRequest($request); $routeContext = RouteContext::fromRequest($request);

View File

@@ -21,7 +21,6 @@ class LocaleMiddleware extends BaseMiddleware
if (defined('GROCY_AUTHENTICATED') && GROCY_AUTHENTICATED) if (defined('GROCY_AUTHENTICATED') && GROCY_AUTHENTICATED)
{ {
$locale = UsersService::getInstance()->GetUserSetting(GROCY_USER_ID, 'locale'); $locale = UsersService::getInstance()->GetUserSetting(GROCY_USER_ID, 'locale');
if (isset($locale) && !empty($locale)) if (isset($locale) && !empty($locale))
{ {
if (in_array($locale, scandir(__DIR__ . '/../localization'))) if (in_array($locale, scandir(__DIR__ . '/../localization')))
@@ -46,7 +45,6 @@ class LocaleMiddleware extends BaseMiddleware
arsort($prefLocales); arsort($prefLocales);
$availableLocales = scandir(__DIR__ . '/../localization'); $availableLocales = scandir(__DIR__ . '/../localization');
foreach ($prefLocales as $locale => $q) foreach ($prefLocales as $locale => $q)
{ {
if (in_array($locale, $availableLocales)) if (in_array($locale, $availableLocales))
@@ -60,7 +58,7 @@ class LocaleMiddleware extends BaseMiddleware
return substr($locale, 0, 5); return substr($locale, 0, 5);
} }
// e.g: cs // e.g. cs
if (in_array(substr($locale, 0, 2), $availableLocales)) if (in_array(substr($locale, 0, 2), $availableLocales))
{ {
return substr($locale, 0, 2); return substr($locale, 0, 2);

View File

@@ -12,3 +12,13 @@ VALUES
ALTER TABLE meal_plan ALTER TABLE meal_plan
ADD section_id INTEGER NOT NULL DEFAULT -1; ADD section_id INTEGER NOT NULL DEFAULT -1;
CREATE TRIGGER prevent_internal_meal_plan_section_removal BEFORE DELETE ON meal_plan_sections
BEGIN
SELECT CASE WHEN((
SELECT 1
FROM meal_plan_sections
WHERE id = OLD.id
AND id = -1
) NOTNULL) THEN RAISE(ABORT, "This is an internally used/required default section and therefore can't be deleted") END;
END;

View File

@@ -1116,5 +1116,12 @@ $(document).on("click", ".change-table-columns-rowgroup-toggle", function()
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_RECIPES) if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_RECIPES)
{ {
$("#meal-plan-nav-link").attr("href", $("#meal-plan-nav-link").attr("href") + "?week=" + moment().startOf("week").format("YYYY-MM-DD")); if ($(window).width() < 768)
{
$("#meal-plan-nav-link").attr("href", $("#meal-plan-nav-link").attr("href") + "?start=" + moment().format("YYYY-MM-DD") + "&days=0");
}
else
{
$("#meal-plan-nav-link").attr("href", $("#meal-plan-nav-link").attr("href") + "?start=" + moment().startOf("week").format("YYYY-MM-DD"));
}
} }

View File

@@ -48,7 +48,7 @@ $(".calendar").each(function()
"scrollTime": "00:00:00", "scrollTime": "00:00:00",
"firstDay": firstDay, "firstDay": firstDay,
"height": "auto", "height": "auto",
"defaultDate": GetUriParam("week"), "defaultDate": GetUriParam("start"),
"viewRender": function(view) "viewRender": function(view)
{ {
if (!isPrimarySection) if (!isPrimarySection)
@@ -309,7 +309,7 @@ $(".calendar").each(function()
{ {
if (isPrimarySection) if (isPrimarySection)
{ {
UpdateUriParam("week", view.start.format("YYYY-MM-DD")); UpdateUriParam("start", view.start.format("YYYY-MM-DD"));
if (firstRender) if (firstRender)
{ {

View File

@@ -9,7 +9,6 @@ class ApplicationService extends BaseService
public function GetChangelog() public function GetChangelog()
{ {
$changelogItems = []; $changelogItems = [];
foreach (glob(__DIR__ . '/../changelog/*.md') as $file) foreach (glob(__DIR__ . '/../changelog/*.md') as $file)
{ {
$fileName = basename($file); $fileName = basename($file);

View File

@@ -6,14 +6,9 @@ class BaseService
{ {
private static $instances = []; private static $instances = [];
public function __construct()
{
}
public static function getInstance() public static function getInstance()
{ {
$className = get_called_class(); $className = get_called_class();
if (!isset(self::$instances[$className])) if (!isset(self::$instances[$className]))
{ {
self::$instances[$className] = new $className(); self::$instances[$className] = new $className();

View File

@@ -6,6 +6,13 @@ use Grocy\Helpers\UrlManager;
class CalendarService extends BaseService class CalendarService extends BaseService
{ {
public function __construct()
{
$this->UrlManager = new UrlManager(GROCY_BASE_URL);
}
private $UrlManager;
public function GetEvents() public function GetEvents()
{ {
$stockEvents = []; $stockEvents = [];
@@ -148,10 +155,4 @@ class CalendarService extends BaseService
return array_merge($stockEvents, $taskEvents, $choreEvents, $batteryEvents, $mealPlanRecipeEvents, $mealPlanNotesEvents, $mealPlanProductEvents); return array_merge($stockEvents, $taskEvents, $choreEvents, $batteryEvents, $mealPlanRecipeEvents, $mealPlanNotesEvents, $mealPlanProductEvents);
} }
public function __construct()
{
parent::__construct();
$this->UrlManager = new UrlManager(GROCY_BASE_URL);
}
} }

View File

@@ -38,7 +38,6 @@ class ChoresService extends BaseService
$users = $this->getUsersService()->GetUsersAsDto(); $users = $this->getUsersService()->GetUsersAsDto();
$assignedUsers = []; $assignedUsers = [];
foreach ($users as $user) foreach ($users as $user)
{ {
if (in_array($user->id, explode(',', $chore->assignment_config))) if (in_array($user->id, explode(',', $chore->assignment_config)))
@@ -210,11 +209,6 @@ class ChoresService extends BaseService
]); ]);
} }
public function __construct()
{
parent::__construct();
}
private function ChoreExists($choreId) private function ChoreExists($choreId)
{ {
$choreRow = $this->getDatabase()->chores()->where('id = :1', $choreId)->fetch(); $choreRow = $this->getDatabase()->chores()->where('id = :1', $choreId)->fetch();

View File

@@ -4,6 +4,9 @@ namespace Grocy\Services;
class DatabaseMigrationService extends BaseService class DatabaseMigrationService extends BaseService
{ {
// This migration will be always execute, can be used to fix things manually
const EMERGENCY_MIGRATION_ID = 9999;
public function MigrateDatabase() public function MigrateDatabase()
{ {
$this->getDatabaseService()->ExecuteDbStatement("CREATE TABLE IF NOT EXISTS migrations (migration INTEGER NOT NULL PRIMARY KEY UNIQUE, execution_time_timestamp DATETIME DEFAULT (datetime('now', 'localtime')))"); $this->getDatabaseService()->ExecuteDbStatement("CREATE TABLE IF NOT EXISTS migrations (migration INTEGER NOT NULL PRIMARY KEY UNIQUE, execution_time_timestamp DATETIME DEFAULT (datetime('now', 'localtime')))");
@@ -36,10 +39,14 @@ class DatabaseMigrationService extends BaseService
{ {
$rowCount = $this->getDatabaseService()->ExecuteDbQuery('SELECT COUNT(*) FROM migrations WHERE migration = ' . $migrationId)->fetchColumn(); $rowCount = $this->getDatabaseService()->ExecuteDbQuery('SELECT COUNT(*) FROM migrations WHERE migration = ' . $migrationId)->fetchColumn();
if (intval($rowCount) === 0) if (intval($rowCount) === 0 || $migrationId == self::EMERGENCY_MIGRATION_ID)
{ {
include $phpFile; include $phpFile;
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
if ($migrationId != self::EMERGENCY_MIGRATION_ID)
{
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
}
} }
} }
@@ -47,14 +54,18 @@ class DatabaseMigrationService extends BaseService
{ {
$rowCount = $this->getDatabaseService()->ExecuteDbQuery('SELECT COUNT(*) FROM migrations WHERE migration = ' . $migrationId)->fetchColumn(); $rowCount = $this->getDatabaseService()->ExecuteDbQuery('SELECT COUNT(*) FROM migrations WHERE migration = ' . $migrationId)->fetchColumn();
if (intval($rowCount) === 0) if (intval($rowCount) === 0 || $migrationId == self::EMERGENCY_MIGRATION_ID)
{ {
$this->getDatabaseService()->GetDbConnectionRaw()->beginTransaction(); $this->getDatabaseService()->GetDbConnectionRaw()->beginTransaction();
try try
{ {
$this->getDatabaseService()->ExecuteDbStatement($sql); $this->getDatabaseService()->ExecuteDbStatement($sql);
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
if ($migrationId != self::EMERGENCY_MIGRATION_ID)
{
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
}
} }
catch (Exception $ex) catch (Exception $ex)
{ {

View File

@@ -4,6 +4,11 @@ namespace Grocy\Services;
class DemoDataGeneratorService extends BaseService class DemoDataGeneratorService extends BaseService
{ {
public function __construct()
{
$this->LocalizationService = new LocalizationService(GROCY_DEFAULT_LOCALE);
}
protected $LocalizationService; protected $LocalizationService;
private $LastSupermarketId = 1; private $LastSupermarketId = 1;
@@ -343,12 +348,6 @@ class DemoDataGeneratorService extends BaseService
} }
} }
public function __construct()
{
parent::__construct();
$this->LocalizationService = new LocalizationService(GROCY_DEFAULT_LOCALE);
}
private function DownloadFileIfNotAlreadyExists($sourceUrl, $destinationPath) private function DownloadFileIfNotAlreadyExists($sourceUrl, $destinationPath)
{ {
if (!file_exists($destinationPath)) if (!file_exists($destinationPath))

View File

@@ -8,6 +8,30 @@ class FilesService extends BaseService
{ {
const FILE_SERVE_TYPE_PICTURE = 'picture'; const FILE_SERVE_TYPE_PICTURE = 'picture';
public function __construct()
{
$this->StoragePath = GROCY_DATAPATH . '/storage';
if (!file_exists($this->StoragePath))
{
mkdir($this->StoragePath);
}
if (GROCY_MODE === 'demo' || GROCY_MODE === 'prerelease')
{
$dbSuffix = GROCY_DEFAULT_LOCALE;
if (defined('GROCY_DEMO_DB_SUFFIX'))
{
$dbSuffix = GROCY_DEMO_DB_SUFFIX;
}
$this->StoragePath = $this->StoragePath . '/' . $dbSuffix;
if (!file_exists($this->StoragePath))
{
mkdir($this->StoragePath);
}
}
}
private $StoragePath; private $StoragePath;
public function DownscaleImage($group, $fileName, $bestFitHeight = null, $bestFitWidth = null) public function DownscaleImage($group, $fileName, $bestFitHeight = null, $bestFitWidth = null)
@@ -58,8 +82,9 @@ class FilesService extends BaseService
$fileNameWithoutExtension = pathinfo($filePath, PATHINFO_FILENAME); $fileNameWithoutExtension = pathinfo($filePath, PATHINFO_FILENAME);
$fileExtension = pathinfo($filePath, PATHINFO_EXTENSION); $fileExtension = pathinfo($filePath, PATHINFO_EXTENSION);
// Then the file is an image
if (getimagesize($filePath) !== false) if (getimagesize($filePath) !== false)
{ // Then the file is an image {
// Also delete all corresponding "__downscaledto" files when deleting an image // Also delete all corresponding "__downscaledto" files when deleting an image
$groupFolderPath = $this->StoragePath . '/' . $group; $groupFolderPath = $this->StoragePath . '/' . $group;
$files = scandir($groupFolderPath); $files = scandir($groupFolderPath);
@@ -87,32 +112,4 @@ class FilesService extends BaseService
return $groupFolderPath . '/' . $fileName; return $groupFolderPath . '/' . $fileName;
} }
public function __construct()
{
parent::__construct();
$this->StoragePath = GROCY_DATAPATH . '/storage';
if (!file_exists($this->StoragePath))
{
mkdir($this->StoragePath);
}
if (GROCY_MODE === 'demo' || GROCY_MODE === 'prerelease')
{
$dbSuffix = GROCY_DEFAULT_LOCALE;
if (defined('GROCY_DEMO_DB_SUFFIX'))
{
$dbSuffix = GROCY_DEMO_DB_SUFFIX;
}
$this->StoragePath = $this->StoragePath . '/' . $dbSuffix;
if (!file_exists($this->StoragePath))
{
mkdir($this->StoragePath);
}
}
}
} }

View File

@@ -8,6 +8,13 @@ use Gettext\Translator;
class LocalizationService class LocalizationService
{ {
public function __construct(string $culture)
{
$this->Culture = $culture;
$this->LoadLocalizations($culture);
}
protected $Po; protected $Po;
protected $PoUserStrings; protected $PoUserStrings;
@@ -62,13 +69,6 @@ class LocalizationService
return $this->Po->toJsonString(); return $this->Po->toJsonString();
} }
public function __construct(string $culture)
{
$this->Culture = $culture;
$this->LoadLocalizations($culture);
}
public function __n($number, $singularForm, $pluralForm) public function __n($number, $singularForm, $pluralForm)
{ {
$this->CheckAndAddMissingTranslationToPot($singularForm); $this->CheckAndAddMissingTranslationToPot($singularForm);

View File

@@ -10,6 +10,39 @@ use Mike42\Escpos\Printer;
class PrintService extends BaseService class PrintService extends BaseService
{ {
/**
* @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'
];
}
/** /**
* Initialises the printer * Initialises the printer
* @return Printer Printer handle * @return Printer Printer handle
@@ -50,37 +83,4 @@ class PrintService extends BaseService
$printer->selectPrintMode(); $printer->selectPrintMode();
$printer->feed(2); $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'
];
}
} }

View File

@@ -63,8 +63,8 @@ class RecipesService extends BaseService
} }
$transactionId = uniqid(); $transactionId = uniqid();
$recipePositions = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id', $recipeId)->fetchAll();
$recipePositions = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id', $recipeId)->fetchAll();
foreach ($recipePositions as $recipePosition) foreach ($recipePositions as $recipePosition)
{ {
if ($recipePosition->only_check_single_unit_in_stock == 0) if ($recipePosition->only_check_single_unit_in_stock == 0)
@@ -129,11 +129,6 @@ class RecipesService extends BaseService
return $lastInsertId; return $lastInsertId;
} }
public function __construct()
{
parent::__construct();
}
private function RecipeExists($recipeId) private function RecipeExists($recipeId)
{ {
$recipeRow = $this->getDataBase()->recipes()->where('id = :1', $recipeId)->fetch(); $recipeRow = $this->getDataBase()->recipes()->where('id = :1', $recipeId)->fetch();

View File

@@ -12,7 +12,6 @@ class SessionService extends BaseService
public function CreateSession($userId, $stayLoggedInPermanently = false) public function CreateSession($userId, $stayLoggedInPermanently = false)
{ {
$newSessionKey = $this->GenerateSessionKey(); $newSessionKey = $this->GenerateSessionKey();
$expires = date('Y-m-d H:i:s', intval(time() + 2592000)); $expires = date('Y-m-d H:i:s', intval(time() + 2592000));
// Default is that sessions expire in 30 days // Default is that sessions expire in 30 days
@@ -39,7 +38,6 @@ class SessionService extends BaseService
public function GetUserBySessionKey($sessionKey) public function GetUserBySessionKey($sessionKey)
{ {
$sessionRow = $this->getDatabase()->sessions()->where('session_key', $sessionKey)->fetch(); $sessionRow = $this->getDatabase()->sessions()->where('session_key', $sessionKey)->fetch();
if ($sessionRow !== null) if ($sessionRow !== null)
{ {
return $this->getDatabase()->users($sessionRow->user_id); return $this->getDatabase()->users($sessionRow->user_id);
@@ -60,7 +58,6 @@ class SessionService extends BaseService
else else
{ {
$sessionRow = $this->getDatabase()->sessions()->where('session_key = :1 AND expires > :2', $sessionKey, date('Y-m-d H:i:s', time()))->fetch(); $sessionRow = $this->getDatabase()->sessions()->where('session_key = :1 AND expires > :2', $sessionKey, date('Y-m-d H:i:s', time()))->fetch();
if ($sessionRow !== null) if ($sessionRow !== null)
{ {
// This should not change the database file modification time as this is used // This should not change the database file modification time as this is used

View File

@@ -33,7 +33,6 @@ class StockService extends BaseService
} }
$missingProducts = $this->GetMissingProducts(); $missingProducts = $this->GetMissingProducts();
foreach ($missingProducts as $missingProduct) foreach ($missingProducts as $missingProduct)
{ {
$product = $this->getDatabase()->products()->where('id', $missingProduct->id)->fetch(); $product = $this->getDatabase()->products()->where('id', $missingProduct->id)->fetch();
@@ -42,7 +41,8 @@ class StockService extends BaseService
$alreadyExistingEntry = $this->getDatabase()->shopping_list()->where('product_id', $missingProduct->id)->fetch(); $alreadyExistingEntry = $this->getDatabase()->shopping_list()->where('product_id', $missingProduct->id)->fetch();
if ($alreadyExistingEntry) if ($alreadyExistingEntry)
{ // Update {
// Update
if ($alreadyExistingEntry->amount < $amountToAdd) if ($alreadyExistingEntry->amount < $amountToAdd)
{ {
$alreadyExistingEntry->update([ $alreadyExistingEntry->update([
@@ -52,7 +52,8 @@ class StockService extends BaseService
} }
} }
else else
{ // Insert {
// Insert
$shoppinglistRow = $this->getDatabase()->shopping_list()->createRow([ $shoppinglistRow = $this->getDatabase()->shopping_list()->createRow([
'product_id' => $missingProduct->id, 'product_id' => $missingProduct->id,
'amount' => $amountToAdd, 'amount' => $amountToAdd,
@@ -197,7 +198,8 @@ class StockService extends BaseService
{ {
$reps = 1; $reps = 1;
if ($runWebhook == 2) if ($runWebhook == 2)
{ // 2 == run $amount times {
// 2 == run $amount times
$reps = intval(floor($amount)); $reps = intval(floor($amount));
} }
@@ -428,7 +430,6 @@ class StockService extends BaseService
public function EditStockEntry(int $stockRowId, float $amount, $bestBeforeDate, $locationId, $shoppingLocationId, $price, $open, $purchasedDate) public function EditStockEntry(int $stockRowId, float $amount, $bestBeforeDate, $locationId, $shoppingLocationId, $price, $open, $purchasedDate)
{ {
$stockRow = $this->getDatabase()->stock()->where('id = :1', $stockRowId)->fetch(); $stockRow = $this->getDatabase()->stock()->where('id = :1', $stockRowId)->fetch();
if ($stockRow === null) if ($stockRow === null)
{ {
throw new \Exception('Stock does not exist'); throw new \Exception('Stock does not exist');
@@ -455,7 +456,6 @@ class StockService extends BaseService
$logOldRowForStockUpdate->save(); $logOldRowForStockUpdate->save();
$openedDate = $stockRow->opened_date; $openedDate = $stockRow->opened_date;
if (boolval($open) && $openedDate == null) if (boolval($open) && $openedDate == null)
{ {
$openedDate = date('Y-m-d'); $openedDate = date('Y-m-d');
@@ -505,7 +505,8 @@ class StockService extends BaseService
$pluginOutput = $plugin->Lookup($barcode); $pluginOutput = $plugin->Lookup($barcode);
if ($pluginOutput !== null) if ($pluginOutput !== null)
{ // Lookup was successful {
// Lookup was successful
if ($addFoundProduct === true) if ($addFoundProduct === true)
{ {
// Add product to database and include new product id in output // Add product to database and include new product id in output
@@ -522,7 +523,6 @@ class StockService extends BaseService
public function GetCurrentStock($includeNotInStockButMissingProducts = false) public function GetCurrentStock($includeNotInStockButMissingProducts = false)
{ {
$sql = 'SELECT * FROM stock_current'; $sql = 'SELECT * FROM stock_current';
if ($includeNotInStockButMissingProducts) if ($includeNotInStockButMissingProducts)
{ {
$missingProductsView = 'stock_missing_products_including_opened'; $missingProductsView = 'stock_missing_products_including_opened';
@@ -538,7 +538,6 @@ class StockService extends BaseService
$currentStockMapped = $this->getDatabaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_GROUP | \PDO::FETCH_OBJ); $currentStockMapped = $this->getDatabaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_GROUP | \PDO::FETCH_OBJ);
$relevantProducts = $this->getDatabase()->products()->where('id IN (SELECT product_id FROM (' . $sql . ') x)'); $relevantProducts = $this->getDatabase()->products()->where('id IN (SELECT product_id FROM (' . $sql . ') x)');
foreach ($relevantProducts as $product) foreach ($relevantProducts as $product)
{ {
$currentStockMapped[$product->id][0]->product_id = $product->id; $currentStockMapped[$product->id][0]->product_id = $product->id;
@@ -614,7 +613,6 @@ class StockService extends BaseService
} }
$stockCurrentRow = FindObjectinArrayByPropertyValue($this->GetCurrentStock(), 'product_id', $productId); $stockCurrentRow = FindObjectinArrayByPropertyValue($this->GetCurrentStock(), 'product_id', $productId);
if ($stockCurrentRow == null) if ($stockCurrentRow == null)
{ {
$stockCurrentRow = new \stdClass(); $stockCurrentRow = new \stdClass();
@@ -665,7 +663,6 @@ class StockService extends BaseService
{ {
$consumeCount = 1; $consumeCount = 1;
} }
$spoilRate = ($consumeCountSpoiled * 100.0) / $consumeCount; $spoilRate = ($consumeCountSpoiled * 100.0) / $consumeCount;
return [ return [
@@ -704,7 +701,6 @@ class StockService extends BaseService
} }
$potentialProduct = $this->getDatabase()->product_barcodes()->where('barcode = :1', $barcode)->fetch(); $potentialProduct = $this->getDatabase()->product_barcodes()->where('barcode = :1', $barcode)->fetch();
if ($potentialProduct === null) if ($potentialProduct === null)
{ {
throw new \Exception("No product with barcode $barcode found"); throw new \Exception("No product with barcode $barcode found");
@@ -722,8 +718,8 @@ class StockService extends BaseService
$returnData = []; $returnData = [];
$shoppingLocations = $this->getDatabase()->shopping_locations(); $shoppingLocations = $this->getDatabase()->shopping_locations();
$rows = $this->getDatabase()->product_price_history()->where('product_id = :1', $productId)->orderBy('purchased_date', 'DESC');
$rows = $this->getDatabase()->product_price_history()->where('product_id = :1', $productId)->orderBy('purchased_date', 'DESC');
foreach ($rows as $row) foreach ($rows as $row)
{ {
$returnData[] = [ $returnData[] = [
@@ -998,6 +994,7 @@ class StockService extends BaseService
{ {
$decimals = intval($this->getUsersService()->GetUserSetting(GROCY_USER_ID, 'stock_decimal_places_amounts')); $decimals = intval($this->getUsersService()->GetUserSetting(GROCY_USER_ID, 'stock_decimal_places_amounts'));
$newAmount = $productRow->amount - $amount; $newAmount = $productRow->amount - $amount;
if ($newAmount < floatval('0.' . str_repeat('0', $decimals - ($decimals <= 0 ? 0 : 1)) . '1')) if ($newAmount < floatval('0.' . str_repeat('0', $decimals - ($decimals <= 0 ? 0 : 1)) . '1'))
{ {
$productRow->delete(); $productRow->delete();
@@ -1032,13 +1029,16 @@ class StockService extends BaseService
{ {
$product = $this->getDatabase()->products()->where('id = :1', $row->product_id)->fetch(); $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(); $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; $factor = 1.0;
if ($conversion != null) if ($conversion != null)
{ {
$factor = floatval($conversion->factor); $factor = floatval($conversion->factor);
} }
$amount = round($row->amount * $factor); $amount = round($row->amount * $factor);
$note = ''; $note = '';
if (GROCY_TPRINTER_PRINT_NOTES) if (GROCY_TPRINTER_PRINT_NOTES)
{ {
if ($row->note != '') if ($row->note != '')
@@ -1047,6 +1047,7 @@ class StockService extends BaseService
} }
} }
} }
if (GROCY_TPRINTER_PRINT_QUANTITY_NAME && $isValidProduct) if (GROCY_TPRINTER_PRINT_QUANTITY_NAME && $isValidProduct)
{ {
$quantityname = $row->qu_name; $quantityname = $row->qu_name;
@@ -1054,6 +1055,7 @@ class StockService extends BaseService
{ {
$quantityname = $row->qu_name_plural; $quantityname = $row->qu_name_plural;
} }
array_push($result_quantity, $amount . ' ' . $quantityname); array_push($result_quantity, $amount . ' ' . $quantityname);
array_push($result_product, $row->product_name . $note); array_push($result_product, $row->product_name . $note);
} }
@@ -1071,6 +1073,7 @@ class StockService extends BaseService
} }
} }
} }
//Add padding to look nicer //Add padding to look nicer
$maxlength = 1; $maxlength = 1;
foreach ($result_quantity as $quantity) foreach ($result_quantity as $quantity)
@@ -1080,6 +1083,7 @@ class StockService extends BaseService
$maxlength = strlen($quantity); $maxlength = strlen($quantity);
} }
} }
$result = []; $result = [];
$length = count($result_quantity); $length = count($result_quantity);
for ($i = 0; $i < $length; $i++) for ($i = 0; $i < $length; $i++)
@@ -1087,6 +1091,7 @@ class StockService extends BaseService
$quantity = str_pad($result_quantity[$i], $maxlength); $quantity = str_pad($result_quantity[$i], $maxlength);
array_push($result, $quantity . ' ' . $result_product[$i]); array_push($result, $quantity . ' ' . $result_product[$i]);
} }
return $result; return $result;
} }
@@ -1222,7 +1227,8 @@ class StockService extends BaseService
$amount -= $stockEntry->amount; $amount -= $stockEntry->amount;
} }
else else
{ // Stock entry amount is > than needed amount -> split the stock entry resp. update the amount {
// Stock entry amount is > than needed amount -> split the stock entry resp. update the amount
$restStockAmount = $stockEntry->amount - $amount; $restStockAmount = $stockEntry->amount - $amount;
$logRowForLocationFrom = $this->getDatabase()->stock_log()->createRow([ $logRowForLocationFrom = $this->getDatabase()->stock_log()->createRow([

View File

@@ -149,11 +149,6 @@ class UserfieldsService extends BaseService
} }
} }
public function __construct()
{
parent::__construct();
}
protected function getOpenApispec() protected function getOpenApispec()
{ {
if ($this->OpenApiSpec == null) if ($this->OpenApiSpec == null)

View File

@@ -97,7 +97,6 @@ class UsersService extends BaseService
public function SetUserSetting($userId, $settingKey, $settingValue) public function SetUserSetting($userId, $settingKey, $settingValue)
{ {
$settingRow = $this->getDatabase()->user_settings()->where('user_id = :1 AND key = :2', $userId, $settingKey)->fetch(); $settingRow = $this->getDatabase()->user_settings()->where('user_id = :1 AND key = :2', $userId, $settingKey)->fetch();
if ($settingRow !== null) if ($settingRow !== null)
{ {
$settingRow->update([ $settingRow->update([

View File

@@ -1,4 +1,4 @@
{ {
"Version": "3.0.1", "Version": "3.1.0",
"ReleaseDate": "2021-01-05" "ReleaseDate": "2021-07-16"
} }