From b8cdda7246367c8418a9474660861208232fc6a5 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 19 May 2023 02:26:10 +0000 Subject: [PATCH] test fixes fleet+expense tests --- Admin/Install/Media.install.json | 16 + Admin/Install/Media.php | 43 ++ Admin/Install/Navigation.install.json | 4 +- Admin/Install/Navigation.php | 10 +- Admin/Install/db.json | 333 +++++++++++ Admin/Install/elementtypes.json | 65 ++ Admin/Install/expensetypes.json | 44 ++ Admin/Installer.php | 163 +++++ Admin/Status.php | 7 + Admin/Uninstaller.php | 7 + Admin/Updater.php | 7 + Controller/ApiController.php | 688 ++++++++++++++++++++++ Controller/BackendController.php | 76 +++ Controller/Controller.php | 76 +++ Models/Expense.php | 78 +++ Models/ExpenseElement.php | 77 +++ Models/ExpenseElementMapper.php | 128 ++++ Models/ExpenseElementType.php | 121 ++++ Models/ExpenseElementTypeL11nMapper.php | 69 +++ Models/ExpenseElementTypeMapper.php | 82 +++ Models/ExpenseMapper.php | 127 ++++ Models/ExpenseStatus.php | 34 ++ Models/ExpenseType.php | 121 ++++ Models/ExpenseTypeL11nMapper.php | 69 +++ Models/ExpenseTypeMapper.php | 82 +++ Models/NullExpense.php | 46 ++ Models/NullExpenseElement.php | 46 ++ Models/NullExpenseElementType.php | 46 ++ Models/NullExpenseType.php | 46 ++ Theme/Backend/Lang/Navigation.en.lang.php | 1 + Theme/Backend/expense-single.tpl.php | 0 Theme/Backend/expenses-list.tpl.php | 0 info.json | 7 +- 33 files changed, 2709 insertions(+), 10 deletions(-) create mode 100644 Admin/Install/Media.install.json create mode 100644 Admin/Install/Media.php create mode 100644 Admin/Install/db.json create mode 100644 Admin/Install/elementtypes.json create mode 100644 Admin/Install/expensetypes.json create mode 100755 Controller/ApiController.php create mode 100755 Controller/BackendController.php create mode 100755 Controller/Controller.php create mode 100644 Models/Expense.php create mode 100644 Models/ExpenseElement.php create mode 100644 Models/ExpenseElementMapper.php create mode 100644 Models/ExpenseElementType.php create mode 100644 Models/ExpenseElementTypeL11nMapper.php create mode 100644 Models/ExpenseElementTypeMapper.php create mode 100644 Models/ExpenseMapper.php create mode 100644 Models/ExpenseStatus.php create mode 100644 Models/ExpenseType.php create mode 100644 Models/ExpenseTypeL11nMapper.php create mode 100644 Models/ExpenseTypeMapper.php create mode 100644 Models/NullExpense.php create mode 100644 Models/NullExpenseElement.php create mode 100644 Models/NullExpenseElementType.php create mode 100644 Models/NullExpenseType.php create mode 100644 Theme/Backend/expense-single.tpl.php create mode 100644 Theme/Backend/expenses-list.tpl.php diff --git a/Admin/Install/Media.install.json b/Admin/Install/Media.install.json new file mode 100644 index 0000000..eb1e051 --- /dev/null +++ b/Admin/Install/Media.install.json @@ -0,0 +1,16 @@ +[ + { + "type": "collection", + "create_directory": true, + "name": "BusinessExpenses", + "virtualPath": "/Modules", + "user": 1 + }, + { + "type": "collection", + "create_directory": true, + "name": "Expense", + "virtualPath": "/Modules/BusinessExpenses", + "user": 1 + } +] \ No newline at end of file diff --git a/Admin/Install/Media.php b/Admin/Install/Media.php new file mode 100644 index 0000000..811cb11 --- /dev/null +++ b/Admin/Install/Media.php @@ -0,0 +1,43 @@ + __DIR__ . '/Media.install.json']); + } +} diff --git a/Admin/Install/Navigation.install.json b/Admin/Install/Navigation.install.json index 6d78487..39399e8 100644 --- a/Admin/Install/Navigation.install.json +++ b/Admin/Install/Navigation.install.json @@ -5,7 +5,7 @@ "type": 2, "subtype": 1, "name": "BusinessExpenses", - "uri": "{/prefix}?{?}", + "uri": "{/base}/businessexpenses/list", "target": "self", "icon": null, "order": 40, @@ -19,7 +19,7 @@ "type": 2, "subtype": 1, "name": "List", - "uri": "{/prefix}?{?}", + "uri": "{/base}/businessexpenses/list", "target": "self", "icon": null, "order": 40, diff --git a/Admin/Install/Navigation.php b/Admin/Install/Navigation.php index bdef136..bcafe45 100644 --- a/Admin/Install/Navigation.php +++ b/Admin/Install/Navigation.php @@ -14,7 +14,7 @@ declare(strict_types=1); namespace Modules\BusinessExpenses\Admin\Install; -use phpOMS\DataStorage\Database\DatabasePool; +use phpOMS\Application\ApplicationAbstract; /** * Navigation class. @@ -29,15 +29,15 @@ class Navigation /** * Install navigation providing * - * @param string $path Module path - * @param DatabasePool $dbPool Database pool for database interaction + * @param ApplicationAbstract $app Application + * @param string $path Module path * * @return void * * @since 1.0.0 */ - public static function install(string $path, DatabasePool $dbPool) : void + public static function install(ApplicationAbstract $app, string $path) : void { - \Modules\Navigation\Admin\Installer::installExternal($dbPool, ['path' => __DIR__ . '/Navigation.install.json']); + \Modules\Navigation\Admin\Installer::installExternal($app, ['path' => __DIR__ . '/Navigation.install.json']); } } diff --git a/Admin/Install/db.json b/Admin/Install/db.json new file mode 100644 index 0000000..4a948d3 --- /dev/null +++ b/Admin/Install/db.json @@ -0,0 +1,333 @@ +{ + "bizexpenses_expense_type": { + "name": "bizexpenses_expense_type", + "fields": { + "bizexpenses_expense_type_id": { + "name": "bizexpenses_expense_type_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "bizexpenses_expense_type_name": { + "name": "bizexpenses_expense_type_name", + "type": "VARCHAR(255)", + "null": false + } + } + }, + "bizexpenses_expense_type_l11n": { + "name": "bizexpenses_expense_type_l11n", + "fields": { + "bizexpenses_expense_type_l11n_id": { + "name": "bizexpenses_expense_type_l11n_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "bizexpenses_expense_type_l11n_title": { + "name": "bizexpenses_expense_type_l11n_title", + "type": "VARCHAR(255)", + "null": false + }, + "bizexpenses_expense_type_l11n_type": { + "name": "bizexpenses_expense_type_l11n_type", + "type": "INT(11)", + "null": false, + "foreignTable": "bizexpenses_expense_type", + "foreignKey": "bizexpenses_expense_type_id" + }, + "bizexpenses_expense_type_l11n_lang": { + "name": "bizexpenses_expense_type_l11n_lang", + "type": "VARCHAR(2)", + "null": false, + "foreignTable": "language", + "foreignKey": "language_639_1" + } + } + }, + "bizexpenses_expense_element_type": { + "name": "bizexpenses_expense_element_type", + "fields": { + "bizexpenses_expense_element_type_id": { + "name": "bizexpenses_expense_element_type_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "bizexpenses_expense_element_type_name": { + "name": "bizexpenses_expense_element_type_name", + "type": "VARCHAR(255)", + "null": false + } + } + }, + "bizexpenses_expense_element_type_l11n": { + "name": "bizexpenses_expense_element_type_l11n", + "fields": { + "bizexpenses_expense_element_type_l11n_id": { + "name": "bizexpenses_expense_element_type_l11n_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "bizexpenses_expense_element_type_l11n_title": { + "name": "bizexpenses_expense_element_type_l11n_title", + "type": "VARCHAR(255)", + "null": false + }, + "bizexpenses_expense_element_type_l11n_type": { + "name": "bizexpenses_expense_element_type_l11n_type", + "type": "INT(11)", + "null": false, + "foreignTable": "bizexpenses_expense_element_type", + "foreignKey": "bizexpenses_expense_element_type_id" + }, + "bizexpenses_expense_element_type_l11n_lang": { + "name": "bizexpenses_expense_element_type_l11n_lang", + "type": "VARCHAR(2)", + "null": false, + "foreignTable": "language", + "foreignKey": "language_639_1" + } + } + }, + "bizexpenses_expense": { + "name": "bizexpenses_expense", + "fields": { + "bizexpenses_expense_id": { + "name": "bizexpenses_expense_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "bizexpenses_expense_status": { + "name": "bizexpenses_expense_status", + "type": "TINYINT(1)", + "null": false + }, + "bizexpenses_expense_description": { + "name": "bizexpenses_expense_description", + "type": "TEXT", + "null": false + }, + "bizexpenses_expense_approved": { + "name": "bizexpenses_expense_approved", + "type": "TINYINT(1)", + "null": false + }, + "bizexpenses_expense_paid": { + "name": "bizexpenses_expense_paid", + "type": "TINYINT(1)", + "null": false + }, + "bizexpenses_expense_net": { + "name": "bizexpenses_expense_net", + "type": "BIGINT", + "null": false + }, + "bizexpenses_expense_gross": { + "name": "bizexpenses_expense_gross", + "type": "BIGINT", + "null": false + }, + "bizexpenses_expense_taxp": { + "name": "bizexpenses_expense_taxP", + "type": "BIGINT", + "null": false + }, + "bizexpenses_expense_created": { + "name": "bizexpenses_expense_created", + "type": "DATETIME", + "null": false + }, + "bizexpenses_expense_start": { + "name": "bizexpenses_expense_start", + "type": "DATETIME", + "null": false + }, + "bizexpenses_expense_end": { + "name": "bizexpenses_expense_end", + "type": "DATETIME", + "null": false + }, + "bizexpenses_expense_type": { + "name": "bizexpenses_expense_type", + "type": "INT", + "null": false, + "foreignTable": "bizexpenses_expense_type", + "foreignKey": "bizexpenses_expense_type_id" + }, + "bizexpenses_expense_from": { + "name": "bizexpenses_expense_from", + "type": "INT", + "null": false, + "foreignTable": "account", + "foreignKey": "account_id" + }, + "bizexpenses_expense_country": { + "name": "bizexpenses_expense_country", + "type": "VARCHAR(2)", + "null": true, + "default": null, + "foreignTable": "country", + "foreignKey": "country_code2" + } + } + }, + "bizexpenses_expense_element": { + "name": "bizexpenses_expense_element", + "fields": { + "bizexpenses_expense_element_id": { + "name": "bizexpenses_expense_element_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "bizexpenses_expense_element_description": { + "name": "bizexpenses_expense_element_description", + "type": "TEXT", + "null": false + }, + "bizexpenses_expense_element_approved": { + "name": "bizexpenses_expense_element_approved", + "type": "TINYINT(1)", + "null": false + }, + "bizexpenses_expense_element_net": { + "name": "bizexpenses_expense_element_net", + "type": "BIGINT", + "null": false + }, + "bizexpenses_expense_element_gross": { + "name": "bizexpenses_expense_element_gross", + "type": "BIGINT", + "null": false + }, + "bizexpenses_expense_element_taxp": { + "name": "bizexpenses_expense_element_taxp", + "type": "BIGINT", + "null": false + }, + "bizexpenses_expense_element_taxr": { + "name": "bizexpenses_expense_element_taxr", + "type": "BIGINT", + "null": false + }, + "bizexpenses_expense_element_quantity": { + "name": "bizexpenses_expense_element_quantity", + "type": "BIGINT", + "null": false + }, + "bizexpenses_expense_element_taxid": { + "name": "bizexpenses_expense_element_taxid", + "type": "TEXT", + "null": false + }, + "bizexpenses_expense_element_start": { + "name": "bizexpenses_expense_element_start", + "type": "DATETIME", + "null": false + }, + "bizexpenses_expense_element_end": { + "name": "bizexpenses_expense_element_end", + "type": "DATETIME", + "null": false + }, + "bizexpenses_expense_element_supplier": { + "name": "bizexpenses_expense_element_supplier", + "type": "INT", + "null": true, + "default": null, + "foreignTable": "suppliermgmt_supplier", + "foreignKey": "suppliermgmt_supplier_id" + }, + "bizexpenses_expense_element_ref": { + "name": "bizexpenses_expense_element_ref", + "type": "INT", + "null": false, + "foreignTable": "account", + "foreignKey": "account_id" + }, + "bizexpenses_expense_element_type": { + "name": "bizexpenses_expense_element_type", + "type": "INT", + "null": false, + "foreignTable": "bizexpenses_expense_element_type", + "foreignKey": "bizexpenses_expense_element_type_id" + }, + "bizexpenses_expense_element_country": { + "name": "bizexpenses_expense_element_country", + "type": "VARCHAR(2)", + "null": true, + "default": null, + "foreignTable": "country", + "foreignKey": "country_code2" + }, + "bizexpenses_expense_element_expense": { + "name": "bizexpenses_expense_element_expense", + "type": "INT", + "null": false, + "foreignTable": "bizexpenses_expense", + "foreignKey": "bizexpenses_expense_id" + } + } + }, + "bizexpenses_expense_media": { + "name": "bizexpenses_expense_media", + "fields": { + "bizexpenses_expense_media_id": { + "name": "bizexpenses_expense_media_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "bizexpenses_expense_media_src": { + "name": "bizexpenses_expense_media_src", + "type": "INT", + "null": false, + "foreignTable": "bizexpenses_expense", + "foreignKey": "bizexpenses_expense_id" + }, + "bizexpenses_expense_media_dst": { + "name": "bizexpenses_expense_media_dst", + "type": "INT", + "null": false, + "foreignTable": "media", + "foreignKey": "media_id" + } + } + }, + "bizexpenses_expense_element_media": { + "name": "bizexpenses_expense_element_media", + "fields": { + "bizexpenses_expense_element_media_id": { + "name": "bizexpenses_expense_element_media_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "bizexpenses_expense_element_media_src": { + "name": "bizexpenses_expense_element_media_src", + "type": "INT", + "null": false, + "foreignTable": "bizexpenses_expense_element", + "foreignKey": "bizexpenses_expense_element_id" + }, + "bizexpenses_expense_element_media_dst": { + "name": "bizexpenses_expense_element_media_dst", + "type": "INT", + "null": false, + "foreignTable": "media", + "foreignKey": "media_id" + } + } + } +} \ No newline at end of file diff --git a/Admin/Install/elementtypes.json b/Admin/Install/elementtypes.json new file mode 100644 index 0000000..697e366 --- /dev/null +++ b/Admin/Install/elementtypes.json @@ -0,0 +1,65 @@ +[ + { + "name": "fuel", + "l11n": { + "en": "Fuel", + "de": "Kraftstoff" + } + }, + { + "name": "entertainment", + "l11n": { + "en": "Entertainment", + "de": "Unterhaltung" + } + }, + { + "name": "breakfast", + "l11n": { + "en": "Breakfast", + "de": "Frühstück" + } + }, + { + "name": "lunch", + "l11n": { + "en": "Lunch", + "de": "Mittagessen" + } + }, + { + "name": "dinner", + "l11n": { + "en": "Dinner", + "de": "Abendessen" + } + }, + { + "name": "hotel", + "l11n": { + "en": "Hotel", + "de": "Hotel" + } + }, + { + "name": "travelling", + "l11n": { + "en": "Travelling (train, airplane, bus, taxi, ...)", + "de": "Reisen (Zug, Flugzeug, Bus, Taxi, ...)" + } + }, + { + "name": "present", + "l11n": { + "en": "Present", + "de": "Geschenk" + } + }, + { + "name": "other", + "l11n": { + "en": "Other", + "de": "Sonstiges" + } + } +] diff --git a/Admin/Install/expensetypes.json b/Admin/Install/expensetypes.json new file mode 100644 index 0000000..32b84a1 --- /dev/null +++ b/Admin/Install/expensetypes.json @@ -0,0 +1,44 @@ +[ + { + "name": "sales", + "l11n": { + "en": "Sales", + "de": "Vertrieb" + } + }, + { + "name": "supplier", + "l11n": { + "en": "Supplier", + "de": "Einkauf" + } + }, + { + "name": "education", + "l11n": { + "en": "Education", + "de": "Fortbildung" + } + }, + { + "name": "event", + "l11n": { + "en": "Event (e.g. conference, trade fair)", + "de": "Event (z.B. Konferenz, Messe)" + } + }, + { + "name": "affiliate", + "l11n": { + "en": "Affiliates", + "de": "Verbundene Unternehmen" + } + }, + { + "name": "other", + "l11n": { + "en": "Other", + "de": "Sonstiges" + } + } +] diff --git a/Admin/Installer.php b/Admin/Installer.php index c252d6c..633cdd7 100644 --- a/Admin/Installer.php +++ b/Admin/Installer.php @@ -14,7 +14,13 @@ declare(strict_types=1); namespace Modules\BusinessExpenses\Admin; +use phpOMS\Application\ApplicationAbstract; +use phpOMS\Config\SettingsInterface; +use phpOMS\Message\Http\HttpRequest; +use phpOMS\Message\Http\HttpResponse; use phpOMS\Module\InstallerAbstract; +use phpOMS\Module\ModuleInfo; +use phpOMS\Uri\HttpUri; /** * Installer class. @@ -26,4 +32,161 @@ use phpOMS\Module\InstallerAbstract; */ final class Installer extends InstallerAbstract { + /** + * Path of the file + * + * @var string + * @since 1.0.0 + */ + public const PATH = __DIR__; + + /** + * {@inheritdoc} + */ + public static function install(ApplicationAbstract $app, ModuleInfo $info, SettingsInterface $cfgHandler) : void + { + parent::install($app, $info, $cfgHandler); + + /* Expense types */ + $fileContent = \file_get_contents(__DIR__ . '/Install/expensetypes.json'); + if ($fileContent === false) { + return; + } + + /** @var array $types */ + $types = \json_decode($fileContent, true); + $expenseTypes = self::createExpenseTypes($app, $types); + + /* Element types */ + $fileContent = \file_get_contents(__DIR__ . '/Install/elementtypes.json'); + if ($fileContent === false) { + return; + } + + /** @var array $types */ + $types = \json_decode($fileContent, true); + $elementTypes = self::createExpenseElementTypes($app, $types); + } + + /** + * Install fuel type + * + * @param ApplicationAbstract $app Application + * @param array, is_required?:bool, is_custom_allowed?:bool, validation_pattern?:string, value_type?:string, values?:array}> $attributes Attribute definition + * + * @return array + * + * @since 1.0.0 + */ + private static function createExpenseTypes(ApplicationAbstract $app, array $types) : array + { + /** @var array $expenseTypes */ + $expenseTypes = []; + + /** @var \Modules\BusinessExpenses\Controller\ApiController $module */ + $module = $app->moduleManager->getModuleInstance('BusinessExpenses'); + + /** @var array $type */ + foreach ($types as $type) { + $response = new HttpResponse(); + $request = new HttpRequest(new HttpUri('')); + + $request->header->account = 1; + $request->setData('name', $type['name'] ?? ''); + $request->setData('title', \reset($type['l11n'])); + $request->setData('language', \array_keys($type['l11n'])[0] ?? 'en'); + + $module->apiExpenseTypeCreate($request, $response); + + $responseData = $response->get(''); + if (!\is_array($responseData)) { + continue; + } + + $expenseTypes[$type['name']] = !\is_array($responseData['response']) + ? $responseData['response']->toArray() + : $responseData['response']; + + $isFirst = true; + foreach ($type['l11n'] as $language => $l11n) { + if ($isFirst) { + $isFirst = false; + continue; + } + + $response = new HttpResponse(); + $request = new HttpRequest(new HttpUri('')); + + $request->header->account = 1; + $request->setData('title', $l11n); + $request->setData('language', $language); + $request->setData('type', $expenseTypes[$type['name']]['id']); + + $module->apiExpenseTypeL11nCreate($request, $response); + } + } + + return $expenseTypes; + } + + /** + * Install fuel type + * + * @param ApplicationAbstract $app Application + * @param array, is_required?:bool, is_custom_allowed?:bool, validation_pattern?:string, value_type?:string, values?:array}> $attributes Attribute definition + * + * @return array + * + * @since 1.0.0 + */ + private static function createExpenseElementTypes(ApplicationAbstract $app, array $types) : array + { + /** @var array $elementTypes */ + $elementTypes = []; + + /** @var \Modules\BusinessExpenses\Controller\ApiController $module */ + $module = $app->moduleManager->getModuleInstance('BusinessExpenses'); + + /** @var array $type */ + foreach ($types as $type) { + $response = new HttpResponse(); + $request = new HttpRequest(new HttpUri('')); + + $request->header->account = 1; + $request->setData('name', $type['name'] ?? ''); + $request->setData('title', \reset($type['l11n'])); + $request->setData('language', \array_keys($type['l11n'])[0] ?? 'en'); + + $module->apiExpenseElementTypeCreate($request, $response); + + $responseData = $response->get(''); + if (!\is_array($responseData)) { + continue; + } + + $elementTypes[$type['name']] = !\is_array($responseData['response']) + ? $responseData['response']->toArray() + : $responseData['response']; + + $isFirst = true; + foreach ($type['l11n'] as $language => $l11n) { + if ($isFirst) { + $isFirst = false; + continue; + } + + $response = new HttpResponse(); + $request = new HttpRequest(new HttpUri('')); + + $request->header->account = 1; + $request->setData('title', $l11n); + $request->setData('language', $language); + $request->setData('type', $elementTypes[$type['name']]['id']); + + $module->apiExpenseElementTypeL11nCreate($request, $response); + } + } + + return $elementTypes; + } } diff --git a/Admin/Status.php b/Admin/Status.php index 884345e..73d41cd 100644 --- a/Admin/Status.php +++ b/Admin/Status.php @@ -26,4 +26,11 @@ use phpOMS\Module\StatusAbstract; */ final class Status extends StatusAbstract { + /** + * Path of the file + * + * @var string + * @since 1.0.0 + */ + public const PATH = __DIR__; } diff --git a/Admin/Uninstaller.php b/Admin/Uninstaller.php index 2c9fff5..1e7e11b 100644 --- a/Admin/Uninstaller.php +++ b/Admin/Uninstaller.php @@ -26,4 +26,11 @@ use phpOMS\Module\UninstallerAbstract; */ final class Uninstaller extends UninstallerAbstract { + /** + * Path of the file + * + * @var string + * @since 1.0.0 + */ + public const PATH = __DIR__; } diff --git a/Admin/Updater.php b/Admin/Updater.php index e748664..310c8c1 100644 --- a/Admin/Updater.php +++ b/Admin/Updater.php @@ -26,4 +26,11 @@ use phpOMS\Module\UpdaterAbstract; */ final class Updater extends UpdaterAbstract { + /** + * Path of the file + * + * @var string + * @since 1.0.0 + */ + public const PATH = __DIR__; } diff --git a/Controller/ApiController.php b/Controller/ApiController.php new file mode 100755 index 0000000..c870c64 --- /dev/null +++ b/Controller/ApiController.php @@ -0,0 +1,688 @@ +validateExpenseTypeCreate($request))) { + $response->set($request->uri->__toString(), new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + /** @var ExpenseType $type */ + $type = $this->createExpenseTypeFromRequest($request); + $this->createModel($request->header->account, $type, ExpenseTypeMapper::class, 'expense_type', $request->getOrigin()); + + $this->fillJsonResponse( + $request, + $response, + NotificationLevel::OK, + '', + $this->app->l11nManager->getText($response->getLanguage(), '0', '0', 'SucessfulCreate'), + $type + ); + } + + /** + * Method to create type from request. + * + * @param RequestAbstract $request Request + * + * @return ExpenseType Returns the created type from the request + * + * @since 1.0.0 + */ + public function createExpenseTypeFromRequest(RequestAbstract $request) : ExpenseType + { + $type = new ExpenseType(); + $type->name = $request->getDataString('name') ?? ''; + $type->setL11n($request->getDataString('title') ?? '', $request->getDataString('language') ?? ISO639x1Enum::_EN); + + return $type; + } + + /** + * Validate type create request + * + * @param RequestAbstract $request Request + * + * @return array Returns the validation array of the request + * + * @since 1.0.0 + */ + private function validateExpenseTypeCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['name'] = !$request->hasData('name')) + || ($val['title'] = !$request->hasData('title')) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create expense attribute l11n + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiExpenseTypeL11nCreate(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void + { + if (!empty($val = $this->validateExpenseTypeL11nCreate($request))) { + $response->set('expense_type_l11n_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $typeL11n = $this->createExpenseTypeL11nFromRequest($request); + $this->createModel($request->header->account, $typeL11n, ExpenseTypeL11nMapper::class, 'expense_type_l11n', $request->getOrigin()); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Localization', 'Localization successfully created', $typeL11n); + } + + /** + * Method to create expense attribute l11n from request. + * + * @param RequestAbstract $request Request + * + * @return BaseStringL11n + * + * @since 1.0.0 + */ + private function createExpenseTypeL11nFromRequest(RequestAbstract $request) : BaseStringL11n + { + $typeL11n = new BaseStringL11n(); + $typeL11n->ref = $request->getDataInt('type') ?? 0; + $typeL11n->setLanguage( + $request->getDataString('language') ?? $request->getLanguage() + ); + $typeL11n->content = $request->getDataString('title') ?? ''; + + return $typeL11n; + } + + /** + * Validate expense attribute l11n create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateExpenseTypeL11nCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['title'] = !$request->hasData('title')) + || ($val['type'] = !$request->hasData('type')) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create a type + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiExpenseElementTypeCreate(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void + { + if (!empty($val = $this->validateExpenseElementTypeCreate($request))) { + $response->set($request->uri->__toString(), new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + /** @var ExpenseType $type */ + $type = $this->createExpenseElementTypeFromRequest($request); + $this->createModel($request->header->account, $type, ExpenseElementTypeMapper::class, 'expense_element_type', $request->getOrigin()); + + $this->fillJsonResponse( + $request, + $response, + NotificationLevel::OK, + '', + $this->app->l11nManager->getText($response->getLanguage(), '0', '0', 'SucessfulCreate'), + $type + ); + } + + /** + * Method to create type from request. + * + * @param RequestAbstract $request Request + * + * @return ExpenseType Returns the created type from the request + * + * @since 1.0.0 + */ + public function createExpenseElementTypeFromRequest(RequestAbstract $request) : ExpenseType + { + $type = new ExpenseType(); + $type->name = $request->getDataString('name') ?? ''; + $type->setL11n($request->getDataString('title') ?? '', $request->getDataString('language') ?? ISO639x1Enum::_EN); + + return $type; + } + + /** + * Validate type create request + * + * @param RequestAbstract $request Request + * + * @return array Returns the validation array of the request + * + * @since 1.0.0 + */ + private function validateExpenseElementTypeCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['name'] = !$request->hasData('name')) + || ($val['title'] = !$request->hasData('title')) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create expense attribute l11n + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiExpenseElementTypeL11nCreate(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void + { + if (!empty($val = $this->validateExpenseElementTypeL11nCreate($request))) { + $response->set('expense_element_type_l11n_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $typeL11n = $this->createExpenseElementTypeL11nFromRequest($request); + $this->createModel($request->header->account, $typeL11n, ExpenseElementTypeL11nMapper::class, 'expense_element_type_l11n', $request->getOrigin()); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Localization', 'Localization successfully created', $typeL11n); + } + + /** + * Method to create expense attribute l11n from request. + * + * @param RequestAbstract $request Request + * + * @return BaseStringL11n + * + * @since 1.0.0 + */ + private function createExpenseElementTypeL11nFromRequest(RequestAbstract $request) : BaseStringL11n + { + $typeL11n = new BaseStringL11n(); + $typeL11n->ref = $request->getDataInt('type') ?? 0; + $typeL11n->setLanguage( + $request->getDataString('language') ?? $request->getLanguage() + ); + $typeL11n->content = $request->getDataString('title') ?? ''; + + return $typeL11n; + } + + /** + * Validate expense attribute l11n create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateExpenseElementTypeL11nCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['title'] = !$request->hasData('title')) + || ($val['type'] = !$request->hasData('type')) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create expense attribute l11n + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiExpenseCreate(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void + { + if (!empty($val = $this->validateExpenseCreate($request))) { + $response->set('expense_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $expense = $this->createExpenseFromRequest($request); + $this->createModel($request->header->account, $expense, ExpenseMapper::class, 'expense', $request->getOrigin()); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Expense', 'Successfully created', $expense); + } + + /** + * Method to create expense attribute l11n from request. + * + * @param RequestAbstract $request Request + * + * @return Expense + * + * @since 1.0.0 + */ + private function createExpenseFromRequest(RequestAbstract $request) : Expense + { + $expense = new Expense(); + $expense->from = new NullAccount((int) $request->header->account); + $expense->type = new NullExpenseType((int) $request->getDataInt('type')); + $expense->status = (int) ($request->getDataInt('status') ?? ExpenseStatus::DRAFT); + $expense->description = $request->getDataString('description') ?? ''; + + $country = $request->getDataString('country') ?? ''; + if (empty($country)) { + $account = $this->app->accountManager->get($request->header->account); + if ($account->id === 0) { + $account = AccountMapper::get()->with('l11n')->where('id', $request->header->account)->execute(); + } + + $country = $account->l11n->country; + } + + $expense->country = $country; + + return $expense; + } + + /** + * Validate expense attribute l11n create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateExpenseCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['type'] = !$request->hasData('type')) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create expense attribute l11n + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiExpenseElementCreate(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void + { + if (!empty($val = $this->validateExpenseElementCreate($request))) { + $response->set('expense_element_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $element = $this->createExpenseElementFromRequest($request); + $this->createModel($request->header->account, $element, ExpenseElementMapper::class, 'expense_element', $request->getOrigin()); + + if ($request->hasFiles()) { + $request->setData('element', $element->id, true); + $this->apiMediaAddToExpenseElement($request, $response, $data); + + // @todo: refill element with parsed data from media (ocr) + } + + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Element', 'Successfully created', $element); + } + + /** + * Method to create expense attribute l11n from request. + * + * @param RequestAbstract $request Request + * + * @return ExpenseElement + * + * @since 1.0.0 + */ + private function createExpenseElementFromRequest(RequestAbstract $request) : ExpenseElement + { + $element = new ExpenseElement(); + $element->expense = (int) $request->getData('expense'); + $element->description = $request->getDataString('description') ?? ''; + $element->type = new NullExpenseElementType((int) $request->getData('type')); + + // @todo: fill from media if available + + // @todo: handle different value set (net, gross, taxr, ...). + // Depending on the value set the other values should be calculated + $element->net = new FloatInt($request->getDataInt('net') ?? 0); + $element->taxR = new FloatInt($request->getDataInt('taxr') ?? 0); + $element->taxP = new FloatInt($request->getDataInt('taxp') ?? 0); + $element->gross = new FloatInt($request->getDataInt('gross') ?? 0); + $element->quantity = new FloatInt($request->getDataInt('quantity') ?? 0); + + if ($request->hasData('supplier')) { + $element->supplier = new NullSupplier((int) $request->getData('supplier')); + } + + // @todo: use country of expense if no country is set + $country = $request->getDataString('country') ?? ''; + if (empty($country)) { + $account = $this->app->accountManager->get($request->header->account); + if ($account->id === 0) { + $account = AccountMapper::get()->with('l11n')->where('id', $request->header->account)->execute(); + } + + $country = $account->l11n->country; + } + + $element->country = $country; + + return $element; + } + + /** + * Api method to create a bill + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiMediaAddToExpenseElement(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void + { + if (!empty($val = $this->validateMediaAddToExpenseElement($request))) { + $response->set($request->uri->__toString(), new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + /** @var \Modules\BusinessExpenses\Models\Expense $expense */ + $expense = ExpenseMapper::get()->where('id', (int) $request->getData('expense'))->execute(); + $path = $this->createExpenseDir($expense); + + $element = (int) $request->getData('element'); + + $uploaded = []; + if (!empty($uploadedFiles = $request->getFiles())) { + $uploaded = $this->app->moduleManager->get('Media')->uploadFiles( + names: [], + fileNames: [], + files: $uploadedFiles, + account: $request->header->account, + basePath: __DIR__ . '/../../../Modules/Media/Files' . $path, + virtualPath: $path, + pathSettings: PathSettings::FILE_PATH, + hasAccountRelation: false, + readContent: (bool) ($request->getData('parse_content') ?? false) + ); + + $collection = null; + foreach ($uploaded as $media) { + $this->createModelRelation( + $request->header->account, + $element, + $media->id, + ExpenseElementMapper::class, + 'media', + '', + $request->getOrigin() + ); + + if ($request->hasData('type')) { + $this->createModelRelation( + $request->header->account, + $media->id, + $request->getDataInt('type'), + MediaMapper::class, + 'types', + '', + $request->getOrigin() + ); + } + + if ($collection === null) { + $collection = MediaMapper::getParentCollection($path)->limit(1)->execute(); + + if ($collection->id === 0) { + $collection = $this->app->moduleManager->get('Media')->createRecursiveMediaCollection( + $path, + $request->header->account, + __DIR__ . '/../../../Modules/Media/Files' . $path, + ); + } + } + + $this->createModelRelation( + $request->header->account, + $collection->id, + $media->id, + CollectionMapper::class, + 'sources', + '', + $request->getOrigin() + ); + } + } + + if (!empty($mediaFiles = $request->getDataJson('media'))) { + foreach ($mediaFiles as $media) { + $this->createModelRelation( + $request->header->account, + $element, + (int) $media, + ExpenseElementMapper::class, + 'media', + '', + $request->getOrigin() + ); + } + } + + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Media', 'Media added to bill.', [ + 'upload' => $uploaded, + 'media' => $mediaFiles, + ]); + } + + /** + * Method to validate bill creation from request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateMediaAddToExpenseElement(RequestAbstract $request) : array + { + $val = []; + if (($val['media'] = (!$request->hasData('media') && empty($request->getFiles()))) + || ($val['expense'] = !$request->hasData('expense')) + || ($val['element'] = !$request->hasData('element')) + ) { + return $val; + } + + return []; + } + + /** + * Validate expense attribute l11n create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateExpenseElementCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['expense'] = !$request->hasData('expense')) + || ($val['type'] = !$request->hasData('type')) + ) { + return $val; + } + + return []; + } + + /** + * Create media directory path + * + * @param Expense $expense Expense + * + * @return string + * + * @since 1.0.0 + */ + private function createExpenseDir(Expense $expense) : string + { + return '/Modules/BusinessExpenses/Expense/' + . $this->app->unitId . '/' + . $expense->createdAt->format('Y/m/d') . '/' + . $expense->id; + } + + /** + * Api method to create bill files + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiExpenseFromUpload(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void + { + } +} diff --git a/Controller/BackendController.php b/Controller/BackendController.php new file mode 100755 index 0000000..febf43c --- /dev/null +++ b/Controller/BackendController.php @@ -0,0 +1,76 @@ +app->l11nManager, $request, $response); + + $view->setTemplate('/Modules/BusinessExpenses/Theme/Backend/expense-list'); + $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1001001001, $request, $response)); + + return $view; + } + + /** + * Routing end-point for application behaviour. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return RenderableInterface Returns a renderable object + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewBusinessExpensesExpense(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : RenderableInterface + { + $view = new View($this->app->l11nManager, $request, $response); + + $view->setTemplate('/Modules/BusinessExpenses/Theme/Backend/expenses-profile'); + $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1001001001, $request, $response)); + + return $view; + } +} diff --git a/Controller/Controller.php b/Controller/Controller.php new file mode 100755 index 0000000..4187ec5 --- /dev/null +++ b/Controller/Controller.php @@ -0,0 +1,76 @@ +type = new ExpenseType(); + $this->start = new \DateTime('now'); + $this->end = new \DateTime('now'); + $this->createdAt = new \DateTimeImmutable('now'); + $this->from = new Account(); + + $this->net = new FloatInt(); + $this->gross = new FloatInt(); + $this->taxP = new FloatInt(); + } +} diff --git a/Models/ExpenseElement.php b/Models/ExpenseElement.php new file mode 100644 index 0000000..1c2031f --- /dev/null +++ b/Models/ExpenseElement.php @@ -0,0 +1,77 @@ +type = new ExpenseElementType(); + $this->net = new FloatInt(); + $this->taxR = new FloatInt(); + $this->taxP = new FloatInt(); + $this->gross = new FloatInt(); + $this->quantity = new FloatInt(); + $this->start = new \DateTime('now'); + $this->end = new \DateTime('now'); + } +} diff --git a/Models/ExpenseElementMapper.php b/Models/ExpenseElementMapper.php new file mode 100644 index 0000000..76356a6 --- /dev/null +++ b/Models/ExpenseElementMapper.php @@ -0,0 +1,128 @@ + + */ +final class ExpenseElementMapper extends DataMapperFactory +{ + /** + * Columns. + * + * @var array + * @since 1.0.0 + */ + public const COLUMNS = [ + 'bizexpenses_expense_element_id' => ['name' => 'bizexpenses_expense_element_id', 'type' => 'int', 'internal' => 'id'], + 'bizexpenses_expense_element_description' => ['name' => 'bizexpenses_expense_element_description', 'type' => 'string', 'internal' => 'description'], + 'bizexpenses_expense_element_approved' => ['name' => 'bizexpenses_expense_element_approved', 'type' => 'bool', 'internal' => 'approved'], + 'bizexpenses_expense_element_net' => ['name' => 'bizexpenses_expense_element_net', 'type' => 'Serializable', 'internal' => 'net'], + 'bizexpenses_expense_element_gross' => ['name' => 'bizexpenses_expense_element_gross', 'type' => 'Serializable', 'internal' => 'gross'], + 'bizexpenses_expense_element_taxp' => ['name' => 'bizexpenses_expense_element_taxp', 'type' => 'Serializable', 'internal' => 'taxP'], + 'bizexpenses_expense_element_taxr' => ['name' => 'bizexpenses_expense_element_taxr', 'type' => 'Serializable', 'internal' => 'taxR'], + 'bizexpenses_expense_element_quantity' => ['name' => 'bizexpenses_expense_element_quantity', 'type' => 'Serializable', 'internal' => 'quantity'], + 'bizexpenses_expense_element_taxid' => ['name' => 'bizexpenses_expense_element_taxid', 'type' => 'string', 'internal' => 'taxId'], + 'bizexpenses_expense_element_start' => ['name' => 'bizexpenses_expense_element_start', 'type' => 'DateTime', 'internal' => 'start'], + 'bizexpenses_expense_element_end' => ['name' => 'bizexpenses_expense_element_end', 'type' => 'DateTime', 'internal' => 'end'], + 'bizexpenses_expense_element_supplier' => ['name' => 'bizexpenses_expense_element_supplier', 'type' => 'int', 'internal' => 'supplier'], + 'bizexpenses_expense_element_ref' => ['name' => 'bizexpenses_expense_element_ref', 'type' => 'int', 'internal' => 'ref'], + 'bizexpenses_expense_element_type' => ['name' => 'bizexpenses_expense_element_type', 'type' => 'int', 'internal' => 'type'], + 'bizexpenses_expense_element_country' => ['name' => 'bizexpenses_expense_element_country', 'type' => 'string', 'internal' => 'country'], + 'bizexpenses_expense_element_expense' => ['name' => 'bizexpenses_expense_element_expense', 'type' => 'int', 'internal' => 'expense'], + ]; + + /** + * Has many relation. + * + * @var array + * @since 1.0.0 + */ + public const HAS_MANY = [ + 'media' => [ + 'mapper' => MediaMapper::class, + 'table' => 'bizexpenses_expense_element_media', + 'external' => 'bizexpenses_expense_element_media_dst', + 'self' => 'bizexpenses_expense_element_media_src', + ], + ]; + + /** + * Belongs to. + * + * @var array + * @since 1.0.0 + */ + public const BELONGS_TO = [ + 'ref' => [ + 'mapper' => AccountMapper::class, + 'external' => 'bizexpenses_expense_element_ref', + ], + 'supplier' => [ + 'mapper' => SupplierMapper::class, + 'external' => 'bizexpenses_expense_element_supplier', + ], + ]; + + /** + * Has one relation. + * + * @var array + * @since 1.0.0 + */ + public const OWNS_ONE = [ + 'type' => [ + 'mapper' => ExpenseElementTypeMapper::class, + 'external' => 'bizexpenses_expense_element_type', + ], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + public const TABLE = 'bizexpenses_expense_element'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + public const PRIMARYFIELD = 'bizexpenses_expense_element_id'; + + /** + * Model to use by the mapper. + * + * @var class-string + * @since 1.0.0 + */ + public const MODEL = BaseStringL11n::class; +} diff --git a/Models/ExpenseElementType.php b/Models/ExpenseElementType.php new file mode 100644 index 0000000..b4123de --- /dev/null +++ b/Models/ExpenseElementType.php @@ -0,0 +1,121 @@ +name = $name; + } + + /** + * Set l11n + * + * @param string|BaseStringL11n $l11n Tag article l11n + * @param string $lang Language + * + * @return void + * + * @since 1.0.0 + */ + public function setL11n(string | BaseStringL11n $l11n, string $lang = ISO639x1Enum::_EN) : void + { + if ($l11n instanceof BaseStringL11n) { + $this->l11n = $l11n; + } elseif (isset($this->l11n) && $this->l11n instanceof BaseStringL11n) { + $this->l11n->content = $l11n; + $this->l11n->setLanguage($lang); + } else { + $this->l11n = new BaseStringL11n(); + $this->l11n->content = $l11n; + $this->l11n->setLanguage($lang); + } + } + + /** + * @return string + * + * @since 1.0.0 + */ + public function getL11n() : string + { + if (!isset($this->l11n)) { + return ''; + } + + return $this->l11n instanceof BaseStringL11n ? $this->l11n->content : $this->l11n; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + ]; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() : mixed + { + return $this->toArray(); + } +} diff --git a/Models/ExpenseElementTypeL11nMapper.php b/Models/ExpenseElementTypeL11nMapper.php new file mode 100644 index 0000000..c628183 --- /dev/null +++ b/Models/ExpenseElementTypeL11nMapper.php @@ -0,0 +1,69 @@ + + */ +final class ExpenseElementTypeL11nMapper extends DataMapperFactory +{ + /** + * Columns. + * + * @var array + * @since 1.0.0 + */ + public const COLUMNS = [ + 'bizexpenses_expense_element_type_l11n_id' => ['name' => 'bizexpenses_expense_element_type_l11n_id', 'type' => 'int', 'internal' => 'id'], + 'bizexpenses_expense_element_type_l11n_title' => ['name' => 'bizexpenses_expense_element_type_l11n_title', 'type' => 'string', 'internal' => 'content', 'autocomplete' => true], + 'bizexpenses_expense_element_type_l11n_type' => ['name' => 'bizexpenses_expense_element_type_l11n_type', 'type' => 'int', 'internal' => 'ref'], + 'bizexpenses_expense_element_type_l11n_lang' => ['name' => 'bizexpenses_expense_element_type_l11n_lang', 'type' => 'string', 'internal' => 'language'], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + public const TABLE = 'bizexpenses_expense_element_type_l11n'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + public const PRIMARYFIELD = 'bizexpenses_expense_element_type_l11n_id'; + + /** + * Model to use by the mapper. + * + * @var class-string + * @since 1.0.0 + */ + public const MODEL = BaseStringL11n::class; +} diff --git a/Models/ExpenseElementTypeMapper.php b/Models/ExpenseElementTypeMapper.php new file mode 100644 index 0000000..7e1c5cb --- /dev/null +++ b/Models/ExpenseElementTypeMapper.php @@ -0,0 +1,82 @@ + + */ +final class ExpenseElementTypeMapper extends DataMapperFactory +{ + /** + * Columns. + * + * @var array + * @since 1.0.0 + */ + public const COLUMNS = [ + 'bizexpenses_expense_element_type_id' => ['name' => 'bizexpenses_expense_element_type_id', 'type' => 'int', 'internal' => 'id'], + 'bizexpenses_expense_element_type_name' => ['name' => 'bizexpenses_expense_element_type_name', 'type' => 'string', 'internal' => 'name', 'autocomplete' => true], + ]; + + /** + * Has many relation. + * + * @var array + * @since 1.0.0 + */ + public const HAS_MANY = [ + 'l11n' => [ + 'mapper' => ExpenseElementTypeL11nMapper::class, + 'table' => 'bizexpenses_expense_element_type_l11n', + 'self' => 'bizexpenses_expense_element_type_l11n_type', + 'column' => 'content', + 'external' => null, + ], + ]; + + /** + * Model to use by the mapper. + * + * @var class-string + * @since 1.0.0 + */ + public const MODEL = ExpenseElementType::class; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + public const TABLE = 'bizexpenses_expense_element_type'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + public const PRIMARYFIELD = 'bizexpenses_expense_element_type_id'; +} diff --git a/Models/ExpenseMapper.php b/Models/ExpenseMapper.php new file mode 100644 index 0000000..3624e74 --- /dev/null +++ b/Models/ExpenseMapper.php @@ -0,0 +1,127 @@ + + */ +final class ExpenseMapper extends DataMapperFactory +{ + /** + * Columns. + * + * @var array + * @since 1.0.0 + */ + public const COLUMNS = [ + 'bizexpenses_expense_id' => ['name' => 'bizexpenses_expense_id', 'type' => 'int', 'internal' => 'id'], + 'bizexpenses_expense_status' => ['name' => 'bizexpenses_expense_status', 'type' => 'int', 'internal' => 'status'], + 'bizexpenses_expense_description' => ['name' => 'bizexpenses_expense_description', 'type' => 'string', 'internal' => 'description'], + 'bizexpenses_expense_approved' => ['name' => 'bizexpenses_expense_approved', 'type' => 'bool', 'internal' => 'approved'], + 'bizexpenses_expense_paid' => ['name' => 'bizexpenses_expense_paid', 'type' => 'bool', 'internal' => 'paid'], + 'bizexpenses_expense_net' => ['name' => 'bizexpenses_expense_net', 'type' => 'Serializable', 'internal' => 'net'], + 'bizexpenses_expense_gross' => ['name' => 'bizexpenses_expense_gross', 'type' => 'Serializable', 'internal' => 'gross'], + 'bizexpenses_expense_taxp' => ['name' => 'bizexpenses_expense_taxp', 'type' => 'Serializable', 'internal' => 'taxP'], + 'bizexpenses_expense_created' => ['name' => 'bizexpenses_expense_created', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt'], + 'bizexpenses_expense_start' => ['name' => 'bizexpenses_expense_start', 'type' => 'DateTime', 'internal' => 'start'], + 'bizexpenses_expense_end' => ['name' => 'bizexpenses_expense_end', 'type' => 'DateTime', 'internal' => 'end'], + 'bizexpenses_expense_type' => ['name' => 'bizexpenses_expense_type', 'type' => 'int', 'internal' => 'type'], + 'bizexpenses_expense_from' => ['name' => 'bizexpenses_expense_from', 'type' => 'int', 'internal' => 'from'], + 'bizexpenses_expense_country' => ['name' => 'bizexpenses_expense_country', 'type' => 'string', 'internal' => 'country'], + ]; + + /** + * Has many relation. + * + * @var array + * @since 1.0.0 + */ + public const HAS_MANY = [ + 'elements' => [ + 'mapper' => ExpenseElementMapper::class, + 'table' => 'bizexpenses_expense_element', + 'self' => 'bizexpenses_expense_element_expense', + 'external' => null, + ], + 'media' => [ + 'mapper' => MediaMapper::class, + 'table' => 'bizexpenses_expense_media', + 'external' => 'bizexpenses_expense_media_dst', + 'self' => 'bizexpenses_expense_media_src', + ], + ]; + + /** + * Belongs to. + * + * @var array + * @since 1.0.0 + */ + public const BELONGS_TO = [ + 'from' => [ + 'mapper' => AccountMapper::class, + 'external' => 'bizexpenses_expense_from', + ], + ]; + + /** + * Has one relation. + * + * @var array + * @since 1.0.0 + */ + public const OWNS_ONE = [ + 'type' => [ + 'mapper' => ExpenseTypeMapper::class, + 'external' => 'bizexpenses_expense_type', + ], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + public const TABLE = 'bizexpenses_expense'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + public const PRIMARYFIELD = 'bizexpenses_expense_id'; + + /** + * Model to use by the mapper. + * + * @var class-string + * @since 1.0.0 + */ + public const MODEL = Expense::class; +} diff --git a/Models/ExpenseStatus.php b/Models/ExpenseStatus.php new file mode 100644 index 0000000..8c198e5 --- /dev/null +++ b/Models/ExpenseStatus.php @@ -0,0 +1,34 @@ +name = $name; + } + + /** + * Set l11n + * + * @param string|BaseStringL11n $l11n Tag article l11n + * @param string $lang Language + * + * @return void + * + * @since 1.0.0 + */ + public function setL11n(string | BaseStringL11n $l11n, string $lang = ISO639x1Enum::_EN) : void + { + if ($l11n instanceof BaseStringL11n) { + $this->l11n = $l11n; + } elseif (isset($this->l11n) && $this->l11n instanceof BaseStringL11n) { + $this->l11n->content = $l11n; + $this->l11n->setLanguage($lang); + } else { + $this->l11n = new BaseStringL11n(); + $this->l11n->content = $l11n; + $this->l11n->setLanguage($lang); + } + } + + /** + * @return string + * + * @since 1.0.0 + */ + public function getL11n() : string + { + if (!isset($this->l11n)) { + return ''; + } + + return $this->l11n instanceof BaseStringL11n ? $this->l11n->content : $this->l11n; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + ]; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() : mixed + { + return $this->toArray(); + } +} diff --git a/Models/ExpenseTypeL11nMapper.php b/Models/ExpenseTypeL11nMapper.php new file mode 100644 index 0000000..d0e241f --- /dev/null +++ b/Models/ExpenseTypeL11nMapper.php @@ -0,0 +1,69 @@ + + */ +final class ExpenseTypeL11nMapper extends DataMapperFactory +{ + /** + * Columns. + * + * @var array + * @since 1.0.0 + */ + public const COLUMNS = [ + 'bizexpenses_expense_type_l11n_id' => ['name' => 'bizexpenses_expense_type_l11n_id', 'type' => 'int', 'internal' => 'id'], + 'bizexpenses_expense_type_l11n_title' => ['name' => 'bizexpenses_expense_type_l11n_title', 'type' => 'string', 'internal' => 'content', 'autocomplete' => true], + 'bizexpenses_expense_type_l11n_type' => ['name' => 'bizexpenses_expense_type_l11n_type', 'type' => 'int', 'internal' => 'ref'], + 'bizexpenses_expense_type_l11n_lang' => ['name' => 'bizexpenses_expense_type_l11n_lang', 'type' => 'string', 'internal' => 'language'], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + public const TABLE = 'bizexpenses_expense_type_l11n'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + public const PRIMARYFIELD = 'bizexpenses_expense_type_l11n_id'; + + /** + * Model to use by the mapper. + * + * @var class-string + * @since 1.0.0 + */ + public const MODEL = BaseStringL11n::class; +} diff --git a/Models/ExpenseTypeMapper.php b/Models/ExpenseTypeMapper.php new file mode 100644 index 0000000..14556bd --- /dev/null +++ b/Models/ExpenseTypeMapper.php @@ -0,0 +1,82 @@ + + */ +final class ExpenseTypeMapper extends DataMapperFactory +{ + /** + * Columns. + * + * @var array + * @since 1.0.0 + */ + public const COLUMNS = [ + 'bizexpenses_expense_type_id' => ['name' => 'bizexpenses_expense_type_id', 'type' => 'int', 'internal' => 'id'], + 'bizexpenses_expense_type_name' => ['name' => 'bizexpenses_expense_type_name', 'type' => 'string', 'internal' => 'name', 'autocomplete' => true], + ]; + + /** + * Has many relation. + * + * @var array + * @since 1.0.0 + */ + public const HAS_MANY = [ + 'l11n' => [ + 'mapper' => ExpenseTypeL11nMapper::class, + 'table' => 'bizexpenses_expense_type_l11n', + 'self' => 'bizexpenses_expense_type_l11n_type', + 'column' => 'content', + 'external' => null, + ], + ]; + + /** + * Model to use by the mapper. + * + * @var class-string + * @since 1.0.0 + */ + public const MODEL = ExpenseType::class; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + public const TABLE = 'bizexpenses_expense_type'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + public const PRIMARYFIELD = 'bizexpenses_expense_type_id'; +} diff --git a/Models/NullExpense.php b/Models/NullExpense.php new file mode 100644 index 0000000..2d128a8 --- /dev/null +++ b/Models/NullExpense.php @@ -0,0 +1,46 @@ +id = $id; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() : mixed + { + return ['id' => $this->id]; + } +} diff --git a/Models/NullExpenseElement.php b/Models/NullExpenseElement.php new file mode 100644 index 0000000..dd3655e --- /dev/null +++ b/Models/NullExpenseElement.php @@ -0,0 +1,46 @@ +id = $id; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() : mixed + { + return ['id' => $this->id]; + } +} diff --git a/Models/NullExpenseElementType.php b/Models/NullExpenseElementType.php new file mode 100644 index 0000000..b09cfc4 --- /dev/null +++ b/Models/NullExpenseElementType.php @@ -0,0 +1,46 @@ +id = $id; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() : mixed + { + return ['id' => $this->id]; + } +} diff --git a/Models/NullExpenseType.php b/Models/NullExpenseType.php new file mode 100644 index 0000000..0d572ad --- /dev/null +++ b/Models/NullExpenseType.php @@ -0,0 +1,46 @@ +id = $id; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() : mixed + { + return ['id' => $this->id]; + } +} diff --git a/Theme/Backend/Lang/Navigation.en.lang.php b/Theme/Backend/Lang/Navigation.en.lang.php index 5415a4b..479b3fc 100644 --- a/Theme/Backend/Lang/Navigation.en.lang.php +++ b/Theme/Backend/Lang/Navigation.en.lang.php @@ -13,4 +13,5 @@ declare(strict_types=1); return ['Navigation' => [ + 'BusinessExpenses' => 'Business Expenses', ]]; diff --git a/Theme/Backend/expense-single.tpl.php b/Theme/Backend/expense-single.tpl.php new file mode 100644 index 0000000..e69de29 diff --git a/Theme/Backend/expenses-list.tpl.php b/Theme/Backend/expenses-list.tpl.php new file mode 100644 index 0000000..e69de29 diff --git a/info.json b/info.json index 43d1f15..7753ae8 100644 --- a/info.json +++ b/info.json @@ -11,13 +11,14 @@ "phpOMS-db": "1.0.0" }, "creator": { - "name": "Orange Management", - "website": "www.spl1nes.com" + "name": "Jingga", + "website": "jingga.app" }, "description": "Accounting module.", "directory": "BusinessExpenses", "dependencies": { - "Admin": "1.0.0" + "Admin": "1.0.0", + "Supplier": "1.0.0" }, "providing": { "Navigation": "*"