From d45c1fc432714e05abbc9e79feaf25ac8d16149d Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sun, 4 Apr 2021 17:10:52 +0200 Subject: [PATCH] many fixes and expands and module expansions --- Admin/Install/Navigation.install.json | 45 +++ Admin/Install/db.json | 192 ++++++++++++ Admin/Routes/Web/Backend.php | 33 ++ Controller/ApiController.php | 331 ++++++++++++++++++++- Controller/BackendController.php | 21 +- Models/AttributeValueType.php | 36 +++ Models/Client.php | 9 + Models/ClientAttribute.php | 88 ++++++ Models/ClientAttributeMapper.php | 74 +++++ Models/ClientAttributeType.php | 180 +++++++++++ Models/ClientAttributeTypeL11n.php | 147 +++++++++ Models/ClientAttributeTypeL11nMapper.php | 57 ++++ Models/ClientAttributeTypeMapper.php | 83 ++++++ Models/ClientAttributeValue.php | 214 +++++++++++++ Models/ClientAttributeValueMapper.php | 62 ++++ Models/ClientMapper.php | 7 + Models/NullClientAttribute.php | 38 +++ Models/NullClientAttributeType.php | 38 +++ Models/NullClientAttributeTypeL11n.php | 38 +++ Models/NullClientAttributeValue.php | 38 +++ Theme/Backend/client-profile-bills.tpl.php | 192 ++++++++++++ Theme/Backend/client-profile-items.tpl.php | 161 ++++++++++ Theme/Backend/client-profile.tpl.php | 181 ++++++----- 23 files changed, 2180 insertions(+), 85 deletions(-) create mode 100644 Models/AttributeValueType.php create mode 100755 Models/ClientAttribute.php create mode 100755 Models/ClientAttributeMapper.php create mode 100755 Models/ClientAttributeType.php create mode 100755 Models/ClientAttributeTypeL11n.php create mode 100755 Models/ClientAttributeTypeL11nMapper.php create mode 100755 Models/ClientAttributeTypeMapper.php create mode 100755 Models/ClientAttributeValue.php create mode 100755 Models/ClientAttributeValueMapper.php create mode 100755 Models/NullClientAttribute.php create mode 100755 Models/NullClientAttributeType.php create mode 100755 Models/NullClientAttributeTypeL11n.php create mode 100755 Models/NullClientAttributeValue.php create mode 100644 Theme/Backend/client-profile-bills.tpl.php create mode 100644 Theme/Backend/client-profile-items.tpl.php diff --git a/Admin/Install/Navigation.install.json b/Admin/Install/Navigation.install.json index fb110af..dc20472 100755 --- a/Admin/Install/Navigation.install.json +++ b/Admin/Install/Navigation.install.json @@ -44,5 +44,50 @@ "children": [] } ] + }, + { + "id": 1003103001, + "pid": "/sales/analysis", + "type": 3, + "subtype": 1, + "name": "Client", + "uri": "{/prefix}sales/analysis/client?{?}", + "target": "self", + "icon": null, + "order": 1, + "from": "ClientManagement", + "permission": { "permission": 2, "type": null, "element": null }, + "parent": 1001602001, + "children": [] + }, + { + "id": 1003103002, + "pid": "/sales/analysis", + "type": 3, + "subtype": 1, + "name": "Region", + "uri": "{/prefix}sales/analysis/region?{?}", + "target": "self", + "icon": null, + "order": 2, + "from": "ClientManagement", + "permission": { "permission": 2, "type": null, "element": null }, + "parent": 1001602001, + "children": [] + }, + { + "id": 1003103003, + "pid": "/sales/analysis", + "type": 3, + "subtype": 1, + "name": "SalesRep", + "uri": "{/prefix}sales/analysis/rep?{?}", + "target": "self", + "icon": null, + "order": 3, + "from": "ClientManagement", + "permission": { "permission": 2, "type": null, "element": null }, + "parent": 1001602001, + "children": [] } ] diff --git a/Admin/Install/db.json b/Admin/Install/db.json index b7316ff..6fb569b 100755 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -83,6 +83,198 @@ } } }, + "clientmgmt_attr_type": { + "name": "clientmgmt_attr_type", + "fields": { + "clientmgmt_attr_type_id": { + "name": "clientmgmt_attr_type_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "clientmgmt_attr_type_name": { + "name": "clientmgmt_attr_type_name", + "type": "VARCHAR(255)", + "null": false + }, + "clientmgmt_attr_type_fields": { + "name": "clientmgmt_attr_type_fields", + "type": "INT(11)", + "null": false + }, + "clientmgmt_attr_type_custom": { + "name": "clientmgmt_attr_type_custom", + "type": "TINYINT(1)", + "null": false + }, + "clientmgmt_attr_type_required": { + "description": "Every item must have this attribute type if set to true.", + "name": "clientmgmt_attr_type_required", + "type": "TINYINT(1)", + "null": false + }, + "clientmgmt_attr_type_pattern": { + "description": "This is a regex validation pattern.", + "name": "clientmgmt_attr_type_pattern", + "type": "VARCHAR(255)", + "null": false + } + } + }, + "clientmgmt_attr_type_l11n": { + "name": "clientmgmt_attr_type_l11n", + "fields": { + "clientmgmt_attr_type_l11n_id": { + "name": "clientmgmt_attr_type_l11n_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "clientmgmt_attr_type_l11n_title": { + "name": "clientmgmt_attr_type_l11n_title", + "type": "VARCHAR(255)", + "null": false + }, + "clientmgmt_attr_type_l11n_type": { + "name": "clientmgmt_attr_type_l11n_type", + "type": "INT(11)", + "null": false, + "foreignTable": "clientmgmt_attr_type", + "foreignKey": "clientmgmt_attr_type_id" + }, + "clientmgmt_attr_type_l11n_lang": { + "name": "clientmgmt_attr_type_l11n_lang", + "type": "VARCHAR(2)", + "null": false, + "foreignTable": "language", + "foreignKey": "language_639_1" + } + } + }, + "clientmgmt_attr_value": { + "name": "clientmgmt_attr_value", + "fields": { + "clientmgmt_attr_value_id": { + "name": "clientmgmt_attr_value_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "clientmgmt_attr_value_default": { + "name": "clientmgmt_attr_value_default", + "type": "TINYINT(1)", + "null": false + }, + "clientmgmt_attr_value_type": { + "name": "clientmgmt_attr_value_type", + "type": "INT(11)", + "null": false + }, + "clientmgmt_attr_value_valueStr": { + "name": "clientmgmt_attr_value_valueStr", + "type": "VARCHAR(255)", + "null": true, + "default": null + }, + "clientmgmt_attr_value_valueInt": { + "name": "clientmgmt_attr_value_valueInt", + "type": "INT(11)", + "null": true, + "default": null + }, + "clientmgmt_attr_value_valueDec": { + "name": "clientmgmt_attr_value_valueDec", + "type": "DECIMAL(19,5)", + "null": true, + "default": null + }, + "clientmgmt_attr_value_valueDat": { + "name": "clientmgmt_attr_value_valueDat", + "type": "DATETIME", + "null": true, + "default": null + }, + "clientmgmt_attr_value_lang": { + "name": "clientmgmt_attr_value_lang", + "type": "VARCHAR(2)", + "null": true, + "default": null, + "foreignTable": "language", + "foreignKey": "language_639_1" + }, + "clientmgmt_attr_value_country": { + "name": "clientmgmt_attr_value_country", + "type": "VARCHAR(2)", + "null": true, + "default": null, + "foreignTable": "country", + "foreignKey": "country_code2" + } + } + }, + "clientmgmt_client_attr_default": { + "name": "clientmgmt_client_attr_default", + "fields": { + "clientmgmt_client_attr_default_id": { + "name": "clientmgmt_client_attr_default_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "clientmgmt_client_attr_default_type": { + "name": "clientmgmt_client_attr_default_type", + "type": "INT(11)", + "null": false, + "foreignTable": "clientmgmt_attr_type", + "foreignKey": "clientmgmt_attr_type_id" + }, + "clientmgmt_client_attr_default_value": { + "name": "clientmgmt_client_attr_default_value", + "type": "INT(11)", + "null": false, + "foreignTable": "clientmgmt_attr_value", + "foreignKey": "clientmgmt_attr_value_id" + } + } + }, + "clientmgmt_client_attr": { + "name": "clientmgmt_client_attr", + "fields": { + "clientmgmt_client_attr_id": { + "name": "clientmgmt_client_attr_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "clientmgmt_client_attr_client": { + "name": "clientmgmt_client_attr_client", + "type": "INT(11)", + "null": false, + "foreignTable": "clientmgmt_client", + "foreignKey": "clientmgmt_client_id" + }, + "clientmgmt_client_attr_type": { + "name": "clientmgmt_client_attr_type", + "type": "INT(11)", + "null": false, + "foreignTable": "clientmgmt_attr_type", + "foreignKey": "clientmgmt_attr_type_id" + }, + "clientmgmt_client_attr_value": { + "name": "clientmgmt_client_attr_value", + "type": "INT(11)", + "null": true, + "default": null, + "foreignTable": "clientmgmt_attr_value", + "foreignKey": "clientmgmt_attr_value_id" + } + } + }, "clientmgmt_client_media": { "name": "clientmgmt_client_media", "fields": { diff --git a/Admin/Routes/Web/Backend.php b/Admin/Routes/Web/Backend.php index c0b6f3a..b31f6ad 100755 --- a/Admin/Routes/Web/Backend.php +++ b/Admin/Routes/Web/Backend.php @@ -50,4 +50,37 @@ return [ ], ], ], + '^.*/sales/analysis/client(\?.*|$)$' => [ + [ + 'dest' => '\Modules\ClientManagement\Controller\BackendController:viewClientAnalysis', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::MODULE_NAME, + 'type' => PermissionType::READ, + 'state' => PermissionState::ANALYSIS, + ], + ], + ], + '^.*/sales/analysis/rep(\?.*|$)$' => [ + [ + 'dest' => '\Modules\ClientManagement\Controller\BackendController:viewSalesRepAnalysis', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::MODULE_NAME, + 'type' => PermissionType::READ, + 'state' => PermissionState::ANALYSIS, + ], + ], + ], + '^.*/sales/analysis/region(\?.*|$)$' => [ + [ + 'dest' => '\Modules\ClientManagement\Controller\BackendController:viewRegionAnalysis', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::MODULE_NAME, + 'type' => PermissionType::READ, + 'state' => PermissionState::ANALYSIS, + ], + ], + ], ]; diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 1f0706e..ec61221 100755 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -26,6 +26,18 @@ use phpOMS\Message\NotificationLevel; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Model\Message\FormValidation; +use Modules\ClientManagement\Models\ClientAttribute; +use Modules\ClientManagement\Models\NullClientAttributeType; +use Modules\ClientManagement\Models\NullClientAttributeValue; +use Modules\ClientManagement\Models\ClientAttributeTypeL11n; +use phpOMS\Message\Http\HttpRequest; +use Modules\ClientManagement\Models\ClientAttributeType; +use Modules\ClientManagement\Models\ClientAttributeValue; +use Modules\ClientManagement\Models\AttributeValueType; +use Modules\ClientManagement\Models\ClientAttributeTypeMapper; +use Modules\ClientManagement\Models\ClientAttributeTypeL11nMapper; +use Modules\ClientManagement\Models\ClientAttributeValueMapper; +use Modules\ClientManagement\Models\ClientAttributeMapper; /** * ClientManagement class. @@ -154,7 +166,318 @@ final class ApiController extends Controller } /** - * Api method to create item files + * Api method to create client attribute + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiClientAttributeCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void + { + if (!empty($val = $this->validateClientAttributeCreate($request))) { + $response->set('attribute_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $attribute = $this->createClientAttributeFromRequest($request); + $this->createModel($request->header->account, $attribute, ClientAttributeMapper::class, 'attribute', $request->getOrigin()); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Attribute', 'Attribute successfully created', $attribute); + } + + /** + * Method to create client attribute from request. + * + * @param RequestAbstract $request Request + * + * @return ClientAttribute + * + * @since 1.0.0 + */ + private function createClientAttributeFromRequest(RequestAbstract $request) : ClientAttribute + { + $attribute = new ClientAttribute(); + $attribute->client = (int) $request->getData('client'); + $attribute->type = new NullClientAttributeType((int) $request->getData('type')); + $attribute->value = new NullClientAttributeValue((int) $request->getData('value')); + + return $attribute; + } + + /** + * Validate client attribute create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateClientAttributeCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['type'] = empty($request->getData('type'))) + || ($val['value'] = empty($request->getData('value'))) + || ($val['client'] = empty($request->getData('client'))) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create client attribute l11n + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiClientAttributeTypeL11nCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void + { + if (!empty($val = $this->validateClientAttributeTypeL11nCreate($request))) { + $response->set('attr_type_l11n_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $attrL11n = $this->createClientAttributeTypeL11nFromRequest($request); + $this->createModel($request->header->account, $attrL11n, ClientAttributeTypeL11nMapper::class, 'attr_type_l11n', $request->getOrigin()); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Attribute type localization', 'Attribute type localization successfully created', $attrL11n); + } + + /** + * Method to create client attribute l11n from request. + * + * @param RequestAbstract $request Request + * + * @return ClientAttributeTypeL11n + * + * @since 1.0.0 + */ + private function createClientAttributeTypeL11nFromRequest(RequestAbstract $request) : ClientAttributeTypeL11n + { + $attrL11n = new ClientAttributeTypeL11n(); + $attrL11n->setType((int) ($request->getData('type') ?? 0)); + $attrL11n->setLanguage((string) ( + $request->getData('language') ?? $request->getLanguage() + )); + $attrL11n->title = (string) ($request->getData('title') ?? ''); + + return $attrL11n; + } + + /** + * Validate client attribute l11n create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateClientAttributeTypeL11nCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['title'] = empty($request->getData('title'))) + || ($val['type'] = empty($request->getData('type'))) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create client attribute type + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiClientAttributeTypeCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void + { + if (!empty($val = $this->validateClientAttributeTypeCreate($request))) { + $response->set('attr_type_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $attrType = $this->createClientAttributeTypeFromRequest($request); + $this->createModel($request->header->account, $attrType, ClientAttributeTypeMapper::class, 'attr_type', $request->getOrigin()); + + $l11nRequest = new HttpRequest($request->uri); + $l11nRequest->setData('type', $attrType->getId()); + $l11nRequest->setData('title', $request->getData('title')); + $l11nRequest->setData('language', $request->getData('language')); + + $l11nAttributeType = $this->createClientAttributeTypeL11nFromRequest($l11nRequest); + $this->createModel($request->header->account, $l11nAttributeType, ClientAttributeTypeL11nMapper::class, 'attr_type_l11n_create', $request->getOrigin()); + + $attrType->setL11n($l11nAttributeType); + + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Attribute type', 'Attribute type successfully created', $attrType); + } + + /** + * Method to create client attribute from request. + * + * @param RequestAbstract $request Request + * + * @return ClientAttributeType + * + * @since 1.0.0 + */ + private function createClientAttributeTypeFromRequest(RequestAbstract $request) : ClientAttributeType + { + $attrType = new ClientAttributeType(); + $attrType->setL11n((string) ($request->getData('name') ?? '')); + $attrType->setFields((int) ($request->getData('fields') ?? 0)); + $attrType->setCustom((bool) ($request->getData('custom') ?? false)); + + return $attrType; + } + + /** + * Validate client attribute create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateClientAttributeTypeCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['name'] = empty($request->getData('name'))) + || ($val['title'] = empty($request->getData('title'))) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create client attribute value + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiClientAttributeValueCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void + { + if (!empty($val = $this->validateClientAttributeValueCreate($request))) { + $response->set('attr_value_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $attrValue = $this->createClientAttributeValueFromRequest($request); + $this->createModel($request->header->account, $attrValue, ClientAttributeValueMapper::class, 'attr_value', $request->getOrigin()); + + if ($attrValue->isDefault) { + $this->createModelRelation( + $request->header->account, + (int) $request->getData('attributetype'), + $attrValue->getId(), + ClientAttributeTypeMapper::class, 'defaults', '', $request->getOrigin() + ); + } + + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Attribute value', 'Attribute value successfully created', $attrValue); + } + + /** + * Method to create client attribute value from request. + * + * @param RequestAbstract $request Request + * + * @return ClientAttributeValue + * + * @since 1.0.0 + */ + private function createClientAttributeValueFromRequest(RequestAbstract $request) : ClientAttributeValue + { + $attrValue = new ClientAttributeValue(); + + $type = $request->getData('type') ?? 0; + if ($type === AttributeValueType::_INT) { + $attrValue->valueInt = (int) $request->getData('value'); + } elseif ($type === AttributeValueType::_STRING) { + $attrValue->valueStr = (string) $request->getData('value'); + } elseif ($type === AttributeValueType::_FLOAT) { + $attrValue->valueDec = (float) $request->getData('value'); + } elseif ($type === AttributeValueType::_DATETIME) { + $attrValue->valueDat = new \DateTime($request->getData('value') ?? ''); + } + + $attrValue->type = $type; + $attrValue->isDefault = (bool) ($request->getData('default') ?? false); + + if ($request->hasData('language')) { + $attrValue->setLanguage((string) ($request->getData('language') ?? $request->getLanguage())); + } + + if ($request->hasData('country')) { + $attrValue->setCountry((string) ($request->getData('country') ?? $request->header->l11n->getCountry())); + } + + return $attrValue; + } + + /** + * Validate client attribute value create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateClientAttributeValueCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['type'] = empty($request->getData('type'))) + || ($val['value'] = empty($request->getData('value'))) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create client files * * @param RequestAbstract $request Request * @param ResponseAbstract $response Response @@ -171,7 +494,7 @@ final class ApiController extends Controller $uploadedFiles = $request->getFiles() ?? []; if (empty($uploadedFiles)) { - $this->fillJsonResponse($request, $response, NotificationLevel::ERROR, 'Item', 'Invalid client image', $uploadedFiles); + $this->fillJsonResponse($request, $response, NotificationLevel::ERROR, 'Client', 'Invalid file', $uploadedFiles); $response->header->status = RequestStatusCode::R_400; return; @@ -196,11 +519,11 @@ final class ApiController extends Controller ClientMapper::class, 'files', '', $request->getOrigin() ); - $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Image', 'Image successfully updated', $uploaded); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'File', 'File successfully updated', $uploaded); } /** - * Api method to create item files + * Api method to create client files * * @param RequestAbstract $request Request * @param ResponseAbstract $response Response diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 94b748e..c46ece2 100755 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -24,6 +24,7 @@ use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Stdlib\Base\SmartDateTime; use phpOMS\Views\View; +use Modules\Media\Models\Media; /** * ClientManagement class. @@ -53,7 +54,12 @@ final class BackendController extends Controller $view->setTemplate('/Modules/ClientManagement/Theme/Backend/client-list'); $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1003102001, $request, $response)); - $client = ClientMapper::getAfterPivot(0, null, 25); + $client = ClientMapper + ::with('notes', models: null) + ::with('contactElements', models: null) + ::with('type', 'backend_image', models: [Media::class]) // @todo: it would be nicer if I coult say files:type or files/type and remove the models parameter? + ::getAfterPivot(0, null, 25); + $view->addData('client', $client); return $view; @@ -103,7 +109,11 @@ final class BackendController extends Controller $view->setTemplate('/Modules/ClientManagement/Theme/Backend/client-profile'); $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1003102001, $request, $response)); - $client = ClientMapper::get((int) $request->getData('id')); + $client = ClientMapper + ::with('files', limit: 5, orderBy: 'createdAt', sortOrder: 'ASC') + ::with('notes', limit: 5, orderBy: 'id', sortOrder: 'ASC') + ::get((int) $request->getData('id')); + $view->setData('client', $client); // stats @@ -111,14 +121,18 @@ final class BackendController extends Controller $ytd = SalesBillMapper::getSalesByClientId($client->getId(), new SmartDateTime('Y-01-01'), new SmartDateTime('now')); $mtd = SalesBillMapper::getSalesByClientId($client->getId(), new SmartDateTime('Y-m-01'), new SmartDateTime('now')); $lastOrder = SalesBillMapper::getLastOrderDateByClientId($client->getId()); - $newestInvoices = SalesBillMapper::with('language', $response->getLanguage(), [BillTypeL11n::class])::getNewestClientInvoices($client->getId(), 5); + $newestInvoices = SalesBillMapper + ::with('language', $response->getLanguage(), [BillTypeL11n::class]) + ::getNewestClientInvoices($client->getId(), 5); $monthlySalesCosts = SalesBillMapper::getClientMonthlySalesCosts($client->getId(), (new SmartDateTime('now'))->createModify(-1), new SmartDateTime('now')); + $items = SalesBillMapper::getClientItem($client->getId(), (new SmartDateTime('now'))->createModify(-1), new SmartDateTime('now')); } else { $ytd = new Money(); $mtd = new Money(); $lastOrder = null; $newestInvoices = []; $monthlySalesCosts = []; + $items = []; } $view->addData('ytd', $ytd); @@ -126,6 +140,7 @@ final class BackendController extends Controller $view->addData('lastOrder', $lastOrder); $view->addData('newestInvoices', $newestInvoices); $view->addData('monthlySalesCosts', $monthlySalesCosts); + $view->addData('items', $items); return $view; } diff --git a/Models/AttributeValueType.php b/Models/AttributeValueType.php new file mode 100644 index 0000000..7bd1a09 --- /dev/null +++ b/Models/AttributeValueType.php @@ -0,0 +1,36 @@ +id; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return []; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/Models/ClientAttributeMapper.php b/Models/ClientAttributeMapper.php new file mode 100755 index 0000000..1eceb4e --- /dev/null +++ b/Models/ClientAttributeMapper.php @@ -0,0 +1,74 @@ + + * @since 1.0.0 + */ + protected static array $columns = [ + 'clientmgmt_client_attr_id' => ['name' => 'clientmgmt_client_attr_id', 'type' => 'int', 'internal' => 'id'], + 'clientmgmt_client_attr_client' => ['name' => 'clientmgmt_client_attr_client', 'type' => 'int', 'internal' => 'client'], + 'clientmgmt_client_attr_type' => ['name' => 'clientmgmt_client_attr_type', 'type' => 'int', 'internal' => 'type'], + 'clientmgmt_client_attr_value' => ['name' => 'clientmgmt_client_attr_value', 'type' => 'int', 'internal' => 'value'], + ]; + + /** + * Has one relation. + * + * @var array + * @since 1.0.0 + */ + protected static array $ownsOne = [ + 'type' => [ + 'mapper' => ClientAttributeTypeMapper::class, + 'external' => 'clientmgmt_client_attr_type', + ], + 'value' => [ + 'mapper' => ClientAttributeValueMapper::class, + 'external' => 'clientmgmt_client_attr_value', + ], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + protected static string $table = 'clientmgmt_client_attr'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + protected static string $primaryField = 'clientmgmt_client_attr_id'; +} diff --git a/Models/ClientAttributeType.php b/Models/ClientAttributeType.php new file mode 100755 index 0000000..474f430 --- /dev/null +++ b/Models/ClientAttributeType.php @@ -0,0 +1,180 @@ +setL11n($name); + } + + /** + * Get id + * + * @return int + * + * @since 1.0.0 + */ + public function getId() : int + { + return $this->id; + } + + /** + * Set l11n + * + * @param string|ClientAttributeTypeL11n $l11n Tag article l11n + * @param string $lang Language + * + * @return void + * + * @since 1.0.0 + */ + public function setL11n($l11n, string $lang = ISO639x1Enum::_EN) : void + { + if ($l11n instanceof ClientAttributeTypeL11n) { + $this->l11n = $l11n; + } elseif ($this->l11n instanceof ClientAttributeTypeL11n && \is_string($l11n)) { + $this->l11n->title = $l11n; + } elseif (\is_string($l11n)) { + $this->l11n = new ClientAttributeTypeL11n(); + $this->l11n->title = $l11n; + $this->l11n->setLanguage($lang); + } + } + + /** + * @return string + * + * @since 1.0.0 + */ + public function getL11n() : string + { + return $this->l11n instanceof ClientAttributeTypeL11n ? $this->l11n->title : $this->l11n; + } + + /** + * Set fields + * + * @param int $fields Fields + * + * @return void + * + * @since 1.0.0 + */ + public function setFields(int $fields) : void + { + $this->fields = $fields; + } + + /** + * Set custom + * + * @param bool $custom FieldsCustom + * + * @return void + * + * @since 1.0.0 + */ + public function setCustom(bool $custom) : void + { + $this->custom = $custom; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return []; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/Models/ClientAttributeTypeL11n.php b/Models/ClientAttributeTypeL11n.php new file mode 100755 index 0000000..acdef52 --- /dev/null +++ b/Models/ClientAttributeTypeL11n.php @@ -0,0 +1,147 @@ +type = $type; + $this->title = $title; + $this->language = $language; + } + + /** + * Get id + * + * @return int + * + * @since 1.0.0 + */ + public function getId() : int + { + return $this->id; + } + + /** + * Get attribute type + * + * @return int|ClientAttributeType + * + * @since 1.0.0 + */ + public function getType() + { + return $this->type; + } + + /** + * Set type. + * + * @param int $type Type id + * + * @return void + * + * @since 1.0.0 + */ + public function setType(int $type) : void + { + $this->type = $type; + } + + /** + * Set language + * + * @param string $language Language + * + * @return void + * + * @since 1.0.0 + */ + public function setLanguage(string $language) : void + { + $this->language = $language; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return []; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/Models/ClientAttributeTypeL11nMapper.php b/Models/ClientAttributeTypeL11nMapper.php new file mode 100755 index 0000000..6015713 --- /dev/null +++ b/Models/ClientAttributeTypeL11nMapper.php @@ -0,0 +1,57 @@ + + * @since 1.0.0 + */ + protected static array $columns = [ + 'clientmgmt_attr_type_l11n_id' => ['name' => 'clientmgmt_attr_type_l11n_id', 'type' => 'int', 'internal' => 'id'], + 'clientmgmt_attr_type_l11n_title' => ['name' => 'clientmgmt_attr_type_l11n_title', 'type' => 'string', 'internal' => 'title', 'autocomplete' => true], + 'clientmgmt_attr_type_l11n_type' => ['name' => 'clientmgmt_attr_type_l11n_type', 'type' => 'int', 'internal' => 'type'], + 'clientmgmt_attr_type_l11n_lang' => ['name' => 'clientmgmt_attr_type_l11n_lang', 'type' => 'string', 'internal' => 'language'], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + protected static string $table = 'clientmgmt_attr_type_l11n'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + protected static string $primaryField = 'clientmgmt_attr_type_l11n_id'; +} diff --git a/Models/ClientAttributeTypeMapper.php b/Models/ClientAttributeTypeMapper.php new file mode 100755 index 0000000..56ffb06 --- /dev/null +++ b/Models/ClientAttributeTypeMapper.php @@ -0,0 +1,83 @@ + + * @since 1.0.0 + */ + protected static array $columns = [ + 'clientmgmt_attr_type_id' => ['name' => 'clientmgmt_attr_type_id', 'type' => 'int', 'internal' => 'id'], + 'clientmgmt_attr_type_name' => ['name' => 'clientmgmt_attr_type_name', 'type' => 'string', 'internal' => 'name', 'autocomplete' => true], + 'clientmgmt_attr_type_fields' => ['name' => 'clientmgmt_attr_type_fields', 'type' => 'int', 'internal' => 'fields'], + 'clientmgmt_attr_type_custom' => ['name' => 'clientmgmt_attr_type_custom', 'type' => 'bool', 'internal' => 'custom'], + 'clientmgmt_attr_type_pattern' => ['name' => 'clientmgmt_attr_type_pattern', 'type' => 'bool', 'internal' => 'validationPattern'], + 'clientmgmt_attr_type_required' => ['name' => 'clientmgmt_attr_type_required', 'type' => 'bool', 'internal' => 'isRequired'], + ]; + + /** + * Has many relation. + * + * @var array + * @since 1.0.0 + */ + protected static array $hasMany = [ + 'l11n' => [ + 'mapper' => ClientAttributeTypeL11nMapper::class, + 'table' => 'clientmgmt_attr_type_l11n', + 'self' => 'clientmgmt_attr_type_l11n_type', + 'column' => 'title', + 'conditional' => true, + 'external' => null, + ], + 'defaults' => [ + 'mapper' => ClientAttributeValueMapper::class, + 'table' => 'clientmgmt_client_attr_default', + 'self' => 'clientmgmt_client_attr_default_type', + 'external' => 'clientmgmt_client_attr_default_value', + 'conditional' => false, + ], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + protected static string $table = 'clientmgmt_attr_type'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + protected static string $primaryField = 'clientmgmt_attr_type_id'; +} diff --git a/Models/ClientAttributeValue.php b/Models/ClientAttributeValue.php new file mode 100755 index 0000000..1712154 --- /dev/null +++ b/Models/ClientAttributeValue.php @@ -0,0 +1,214 @@ +type = $type; + $this->language = $language; + + $this->setValue($value); + } + + /** + * Get id + * + * @return int + * + * @since 1.0.0 + */ + public function getId() : int + { + return $this->id; + } + + /** + * Set value + * + * @param int|string|float|\DateTimeInterface $value Value + * + * @return void + * + * @since 1.0.0 + */ + public function setValue($value) : void + { + if (\is_string($value)) { + $this->valueStr = $value; + } elseif (\is_int($value)) { + $this->valueInt = $value; + } elseif (\is_float($value)) { + $this->valueDec = $value; + } elseif ($value instanceof \DateTimeInterface) { + $this->valueDat = $value; + } + } + + public function getValue() : mixed + { + if (!empty($this->valueStr)) { + return $this->valueStr; + } elseif (!empty($this->valueInt)) { + return $this->valueInt; + } elseif (!empty($this->valueDec)) { + return $this->valueDec; + } elseif ($this->valueDat instanceof \DateTimeInterface) { + return $this->valueDat; + } + + return null; + } + + /** + * Set language + * + * @param string $language Language + * + * @return void + * + * @since 1.0.0 + */ + public function setLanguage(string $language) : void + { + $this->language = $language; + } + + /** + * Set country + * + * @param string $country Country + * + * @return void + * + * @since 1.0.0 + */ + public function setCountry(string $country) : void + { + $this->country = $country; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return []; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/Models/ClientAttributeValueMapper.php b/Models/ClientAttributeValueMapper.php new file mode 100755 index 0000000..a319522 --- /dev/null +++ b/Models/ClientAttributeValueMapper.php @@ -0,0 +1,62 @@ + + * @since 1.0.0 + */ + protected static array $columns = [ + 'clientmgmt_attr_value_id' => ['name' => 'clientmgmt_attr_value_id', 'type' => 'int', 'internal' => 'id'], + 'clientmgmt_attr_value_default' => ['name' => 'clientmgmt_attr_value_default', 'type' => 'bool', 'internal' => 'isDefault'], + 'clientmgmt_attr_value_type' => ['name' => 'clientmgmt_attr_value_type', 'type' => 'int', 'internal' => 'type'], + 'clientmgmt_attr_value_valueStr' => ['name' => 'clientmgmt_attr_value_valueStr', 'type' => 'string', 'internal' => 'valueStr'], + 'clientmgmt_attr_value_valueInt' => ['name' => 'clientmgmt_attr_value_valueInt', 'type' => 'int', 'internal' => 'valueInt'], + 'clientmgmt_attr_value_valueDec' => ['name' => 'clientmgmt_attr_value_valueDec', 'type' => 'float', 'internal' => 'valueDec'], + 'clientmgmt_attr_value_valueDat' => ['name' => 'clientmgmt_attr_value_valueDat', 'type' => 'DateTime', 'internal' => 'valueDat'], + 'clientmgmt_attr_value_lang' => ['name' => 'clientmgmt_attr_value_lang', 'type' => 'string', 'internal' => 'language'], + 'clientmgmt_attr_value_country' => ['name' => 'clientmgmt_attr_value_country', 'type' => 'string', 'internal' => 'country'], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + protected static string $table = 'clientmgmt_attr_value'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + protected static string $primaryField = 'clientmgmt_attr_value_id'; +} diff --git a/Models/ClientMapper.php b/Models/ClientMapper.php index c5ff98c..2b048b7 100755 --- a/Models/ClientMapper.php +++ b/Models/ClientMapper.php @@ -115,5 +115,12 @@ final class ClientMapper extends DataMapperAbstract 'external' => 'clientmgmt_client_contactelement_dst', 'self' => 'clientmgmt_client_contactelement_src', ], + 'attributes' => [ + 'mapper' => ClientAttributeMapper::class, + 'table' => 'clientmgmt_client_attr', + 'self' => 'clientmgmt_client_attr_client', + 'conditional' => true, + 'external' => null, + ], ]; } diff --git a/Models/NullClientAttribute.php b/Models/NullClientAttribute.php new file mode 100755 index 0000000..d450f36 --- /dev/null +++ b/Models/NullClientAttribute.php @@ -0,0 +1,38 @@ +id = $id; + } +} diff --git a/Models/NullClientAttributeType.php b/Models/NullClientAttributeType.php new file mode 100755 index 0000000..89ece90 --- /dev/null +++ b/Models/NullClientAttributeType.php @@ -0,0 +1,38 @@ +id = $id; + } +} diff --git a/Models/NullClientAttributeTypeL11n.php b/Models/NullClientAttributeTypeL11n.php new file mode 100755 index 0000000..634e604 --- /dev/null +++ b/Models/NullClientAttributeTypeL11n.php @@ -0,0 +1,38 @@ +id = $id; + } +} diff --git a/Models/NullClientAttributeValue.php b/Models/NullClientAttributeValue.php new file mode 100755 index 0000000..d70cc18 --- /dev/null +++ b/Models/NullClientAttributeValue.php @@ -0,0 +1,38 @@ +id = $id; + } +} diff --git a/Theme/Backend/client-profile-bills.tpl.php b/Theme/Backend/client-profile-bills.tpl.php new file mode 100644 index 0000000..6510a36 --- /dev/null +++ b/Theme/Backend/client-profile-bills.tpl.php @@ -0,0 +1,192 @@ +getData('newestInvoices') ?? []; + +?> + +
+
+
+
getHtml('Bills'); ?>
+ + + + + $value) : + ++$count; + $url = UriFactory::build('{/prefix}sales/bill?{?}&id=' . $value->getId()); + ?> + +
+ getHtml('ID', '0', '0'); ?> + + + + getHtml('Type'); ?> + + + + getHtml('ClientID'); ?> + + + + getHtml('Client'); ?> + + + + getHtml('Address'); ?> + + + + getHtml('Postal'); ?> + + + + getHtml('City'); ?> + + + + getHtml('Country'); ?> + + + + getHtml('Net'); ?> + + + + getHtml('Profit'); ?> + + + + getHtml('Created'); ?> + + + +
+ getNumber(); ?> + type->getL11n(); ?> + client->number; ?> + printHtml($value->billTo); ?> + billAddress; ?> + billZip; ?> + billCity; ?> + billCountry; ?> + net->getCurrency(); ?> + profit->getCurrency(); ?> + createdAt->format('Y-m-d'); ?> + + +
getHtml('Empty', '0', '0'); ?> + +
+
+
+
diff --git a/Theme/Backend/client-profile-items.tpl.php b/Theme/Backend/client-profile-items.tpl.php new file mode 100644 index 0000000..531a6a3 --- /dev/null +++ b/Theme/Backend/client-profile-items.tpl.php @@ -0,0 +1,161 @@ +getData('items') ?? []; + +?> + +
+
+
+
getHtml('Items'); ?>
+ + + + + $value) : + if ($value->itemNumber === '') { + continue; + } + + ++$count; + $url = UriFactory::build('{/prefix}sales/item/profile?{?}&id=' . $value->getId()); + ?> + +
+ getHtml('ID', '0', '0'); ?> + + + + getHtml('Name'); ?> + + + + getHtml('Quantity'); ?> + + + + getHtml('UnitPrice'); ?> + + + + getHtml('Discount'); ?> + + + + getHtml('Discount%'); ?> + + + + getHtml('DiscountBonus'); ?> + + + + getHtml('TotalPrice'); ?> + + + +
+ printHtml($value->itemNumber); ?> + printHtml($value->itemName); ?> + printHtml((string) $value->quantity); ?> + printHtml($value->singleSalesPriceNet->getCurrency()); ?> + + + + printHtml($value->totalSalesPriceNet->getCurrency()); ?> + + +
getHtml('Empty', '0', '0'); ?> + +
+
+
+
diff --git a/Theme/Backend/client-profile.tpl.php b/Theme/Backend/client-profile.tpl.php index 9fac0f0..8ae5a6d 100755 --- a/Theme/Backend/client-profile.tpl.php +++ b/Theme/Backend/client-profile.tpl.php @@ -37,24 +37,20 @@ echo $this->getData('nav')->render();
@@ -83,7 +79,10 @@ echo $this->getData('nav')->render();
-
getHtml('Contact'); ?>
+
+ getHtml('Contact'); ?> + +
@@ -97,28 +96,50 @@ echo $this->getData('nav')->render();
-
getHtml('Address'); ?>
-
- - mainAddress->addition)) : ?> -
-
- -
-
-
-
-
-
-
-
-
-
-
+
+ getHtml('Address'); ?> + + + + + +
+
+ + mainAddress->addition)) : ?> +
+
+ +
+
+
+
+
+
+
+
+
+
+
@@ -349,8 +370,38 @@ echo $this->getData('nav')->render(); - request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>> + request->uri->fragment === 'c-tab-3' ? ' checked' : ''; ?>>
+
+
+
+

getHtml('Address'); ?>

+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -384,30 +435,20 @@ echo $this->getData('nav')->render();
- request->uri->fragment === 'c-tab-3' ? ' checked' : ''; ?>> + request->uri->fragment === 'c-tab-5' ? ' checked' : ''; ?>>
-

getHtml('Address'); ?>

+

getHtml('Payment'); ?>

-
-
+
-
-
-
-
-
-
-
-
-
@@ -415,9 +456,7 @@ echo $this->getData('nav')->render();
-
- request->uri->fragment === 'c-tab-4' ? ' checked' : ''; ?>> -
+
@@ -448,28 +487,6 @@ echo $this->getData('nav')->render();
- request->uri->fragment === 'c-tab-5' ? ' checked' : ''; ?>> -
-
-
-
-

getHtml('Payment'); ?>

-
-
- -
-
-
-
-
-
-
-
-
-
request->uri->fragment === 'c-tab-6' ? ' checked' : ''; ?>>
@@ -546,6 +563,14 @@ echo $this->getData('nav')->render();
request->uri->fragment === 'c-tab-9' ? ' checked' : ''; ?>> +
+ +
+ request->uri->fragment === 'c-tab-9' ? ' checked' : ''; ?>> +
+ +
+ request->uri->fragment === 'c-tab-10' ? ' checked' : ''; ?>>