From f892ebcb7a70b4b3998c415f9ab0c91e147e0e2f Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 10 Feb 2023 18:20:57 +0100 Subject: [PATCH] many small fixes --- Admin/Install/Media.install.json | 2 +- Admin/Install/Navigation.install.json | 96 ++- Admin/Installer.php | 14 +- Admin/Routes/Web/Api.php | 32 + Admin/Routes/Web/Backend.php | 98 ++- Controller/ApiController.php | 139 ++- Controller/BackendController.php | 130 ++- Models/Item.php | 3 +- Theme/Backend/Lang/Navigation.en.lang.php | 2 +- Theme/Backend/item-list.tpl.php | 156 ++++ Theme/Backend/item-profile.tpl.php | 980 ++++++++++++++++++++++ Theme/Backend/stock-item-list.tpl.php | 143 ++++ Theme/Backend/stock-list.tpl.php | 78 -- 13 files changed, 1734 insertions(+), 139 deletions(-) create mode 100644 Admin/Routes/Web/Api.php create mode 100755 Theme/Backend/item-list.tpl.php create mode 100644 Theme/Backend/item-profile.tpl.php create mode 100755 Theme/Backend/stock-item-list.tpl.php delete mode 100755 Theme/Backend/stock-list.tpl.php diff --git a/Admin/Install/Media.install.json b/Admin/Install/Media.install.json index b57a11d..5cd95c7 100755 --- a/Admin/Install/Media.install.json +++ b/Admin/Install/Media.install.json @@ -9,7 +9,7 @@ { "type": "collection", "create_directory": true, - "name": "Articles", + "name": "Items", "virtualPath": "/Modules/ItemManagement", "user": 1 }, diff --git a/Admin/Install/Navigation.install.json b/Admin/Install/Navigation.install.json index 9bc633d..cb23dac 100755 --- a/Admin/Install/Navigation.install.json +++ b/Admin/Install/Navigation.install.json @@ -8,7 +8,7 @@ "uri": null, "target": "self", "icon": "fa fa-usd", - "order": 20, + "order": 35, "from": "ItemManagement", "permission": { "permission": 2, "category": null, "element": null }, "parent": 0, @@ -22,8 +22,8 @@ "uri": "{/lang}/{/app}/item/attribute/type/list?{?}", "target": "self", "icon": null, - "order": 15, - "from": "Sales", + "order": 5, + "from": "ItemManagement", "permission": { "permission": 2, "category": null, "element": null }, "parent": 1004801001, "children": [ @@ -36,8 +36,8 @@ "uri": "{/lang}/{/app}/item/attribute/type/list?{?}", "target": "self", "icon": null, - "order": 15, - "from": "Sales", + "order": 5, + "from": "ItemManagement", "permission": { "permission": 2, "category": null, "element": null }, "parent": 1004802001, "children": [] @@ -51,13 +51,75 @@ "uri": "{/lang}/{/app}/item/attribute/value/list?{?}", "target": "self", "icon": null, - "order": 15, - "from": "Sales", + "order": 10, + "from": "ItemManagement", "permission": { "permission": 2, "category": null, "element": null }, "parent": 1004802001, "children": [] } ] + }, + { + "id": 1004803001, + "pid": "/", + "type": 2, + "subtype": 1, + "name": "Items", + "uri": "{/lang}/{/app}/item/list", + "target": "self", + "icon": null, + "order": 10, + "from": "ItemManagement", + "permission": { "permission": 2, "category": null, "element": null }, + "parent": 1004801001, + "children": [ + { + "id": 1004803002, + "pid": "/item", + "type": 3, + "subtype": 1, + "name": "List", + "uri": "{/lang}/{/app}/item/list", + "target": "self", + "icon": null, + "order": 1, + "from": "ItemManagement", + "permission": { "permission": 2, "category": null, "element": null }, + "parent": 1004803001, + "children": [ + { + "id": 1004803003, + "pid": "/item", + "type": 3, + "subtype": 1, + "name": "Item", + "uri": "{/lang}/{/app}/item/profile?{?}", + "target": "self", + "icon": null, + "order": 1, + "from": "ItemManagement", + "permission": { "permission": 2, "category": null, "element": null }, + "parent": 1004803002, + "children": [] + } + ] + }, + { + "id": 1004803004, + "pid": "/item", + "type": 3, + "subtype": 1, + "name": "Create", + "uri": "{/lang}/{/app}/item/create?{?}", + "target": "self", + "icon": null, + "order": 5, + "from": "ItemManagement", + "permission": { "permission": 4, "category": null, "element": null }, + "parent": 1004803001, + "children": [] + } + ] } ] }, @@ -66,7 +128,7 @@ "pid": "/", "type": 2, "subtype": 1, - "name": "Articles", + "name": "Items", "uri": "{/lang}/{/app}/sales/item/list", "target": "self", "icon": null, @@ -94,7 +156,7 @@ "pid": "/sales/item", "type": 3, "subtype": 1, - "name": "Article", + "name": "Item", "uri": "{/lang}/{/app}/sales/item/single?{?}", "target": "self", "icon": null, @@ -128,7 +190,7 @@ "pid": "/", "type": 2, "subtype": 1, - "name": "Articles", + "name": "Items", "uri": "{/lang}/{/app}/purchase/item/list", "target": "self", "icon": null, @@ -156,7 +218,7 @@ "pid": "/purchase/item", "type": 3, "subtype": 1, - "name": "Article", + "name": "Item", "uri": "{/lang}/{/app}/purchase/item/single?{?}", "target": "self", "icon": null, @@ -190,7 +252,7 @@ "pid": "/", "type": 2, "subtype": 1, - "name": "Articles", + "name": "Items", "uri": "{/lang}/{/app}/warehouse/item/list", "target": "self", "icon": null, @@ -218,7 +280,7 @@ "pid": "/warehouse/item", "type": 3, "subtype": 1, - "name": "Article", + "name": "Item", "uri": "{/lang}/{/app}/warehouse/item/single?{?}", "target": "self", "icon": null, @@ -252,7 +314,7 @@ "pid": "/", "type": 2, "subtype": 1, - "name": "Articles", + "name": "Items", "uri": "{/lang}/{/app}/production/item/list", "target": "self", "icon": null, @@ -280,7 +342,7 @@ "pid": "ac2ed3d0212c51b3ba19a5c862eca728c3cb9d18", "type": 3, "subtype": 1, - "name": "Article", + "name": "Item", "uri": "{/lang}/{/app}/production/item/single?{?}", "target": "self", "icon": null, @@ -314,7 +376,7 @@ "pid": "/sales/analysis", "type": 3, "subtype": 1, - "name": "Article", + "name": "Item", "uri": "{/lang}/{/app}/sales/analysis/item", "target": "self", "icon": null, @@ -329,7 +391,7 @@ "pid": "/purchase/analysis", "type": 3, "subtype": 1, - "name": "Article", + "name": "Item", "uri": "{/lang}/{/app}/purchase/analysis/item", "target": "self", "icon": null, diff --git a/Admin/Installer.php b/Admin/Installer.php index a3e0126..6d97f40 100755 --- a/Admin/Installer.php +++ b/Admin/Installer.php @@ -128,11 +128,17 @@ final class Installer extends InstallerAbstract $request->setData('number', (string) $item['number']); $module->apiItemCreate($request, $response); - $itemId = $response->get('')['response']->getId(); - $itemArray[] = !\is_array($response['response']) - ? $response['response']->toArray() - : $response['response']; + $responseData = $response->get(''); + if (!\is_array($responseData)) { + continue; + } + + $itemId = $responseData['response']->getId(); + + $itemArray[] = !\is_array($responseData['response']) + ? $responseData['response']->toArray() + : $responseData['response']; foreach ($item['l11ns'] as $name => $l11ns) { $l11nType = $l11nTypes[$name]; diff --git a/Admin/Routes/Web/Api.php b/Admin/Routes/Web/Api.php new file mode 100644 index 0000000..2f40a0d --- /dev/null +++ b/Admin/Routes/Web/Api.php @@ -0,0 +1,32 @@ + [ + [ + 'dest' => '\Modules\ItemManagement\Controller\ApiController:apiItemFind', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => ApiController::NAME, + 'type' => PermissionType::READ, + 'state' => PermissionCategory::SALES_ITEM, + ], + ], + ], +]; diff --git a/Admin/Routes/Web/Backend.php b/Admin/Routes/Web/Backend.php index a3b41f1..d374f3e 100755 --- a/Admin/Routes/Web/Backend.php +++ b/Admin/Routes/Web/Backend.php @@ -51,9 +51,9 @@ return [ ], ], ], - '^.*/sales/item/list.*$' => [ + '^/item/list.*$' => [ [ - 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementSalesList', + 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementItemList', 'verb' => RouteVerb::GET, 'permission' => [ 'module' => BackendController::NAME, @@ -62,25 +62,37 @@ return [ ], ], ], - '^.*/purchase/item/list.*$' => [ + '^/item/create.*$' => [ [ - 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementPurchaseList', + 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementItemCreate', 'verb' => RouteVerb::GET, 'permission' => [ 'module' => BackendController::NAME, - 'type' => PermissionType::READ, - 'state' => PermissionCategory::PURCHASE_ITEM, + 'type' => PermissionType::CREATE, + 'state' => PermissionCategory::SALES_ITEM, ], ], ], - '^.*/warehouse/item/list.*$' => [ + '^/item/profile.*$' => [ [ - 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementWarehousingList', + 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementItemItem', 'verb' => RouteVerb::GET, 'permission' => [ 'module' => BackendController::NAME, 'type' => PermissionType::READ, - 'state' => PermissionCategory::STOCK_ITEM, + 'state' => PermissionCategory::SALES_ITEM, + ], + ], + ], + + '^.*/sales/item/list.*$' => [ + [ + 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementSalesList', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::NAME, + 'type' => PermissionType::READ, + 'state' => PermissionCategory::SALES_ITEM, ], ], ], @@ -95,28 +107,6 @@ return [ ], ], ], - '^.*/purchase/item/create.*$' => [ - [ - 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementPurchaseCreate', - 'verb' => RouteVerb::GET, - 'permission' => [ - 'module' => BackendController::NAME, - 'type' => PermissionType::CREATE, - 'state' => PermissionCategory::PURCHASE_ITEM, - ], - ], - ], - '.*/warehouse/item/create.*$' => [ - [ - 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementWarehousingCreate', - 'verb' => RouteVerb::GET, - 'permission' => [ - 'module' => BackendController::NAME, - 'type' => PermissionType::CREATE, - 'state' => PermissionCategory::STOCK_ITEM, - ], - ], - ], '^.*/sales/item/profile.*$' => [ [ 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementSalesItem', @@ -128,6 +118,29 @@ return [ ], ], ], + + '^.*/purchase/item/list.*$' => [ + [ + 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementPurchaseList', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::NAME, + 'type' => PermissionType::READ, + 'state' => PermissionCategory::PURCHASE_ITEM, + ], + ], + ], + '^.*/purchase/item/create.*$' => [ + [ + 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementPurchaseCreate', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::NAME, + 'type' => PermissionType::CREATE, + 'state' => PermissionCategory::PURCHASE_ITEM, + ], + ], + ], '^.*/purchase/item/profile.*$' => [ [ 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementPurchaseItem', @@ -139,6 +152,29 @@ return [ ], ], ], + + '^.*/warehouse/item/list.*$' => [ + [ + 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementWarehousingList', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::NAME, + 'type' => PermissionType::READ, + 'state' => PermissionCategory::STOCK_ITEM, + ], + ], + ], + '.*/warehouse/item/create.*$' => [ + [ + 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementWarehousingCreate', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::NAME, + 'type' => PermissionType::CREATE, + 'state' => PermissionCategory::STOCK_ITEM, + ], + ], + ], '^.*/warehouse/item/profile.*$' => [ [ 'dest' => '\Modules\ItemManagement\Controller\BackendController:viewItemManagementWarehouseItem', diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 6013303..f01897b 100755 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -14,6 +14,8 @@ declare(strict_types=1); namespace Modules\ItemManagement\Controller; +use Modules\Admin\Models\NullAccount; +use Modules\Billing\Models\PricingMapper; use Modules\ItemManagement\Models\Item; use Modules\ItemManagement\Models\ItemAttribute; use Modules\ItemManagement\Models\ItemAttributeMapper; @@ -35,7 +37,10 @@ use Modules\ItemManagement\Models\ItemRelationTypeMapper; use Modules\ItemManagement\Models\NullItemAttributeType; use Modules\ItemManagement\Models\NullItemAttributeValue; use Modules\ItemManagement\Models\NullItemL11nType; +use Modules\Media\Models\Collection; +use Modules\Media\Models\CollectionMapper; use Modules\Media\Models\MediaMapper; +use Modules\Media\Models\MediaTypeMapper; use Modules\Media\Models\PathSettings; use phpOMS\Localization\BaseStringL11n; use phpOMS\Localization\ISO4217CharEnum; @@ -46,6 +51,7 @@ use phpOMS\Message\NotificationLevel; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Model\Message\FormValidation; +use phpOMS\System\MimeType; /** * ItemManagement class. @@ -57,6 +63,64 @@ use phpOMS\Model\Message\FormValidation; */ final class ApiController extends Controller { + /** + * Api method to find items + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiItemFind(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void + { + $l11n = ItemL11nMapper::getAll() + ->with('type') + ->where('type/title', ['name1', 'name2', 'name3'], 'IN') + ->where('language', $request->getLanguage()) + ->where('description', '%' . ($request->getData('search') ?? '') . '%', 'LIKE') + ->execute(); + + $items = []; + foreach ($l11n as $item) { + $items[] = $item->item; + } + + $response->header->set('Content-Type', MimeType::M_JSON, true); + $response->set( + $request->uri->__toString(), + \array_values( + ItemMapper::getAll() + ->with('l11n') + ->with('l11n/type') + ->where('id', $items, 'IN') + ->where('l11n/type/title', ['name1', 'name2', 'name3'], 'IN') + ->where('l11n/language', $request->getLanguage()) + ->execute() + ) + ); + + /* + @todo: BIG TODO. + This is the query I want to be used internally: + + select itemmgmt_item.itemmgmt_item_no, itemmgmt_item_l11n.itemmgmt_item_l11n_description + from itemmgmt_item + left join itemmgmt_item_l11n on itemmgmt_item.itemmgmt_item_id = itemmgmt_item_l11n.itemmgmt_item_l11n_item + left join itemmgmt_item_l11n_type on itemmgmt_item_l11n.itemmgmt_item_l11n_typeref = itemmgmt_item_l11n_type.itemmgmt_item_l11n_type_id + where + itemmgmt_item_l11n_type.itemmgmt_item_l11n_type_title IN ("name1", "name2", "name3") + AND itemmgmt_item_l11n.itemmgmt_item_l11n_lang = "en" + AND itemmgmt_item_l11n.itemmgmt_item_l11n_description LIKE "%Doc%" + + It is not used because they are defined as has many relations and therefore queried as a loop internally. I as the person making the request know its a 1 to 1 result despite the 1 to many db relation. + */ + } + /** * Api method to create item * @@ -85,9 +149,74 @@ final class ApiController extends Controller $this->createModel($request->header->account, $item, ItemMapper::class, 'item', $request->getOrigin()); $this->app->dbPool->get()->con->commit(); + $this->createMediaDirForItem($item->number, $request->header->account); + + $uploadedFiles = $request->getFile('item_profile_image'); + if (!empty($uploadedFiles)) { + // upload image + $uploaded = $this->app->moduleManager->get('Media')->uploadFiles( + names: [], + fileNames: [], + files: $uploadedFiles, + account: $request->header->account, + basePath: __DIR__ . '/../../../Modules/Media/Files/Modules/ItemManagement/Items/' . $item->number, + virtualPath: '/Modules/ItemManagement/Items/' . $item->number, + pathSettings: PathSettings::FILE_PATH + ); + + // create type / media relation + $profileImageType = MediaTypeMapper::get() + ->where('name', 'item_profile_image') + ->execute(); + + $this->createModelRelation( + $request->header->account, + $uploaded[0]->getId(), + $profileImageType->getId(), + MediaMapper::class, + 'types', + '', + $request->getOrigin() + ); + + // create item relation + $this->createModelRelation( + $request->header->account, + $item->getId(), + $uploaded[0]->getId(), + ItemMapper::class, + 'files', + '', + $request->getOrigin() + ); + } + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Item', 'Item successfully created', $item); } + /** + * Create directory for an account + * + * @param string $number Item number + * @param int $createdBy Creator of the directory + * + * @return Collection + * + * @since 1.0.0 + */ + private function createMediaDirForItem(string $number, int $createdBy) : Collection + { + $collection = new Collection(); + $collection->name = $number; + $collection->setVirtualPath('/Modules/ItemManagement/Items'); + $collection->setPath('/Modules/Media/Files/Modules/ItemManagement/Items/' . $number); + $collection->createdBy = new NullAccount($createdBy); + + CollectionMapper::create()->execute($collection); + + return $collection; + } + /** * Method to create item from request. * @@ -104,8 +233,8 @@ final class ApiController extends Controller $item->salesPrice = new Money($request->getData('salesprice', 'int') ?? 0); $item->purchasePrice = new Money($request->getData('purchaseprice', 'int') ?? 0); $item->info = (string) ($request->getData('info') ?? ''); - $item->parent = ($request->getData('parent') !== null) ? (int) $request->getData('parent') : null; - $item->unit = ($request->getData('unit') !== null) ? (int) $request->getData('unit') : null; + $item->parent = $request->getData('parent', 'int'); + $item->unit = $request->getData('unit', 'int'); return $item; } @@ -817,8 +946,8 @@ final class ApiController extends Controller fileNames: $request->getDataList('filenames'), files: $uploadedFiles, account: $request->header->account, - basePath: __DIR__ . '/../../../Modules/Media/Files/Modules/ItemManagement/Articles/' . ($request->getData('item') ?? '0'), - virtualPath: '/Modules/ItemManagement/Articles/' . ($request->getData('item') ?? '0'), + basePath: __DIR__ . '/../../../Modules/Media/Files/Modules/ItemManagement/Items/' . ($request->getData('item') ?? '0'), + virtualPath: '/Modules/ItemManagement/Items/' . ($request->getData('item') ?? '0'), pathSettings: PathSettings::FILE_PATH ); @@ -888,7 +1017,7 @@ final class ApiController extends Controller return; } - $request->setData('virtualpath', '/Modules/ItemManagement/Articles/' . $request->getData('id'), true); + $request->setData('virtualpath', '/Modules/ItemManagement/Items/' . $request->getData('id'), true); $this->app->moduleManager->get('Editor')->apiEditorCreate($request, $response, $data); if ($response->header->status !== RequestStatusCode::R_200) { diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 2b27ab3..46e5eda 100755 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -161,6 +161,41 @@ final class BackendController extends Controller return $view; } + /** + * Routing end-point for application behaviour. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return RenderableInterface + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewItemManagementItemList(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : RenderableInterface + { + $view = new View($this->app->l11nManager, $request, $response); + $view->setTemplate('/Modules/ItemManagement/Theme/Backend/item-list'); + $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1004801001, $request, $response)); + + /** @var \Modules\ItemManagement\Models\Item[] $items */ + $items = ItemMapper::getAll() + ->with('l11n') + ->with('l11n/type') + ->with('files') + ->with('files/types') + ->where('l11n/language', $response->getLanguage()) + ->where('l11n/type/title', ['name1', 'name2', 'name3'], 'IN') + ->where('files/types/name', 'item_profile_image') + ->limit(25) + ->execute(); + + $view->addData('items', $items); + + return $view; + } + /** * Routing end-point for application behaviour. * @@ -239,7 +274,7 @@ final class BackendController extends Controller public function viewItemManagementWarehousingList(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : RenderableInterface { $view = new View($this->app->l11nManager, $request, $response); - $view->setTemplate('/Modules/ItemManagement/Theme/Backend/stock-list'); + $view->setTemplate('/Modules/ItemManagement/Theme/Backend/stock-item-list'); $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1004807001, $request, $response)); $items = ItemMapper::getAll()->execute(); @@ -311,6 +346,99 @@ final class BackendController extends Controller return $view; } + /** + * Routing end-point for application behaviour. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return View + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewItemManagementItemItem(RequestAbstract $request, ResponseAbstract $response, $data = null) : View + { + $head = $response->get('Content')->getData('head'); + $head->addAsset(AssetType::CSS, 'Resources/chartjs/Chartjs/chart.css'); + $head->addAsset(AssetType::JSLATE, 'Resources/chartjs/Chartjs/chart.js'); + $head->addAsset(AssetType::JSLATE, 'Modules/ItemManagement/Controller.js', ['type' => 'module']); + + $view = new View($this->app->l11nManager, $request, $response); + $view->setTemplate('/Modules/ItemManagement/Theme/Backend/item-profile'); + $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1004803001, $request, $response)); + + /** @var \Modules\ItemManagement\Models\Item $item */ + $item = ItemMapper::get() + ->with('l11n') + ->with('l11n/type') + ->with('files') + ->with('files/types') + ->with('notes') + ->where('id', (int) $request->getData('id')) + ->where('l11n/language', $response->getLanguage()) + ->where('l11n/type/title', ['name1', 'name2', 'name3'], 'IN') + ->limit(5, 'files')->sort('files/id', OrderType::DESC) // @todo: limit not working!!! + ->limit(5, 'notes')->sort('notes/id', OrderType::DESC) + ->execute(); + + $view->addData('item', $item); + + /** @var \Model\Setting $settings */ + $settings = $this->app->appSettings->get(null, [ + SettingsEnum::DEFAULT_LOCALIZATION, + ]); + + $view->setData('defaultlocalization', LocalizationMapper::get()->where('id', (int) $settings->getId())->execute()); + + // stats + if ($this->app->moduleManager->isActive('Billing')) { + $ytd = SalesBillMapper::getSalesByItemId($item->getId(), new SmartDateTime('Y-01-01'), new SmartDateTime('now')); + $mtd = SalesBillMapper::getSalesByItemId($item->getId(), new SmartDateTime('Y-m-01'), new SmartDateTime('now')); + $avg = SalesBillMapper::getAvgSalesPriceByItemId($item->getId(), (new SmartDateTime('now'))->smartModify(-1), new SmartDateTime('now')); + + $lastOrder = SalesBillMapper::getLastOrderDateByItemId($item->getId()); + + $newestInvoices = SalesBillMapper::getAll() + ->with('type') + ->where('type/transferType', BillTransferType::SALES) + ->sort('id', OrderType::DESC) + ->limit(5) + ->execute(); + + $topCustomers = []; + $allInvoices = SalesBillMapper::getItemBills($item->getId(), new SmartDateTime('Y-01-01'), new SmartDateTime('now')); + $regionSales = SalesBillMapper::getItemRegionSales($item->getId(), new SmartDateTime('Y-01-01'), new SmartDateTime('now')); + $countrySales = SalesBillMapper::getItemCountrySales($item->getId(), new SmartDateTime('Y-01-01'), new SmartDateTime('now'), 5); + $monthlySalesCosts = SalesBillMapper::getItemMonthlySalesCosts($item->getId(), (new SmartDateTime('now'))->createModify(-1), new SmartDateTime('now')); + } else { + $ytd = new Money(); + $mtd = new Money(); + $avg = new Money(); + $lastOrder = null; + $newestInvoices = []; + $allInvoices = []; + $topCustomers = []; + $regionSales = []; + $countrySales = []; + $monthlySalesCosts = []; + } + + $view->addData('ytd', $ytd); + $view->addData('mtd', $mtd); + $view->addData('avg', $avg); + $view->addData('lastOrder', $lastOrder); + $view->addData('newestInvoices', $newestInvoices); + $view->addData('allInvoices', $allInvoices); + $view->addData('topCustomers', $topCustomers); + $view->addData('regionSales', $regionSales); + $view->addData('countrySales', $countrySales); + $view->addData('monthlySalesCosts', $monthlySalesCosts); + + return $view; + } + /** * Routing end-point for application behaviour. * diff --git a/Models/Item.php b/Models/Item.php index 8018dfa..3095f65 100755 --- a/Models/Item.php +++ b/Models/Item.php @@ -27,7 +27,7 @@ use phpOMS\Localization\Money; * @link https://jingga.app * @since 1.0.0 */ -class Item +class Item implements \JsonSerializable { /** * ID. @@ -374,6 +374,7 @@ class Item 'number' => $this->number, 'status' => $this->status, 'info' => $this->info, + 'l11n' => $this->l11n, ]; } diff --git a/Theme/Backend/Lang/Navigation.en.lang.php b/Theme/Backend/Lang/Navigation.en.lang.php index 1f3c37a..c3a973c 100755 --- a/Theme/Backend/Lang/Navigation.en.lang.php +++ b/Theme/Backend/Lang/Navigation.en.lang.php @@ -14,7 +14,7 @@ declare(strict_types=1); return ['Navigation' => [ 'Analyze' => 'Analyze', - 'Article' => 'Article', + 'Items' => 'Items', 'Attributes' => 'Attributes', 'Create' => 'Create', 'ItemManagement' => 'Item Management', diff --git a/Theme/Backend/item-list.tpl.php b/Theme/Backend/item-list.tpl.php new file mode 100755 index 0000000..cda26db --- /dev/null +++ b/Theme/Backend/item-list.tpl.php @@ -0,0 +1,156 @@ +getData('items') ?? []; + +echo $this->getData('nav')->render(); ?> + +
+
+
+
getHtml('Items'); ?>
+
+ + + + + $value) : ++$count; + $url = UriFactory::build('{/lang}/{/app}/item/profile?{?}&id=' . $value->getId()); + $image = $value->getFileByTypeName('item_profile_image'); + ?> + +
+ getHtml('Number'); ?> + + + + getHtml('Name'); ?> + + + + getHtml('Name'); ?> + + + + getHtml('Name'); ?> + + + + getHtml('Price'); ?> + + + + getHtml('Available'); ?> + + + + getHtml('Reserved'); ?> + + + + getHtml('Ordered'); ?> + + + + +
<?= $this->getHtml('IMG_alt_item'); ?> + printHtml($value->number); ?> + printHtml($value->getL11n('name1')->description); ?> + printHtml($value->getL11n('name2')->description); ?> + printHtml($value->getL11n('name3')->description); ?> + printHtml($value->salesPrice->getCurrency()); ?> + + + + + +
getHtml('Empty', '0', '0'); ?> + +
+
+
+
+
diff --git a/Theme/Backend/item-profile.tpl.php b/Theme/Backend/item-profile.tpl.php new file mode 100644 index 0000000..295637a --- /dev/null +++ b/Theme/Backend/item-profile.tpl.php @@ -0,0 +1,980 @@ +getData('item'); + +$itemL11n = $item->getL11ns(); +$itemAttribute = $item->getAttributes(); + +$notes = $item->getNotes(); +$files = $item->getFiles(); + +$newestInvoices = $this->getData('newestInvoices') ?? []; +$allInvoices = $this->getData('allInvoices') ?? []; +$topCustomers = $this->getData('topCustomers') ?? []; +$regionSales = $this->getData('regionSales') ?? []; +$countrySales = $this->getData('countrySales') ?? []; +$monthlySalesCosts = $this->getData('monthlySalesCosts') ?? []; + +$languages = ISO639Enum::getConstants(); + +/** @var \phpOMS\Localization\Localization $l11n */ +$l11n = $this->getData('defaultlocalization') ?? new NullLocalization(); + +echo $this->getData('nav')->render(); +?> + +
+
+ +
+
+ request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>> +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ + getFileByTypeName('item_profile_image'); ?> +
+
+ <?= $this->printHtml($image->name); ?> +
+
+ +
+
+ +
+
+
+
+
+
+
+
+ +
getHtml('YTDSales'); ?>: + getData('ytd')->getCurrency(); ?> +
getHtml('MTDSales'); ?>: + getData('mtd')->getCurrency(); ?> +
getHtml('ILV'); ?>: + +
getHtml('MRR'); ?>: + +
+
+
+
+ +
+
+
+ +
getHtml('LastOrder'); ?>: + getData('lastOrder') !== null ? $this->getData('lastOrder')->format('Y-m-d H:i') : ''; ?> +
getHtml('PriceChange'); ?>: + +
getHtml('Created'); ?>: + createdAt->format('Y-m-d H:i'); ?> +
getHtml('Modified'); ?>: + +
+
+
+
+ +
+
+
+ +
getHtml('SalesPrice'); ?>: + salesPrice->getCurrency(); ?> +
getHtml('PurchasePrice'); ?>: + purchasePrice->getCurrency(); ?> +
getHtml('Margin'); ?>: + salesPrice->getInt() === 0 + ? 0 + : ($item->salesPrice->getInt() - $item->purchasePrice->getInt()) / $item->salesPrice->getInt() * 100, 2 + ); ?> % +
getHtml('AvgPrice'); ?>: + getData('avg')->getCurrency(); ?> +
+
+
+
+
+ +
+
+
+
getHtml('Notes'); ?>
+
+ + + + + getId()); + ?> + +
getHtml('Title'); ?> + getHtml('CreatedAt'); ?> +
printHtml($note->title); ?> + printHtml($note->createdAt->format('Y-m-d')); ?> + +
+
+
+
+ +
+
+
getHtml('Documents'); ?>
+
+ + + + + getId()); + ?> + +
getHtml('Title'); ?> + + getHtml('CreatedAt'); ?> +
printHtml($file->name); ?> + printHtml($file->extension); ?> + printHtml($file->createdAt->format('Y-m-d')); ?> + +
+
+
+
+
+ +
+
+
+
getHtml('RecentInvoices'); ?>
+
+ + + + + getId()); + ?> + +
getHtml('Number'); ?> + getHtml('Type'); ?> + getHtml('Name'); ?> + getHtml('Net'); ?> + getHtml('Date'); ?> +
printHtml($invoice->getNumber()); ?> + printHtml($invoice->type->getL11n()); ?> + printHtml($invoice->billTo); ?> + printHtml($invoice->netSales->getCurrency()); ?> + printHtml($invoice->createdAt->format('Y-m-d')); ?> + +
+
+
+
+
+ +
+
+
+
Top Customers
+ + + + + getId()); + ?> + +
getHtml('Number'); ?> + getHtml('Name'); ?> + getHtml('Country'); ?> + getHtml('Net'); ?> +
printHtml($client->number); ?> + printHtml($client->profile->account->name1); ?> printHtml($client->profile->account->name2); ?> + printHtml($client->mainAddress->getCountry()); ?> + getCurrency(); ?> + +
+
+
+ +
+
+
getHtml('Sales'); ?>
+
+ + + ], + "datasets": [ + { + "label": "getHtml('Margin'); ?>", + "type": "line", + "data": [ + + + ], + "yAxisID": "axis-2", + "fill": false, + "borderColor": "rgb(255, 99, 132)", + "backgroundColor": "rgb(255, 99, 132)" + }, + { + "label": "getHtml('Sales'); ?>", + "type": "bar", + "data": [ + + + ], + "yAxisID": "axis-1", + "backgroundColor": "rgb(54, 162, 235)" + } + ] + }, + "options": { + "scales": { + "yAxes": [ + { + "id": "axis-1", + "display": true, + "position": "left" + }, + { + "id": "axis-2", + "display": true, + "position": "right", + "scaleLabel": { + "display": true, + "labelString": "getHtml('Margin'); ?> %" + }, + "gridLines": { + "display": false + }, + "beginAtZero": true, + "ticks": { + "min": 0, + "max": 100, + "stepSize": 10 + } + } + ] + } + } + }'> +
+
+
+
+ +
+
+
+
Regions
+
+ , + , + , + , + , + + ], + "backgroundColor": [ + "rgb(255, 99, 132)", + "rgb(255, 159, 64)", + "rgb(255, 205, 86)", + "rgb(75, 192, 192)", + "rgb(54, 162, 235)", + "rgb(153, 102, 255)" + ] + }] + } + }'> +
+
+
+ +
+
+
getHtml('Countries'); ?>
+
+ + ], + "datasets": [{ + "label": "YTD", + "type": "bar", + "data": [ + + + ], + "backgroundColor": "rgb(54, 162, 235)" + }] + } + }'> +
+
+
+
+
+
+
+ +
+
+
+
+
+
getHtml('Description'); ?>
+
+ + + + + + + + +
+
+ +
+
+ +
+
+
+
+
+
+
+
+ +
+
+
getHtml('Localizations'); ?>
+ + + + + $value) : ++$c; + $url = UriFactory::build('{/lang}/{/app}/admin/group/settings?{?}&id=' . $value->getId()); ?> + + +
+ getHtml('ID', '0', '0'); ?> + getHtml('Name'); ?> + getHtml('Localization'); ?> +
+ getId(); ?> + printHtml($value->type->title); ?> + printHtml($value->description); ?> + + +
getHtml('Empty', '0', '0'); ?> + +
+
+
+
+
+ +
+
+
+
+
getHtml('Attribute'); ?>
+
+
+ + + + + + + + + + + +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+ +
+
+
getHtml('Groups'); ?>
+ + + + + $value) : ++$c; + $url = UriFactory::build('{/lang}/{/app}/admin/group/settings?{?}&id=' . $value->getId()); ?> + + +
+ getHtml('ID', '0', '0'); ?> + getHtml('Name'); ?> + getHtml('Name'); ?> +
+ getId(); ?> + printHtml($value->type->getL11n()); ?> + value->getValue() instanceof \DateTime ? $value->value->getValue()->format('Y-m-d') : $this->printHtml((string) $value->value->getValue()); ?> + + +
getHtml('Empty', '0', '0'); ?> + +
+
+
+
+
+ +
+
+
+
+
+

getHtml('Pricing'); ?>

+
+
+
+ + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
getHtml('Prices'); ?>
+ + + + + $value) : ++$c; + $url = UriFactory::build('{/lang}/{/app}/admin/group/settings?{?}&id=' . $value->getId()); ?> + + +
+ getHtml('ID', '0', '0'); ?> + getHtml('Name'); ?> +
+ getId(); ?> + printHtml($value->name); ?> + + +
getHtml('Empty', '0', '0'); ?> + +
+
+
+
+
+ +
+
+
+
+
+

getHtml('Purchase'); ?>

+
+
+
+ + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+

getHtml('Supplier'); ?>

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + + + + $value) : ++$c; + $url = UriFactory::build('{/lang}/{/app}/admin/group/settings?{?}&id=' . $value->getId()); ?> + + +
getHtml('Prices'); ?>
+ getHtml('ID', '0', '0'); ?> + getHtml('Name'); ?> +
+ getId(); ?> + printHtml($value->name); ?> + + +
getHtml('Empty', '0', '0'); ?> + +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+

getHtml('General'); ?>

+
+
+
+ + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+

getHtml('General'); ?>

+
+
+
+ + + + + + + + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
getHtml('RecentInvoices'); ?>
+ + + + + getId()); + ?> + +
getHtml('Number'); ?> + getHtml('Type'); ?> + getHtml('Name'); ?> + getHtml('Net'); ?> + getHtml('Date'); ?> +
getNumber(); ?> + type->getL11n(); ?> + billTo; ?> + netSales->getCurrency(); ?> + createdAt->format('Y-m-d'); ?> + +
+
+
+
+
+ + +
+
+
+ + + + + + + + +
getHtml('Logs'); ?>
IP + getHtml('ID', '0', '0'); ?> + getHtml('Name'); ?> + getHtml('Log'); ?> + getHtml('Date'); ?> +
+
printHtml($this->request->getOrigin()); ?> + printHtml((string) $this->request->header->account); ?> + printHtml((string) $this->request->header->account); ?> + Creating item + printHtml((new \DateTime('now'))->format('Y-m-d H:i:s')); ?> +
+
+
+
+
+
\ No newline at end of file diff --git a/Theme/Backend/stock-item-list.tpl.php b/Theme/Backend/stock-item-list.tpl.php new file mode 100755 index 0000000..c78f812 --- /dev/null +++ b/Theme/Backend/stock-item-list.tpl.php @@ -0,0 +1,143 @@ +getData('items') ?? []; + +echo $this->getData('nav')->render(); ?> + +
+
+
+
getHtml('Items'); ?>
+
+ + + + + $value) : ++$count; + $url = UriFactory::build('{/lang}/{/app}/item/profile?{?}&id=' . $value->getId()); + $image = $value->getFileByTypeName('item_profile_image'); + ?> + +
+ getHtml('Number'); ?> + + + + getHtml('Name'); ?> + + + + getHtml('Name'); ?> + + + + getHtml('Name'); ?> + + + + getHtml('Available'); ?> + + + + getHtml('Reserved'); ?> + + + + getHtml('Ordered'); ?> + + + + +
<?= $this->getHtml('IMG_alt_item'); ?> + printHtml($value->number); ?> + printHtml($value->getL11n('name1')->description); ?> + printHtml($value->getL11n('name2')->description); ?> + printHtml($value->getL11n('name3')->description); ?> + + + + + +
getHtml('Empty', '0', '0'); ?> + +
+
+
+
+
diff --git a/Theme/Backend/stock-list.tpl.php b/Theme/Backend/stock-list.tpl.php deleted file mode 100755 index 9ba9720..0000000 --- a/Theme/Backend/stock-list.tpl.php +++ /dev/null @@ -1,78 +0,0 @@ -getData('items'); - -echo $this->getData('nav')->render(); ?> - -
-
-
-
getHtml('Items'); ?>
-
- - - - - $value) : ++$count; - $url = UriFactory::build('{/lang}/{/app}/warehouse/item/profile?{?}&id=' . $value->getId()); - $image = $value->getFileByType('backend_image'); - ?> - -
- getHtml('ID', '0', '0'); ?> - - - getHtml('Name'); ?> - - - getHtml('Name'); ?> - - - getHtml('Name'); ?> - - - getHtml('Available'); ?> - - - getHtml('Reserved'); ?> - - - getHtml('Ordered'); ?> - - -
<?= $this->printHtml($image->name); ?> - printHtml($value->number); ?> - printHtml($value->getL11n('name1')->description); ?> - printHtml($value->getL11n('name2')->description); ?> - printHtml($value->getL11n('name3')->description); ?> - - - - - -
getHtml('Empty', '0', '0'); ?> - -
-
-
-
-