mirror of
https://github.com/grocy/grocy.git
synced 2025-08-14 17:54:40 +00:00
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:
@@ -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>
|
||||||
|
|
||||||
-----
|
-----
|
||||||
|
@@ -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
|
@@ -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);
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
|
@@ -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();
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
||||||
@@ -58,8 +58,11 @@ class ExceptionController extends BaseApiController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($exception instanceof HttpNotFoundException)
|
if ($exception instanceof HttpNotFoundException)
|
||||||
|
{
|
||||||
|
if (!defined('GROCY_AUTHENTICATED'))
|
||||||
{
|
{
|
||||||
define('GROCY_AUTHENTICATED', false);
|
define('GROCY_AUTHENTICATED', false);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->renderPage($response->withStatus(404), 'errors/404', [
|
return $this->renderPage($response->withStatus(404), 'errors/404', [
|
||||||
'exception' => $exception
|
'exception' => $exception
|
||||||
|
@@ -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'];
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
45
controllers/GrocycodeTrait.php
Normal file
45
controllers/GrocycodeTrait.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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');
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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.
|
||||||
*
|
*
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
|
@@ -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 = [];
|
||||||
|
@@ -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
|
||||||
|
@@ -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 "ארוחת ערב"
|
||||||
|
@@ -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 "סעיף"
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
@@ -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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
|
@@ -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);
|
||||||
|
@@ -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();
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
||||||
|
@@ -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,26 +39,34 @@ 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;
|
||||||
|
|
||||||
|
if ($migrationId != self::EMERGENCY_MIGRATION_ID)
|
||||||
|
{
|
||||||
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
|
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function ExecuteSqlMigrationWhenNeeded(int $migrationId, string $sql)
|
private function ExecuteSqlMigrationWhenNeeded(int $migrationId, string $sql)
|
||||||
{
|
{
|
||||||
$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);
|
||||||
|
|
||||||
|
if ($migrationId != self::EMERGENCY_MIGRATION_ID)
|
||||||
|
{
|
||||||
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
|
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (Exception $ex)
|
catch (Exception $ex)
|
||||||
{
|
{
|
||||||
$this->getDatabaseService()->GetDbConnectionRaw()->rollback();
|
$this->getDatabaseService()->GetDbConnectionRaw()->rollback();
|
||||||
|
@@ -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))
|
||||||
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
@@ -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'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
||||||
|
@@ -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
|
||||||
|
@@ -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([
|
||||||
|
@@ -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)
|
||||||
|
@@ -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([
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"Version": "3.0.1",
|
"Version": "3.1.0",
|
||||||
"ReleaseDate": "2021-01-05"
|
"ReleaseDate": "2021-07-16"
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user