From 30694202da51db8c9e0df9e2d319fcc1566f5c5c Mon Sep 17 00:00:00 2001 From: Luca Haid Date: Fri, 31 Jan 2025 16:09:03 +0100 Subject: [PATCH] Added new WarehouseOrder Module --- Layout/default/VueViews/Vue.php | 47 ++-- .../WarehouseEShopController.php | 16 +- .../WarehouseEShopOrderController.php | 6 +- .../WarehouseHistoryController.php | 2 +- .../WarehouseOrderController.php | 117 ++++------ .../WarehouseOrder/WarehouseOrderModel.php | 48 ++-- .../WarehouseOrderItem/WarehouseOrderItem.php | 9 - .../WarehouseOrderItemModel.php | 16 -- .../WarehouseOrderRequestController.php | 16 +- .../20250131150000_warehouse_modify_10.php | 57 +++++ lib/TTCrud/TTCrud.php | 77 +++--- public/bundler.php | 1 + .../pages/WarehouseOrder/WarehouseOrder.css | 27 +++ .../js/pages/WarehouseOrder/WarehouseOrder.js | 219 +++++++++++++----- .../tt-components/css/tt-position-manager.css | 64 +++++ .../vue/tt-components/tt-autocomplete.js | 19 +- .../vue/tt-components/tt-position-manager.js | 163 +++++++++++++ 17 files changed, 623 insertions(+), 281 deletions(-) delete mode 100644 application/WarehouseOrderItem/WarehouseOrderItem.php delete mode 100644 application/WarehouseOrderItem/WarehouseOrderItemModel.php create mode 100644 db/migrations/20250131150000_warehouse_modify_10.php create mode 100644 public/js/pages/WarehouseOrder/WarehouseOrder.css create mode 100644 public/plugins/vue/tt-components/css/tt-position-manager.css create mode 100644 public/plugins/vue/tt-components/tt-position-manager.js diff --git a/Layout/default/VueViews/Vue.php b/Layout/default/VueViews/Vue.php index 491c0e354..35a73afdd 100644 --- a/Layout/default/VueViews/Vue.php +++ b/Layout/default/VueViews/Vue.php @@ -1,64 +1,47 @@ -
:path="window['TT_CONFIG']['PATH']"> - - <>> + <> +>
- \ No newline at end of file + diff --git a/application/WarehouseEShop/WarehouseEShopController.php b/application/WarehouseEShop/WarehouseEShopController.php index af598a276..956689926 100644 --- a/application/WarehouseEShop/WarehouseEShopController.php +++ b/application/WarehouseEShop/WarehouseEShopController.php @@ -4,7 +4,6 @@ // Hide Articles - class WarehouseEShopController extends TTCrud { protected string $headerTitle = 'Energie Steiermark Shop'; protected bool $createText = false; @@ -12,11 +11,13 @@ class WarehouseEShopController extends TTCrud { protected array $columns = [ ['key' => 'title', 'text' => 'Artikel', 'priority' => 11], ['key' => 'category', 'text' => 'Kategorie', 'table' => false], - ['key' => 'price', 'text' => 'Preis', 'table' => ['filter' => false,'sortable' => false,'class' => 'text-right']], - ['key' => 'amount', 'text' => 'Menge', 'table' => ['filter' => false,'sortable' => false,'class' => 'p-0 width-80'], 'priority' => 9], - ['key' => 'add', 'text' => 'Hinzufügen', 'table' => ['filter' => false,'sortable' => false, 'class' => 'width-120 text-center'], 'priority' => 5000] + ['key' => 'price', 'text' => 'Preis', 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-right']], + ['key' => 'amount', 'text' => 'Menge', 'table' => ['filter' => false, 'sortable' => false, 'class' => 'p-0 width-80'], 'priority' => 9], + ['key' => 'add', 'text' => 'Hinzufügen', 'table' => ['filter' => false, 'sortable' => false, 'class' => 'width-120 text-center'], 'priority' => 5000] ]; + protected array $permissionCheck = ['WarehouseEShop']; + protected array $infoMessages = [ 'create' => 'Not possible', 'update' => 'Not possible', @@ -24,10 +25,6 @@ class WarehouseEShopController extends TTCrud { 'noChanges' => 'Keine Änderungen', ]; - public function permissionCheck(): bool { - return $this->user->can(["WarehouseEShop"]); - } - protected function prepareCrudConfig() { if (!$this->user->can('WarehouseAdmin')) { $this->columns[2]['table'] = false; @@ -62,5 +59,6 @@ class WarehouseEShopController extends TTCrud { "total_pages" => ceil($filteredAvailable / $perPage), "per_page" => $perPage, "filtered_available" => $filteredAvailable, - "total_rows" => $totalRows]]); } + "total_rows" => $totalRows]]); + } } \ No newline at end of file diff --git a/application/WarehouseEShopOrder/WarehouseEShopOrderController.php b/application/WarehouseEShopOrder/WarehouseEShopOrderController.php index 72c0fe1fc..18d5bf81c 100644 --- a/application/WarehouseEShopOrder/WarehouseEShopOrderController.php +++ b/application/WarehouseEShopOrder/WarehouseEShopOrderController.php @@ -23,6 +23,8 @@ class WarehouseEShopOrderController extends TTCrud { ['key' => 'actions', 'text' => 'Aktionen', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']] ]; + protected array $permissionCheck = ['WarehouseEShop']; + protected array $additionalActions = [ ['key' => 'openHistory', 'title' => 'Historie', 'class' => 'fas fa-history text-primary'], ['key' => 'showTrackingHistory', 'title' => 'Tracking Historie', 'class' => 'fas fa-truck text-primary'], @@ -38,10 +40,6 @@ class WarehouseEShopOrderController extends TTCrud { ]; //@formatter:on - public function permissionCheck(): bool { - return $this->user->can(["WarehouseEShop"]); - } - protected function customRowsHandler($rows): array { $statusToText = [ 'new' => 'Neu', diff --git a/application/WarehouseHistory/WarehouseHistoryController.php b/application/WarehouseHistory/WarehouseHistoryController.php index ea395d65b..e52dcbccd 100644 --- a/application/WarehouseHistory/WarehouseHistoryController.php +++ b/application/WarehouseHistory/WarehouseHistoryController.php @@ -19,7 +19,7 @@ class WarehouseHistoryController { WarehouseHistoryModel::create(['table' => $mod, 'row_id' => $postData['id'], 'key' => $key, - 'old_value' => $currentData->$key, + 'old_value' => $currentData->$key ?? '', 'new_value' => $value, 'note' => '', 'user_id' => $me->id, diff --git a/application/WarehouseOrder/WarehouseOrderController.php b/application/WarehouseOrder/WarehouseOrderController.php index 18ad077ca..d964adf1d 100644 --- a/application/WarehouseOrder/WarehouseOrderController.php +++ b/application/WarehouseOrder/WarehouseOrderController.php @@ -7,23 +7,26 @@ class WarehouseOrderController extends TTCrud { protected array $columns = [ ['key' => 'id', 'text' => 'ID', 'modal' => false], - ['key' => 'distributorId', 'text' => 'Lieferant', 'required' => true, 'type' => 'autocomplete','table' => ['class' => 'text-nowrap', 'filter' => 'autocomplete'],'modal' => [ - 'apiUrl' => 'WarehouseDistributor/autocomplete','items' => 'WarehouseDistributor/autocomplete', 'type' => 'autocomplete']], - ['key' => 'extRef', 'text' => 'Externe Referenz', 'required' => false], - ['key' => 'intRef', 'text' => 'Interne Referenz', 'required' => false], - ['key' => 'status', 'text' => 'Status', 'required' => true, 'modal' => ['type' => 'select', 'items' => [ - ['value' => 'new', 'text' => 'Neu'], - ['value' => 'accepted', 'text' => 'An Lieferant übergeben'], - ['value' => 'sent', 'text' => 'Gesendet'], - ['value' => 'done', 'text' => 'Erledigt'], - ]]], - ['key' => 'trackingNumber', 'text' => 'Trackingnummer', 'required' => false], - ['key' => 'sum', 'text' => 'Summe', 'required' => true, 'modal' => false, 'table' => ['filter' => 'numberRange']], - ['key' => 'create', 'text' => 'Erstellt', 'required' => true, 'modal' => false, 'filter' => 'datetime'], - ['key' => 'createBy', 'text' => 'Erstellt von', 'required' => true, 'table' => ['filter' => 'select'], 'modal' => ['type' => 'select', 'items' => []]], - ['key' => 'actions', 'text' => 'Aktionen', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']], + ['key' => 'orderNumber', 'text' => 'Bestellnummer', 'required' => true, 'modal' => false], + ['key' => 'delAddrCity', 'text' => 'Stadt', 'required' => true, 'modal' => false], + ['key' => 'delAddrEMail', 'text' => 'E-Mail', 'required' => true, 'modal' => false], + ['key' => 'delAddrLine', 'text' => 'Adresse', 'required' => true, 'modal' => false], + ['key' => 'delAddrName', 'text' => 'Name', 'required' => true, 'modal' => false], + ['key' => 'delAddrPLZ', 'text' => 'PLZ', 'required' => true, 'modal' => false], + ['key' => 'editor', 'text' => 'Bearbeiter', 'required' => true, 'modal' => false], + ['key' => 'note', 'text' => 'Notiz', 'required' => true, 'modal' => false], + ['key' => 'positions', 'text' => 'Positionen', 'required' => true, 'modal' => false], + ['key' => 'create', 'text' => 'Erstellt', 'required' => true, 'modal' => false], + ['key' => 'createBy', 'text' => 'Erstellt von', 'required' => true, 'modal' => ['type' => 'select']], + ['key' => 'actions', + 'text' => 'Aktionen', + 'required' => false, + 'modal' => false, + 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']], ]; + protected array $permissionCheck = ['WarehouseAdmin']; + protected array $additionalActions = [['key' => 'openHistory', 'title' => 'Historie', 'class' => 'fas fa-history text-primary']]; protected array $infoMessages = ['create' => 'Bestellung wurde erfolgreich erstellt.', @@ -31,72 +34,36 @@ class WarehouseOrderController extends TTCrud { 'delete' => 'Bestellung wurde gelöscht', 'noChanges' => 'Keine Änderungen',]; - public function permissionCheck(): bool { - return $this->user->can(["WarehouseEShop"]); + protected function beforeCreate(): bool { + $currentCount = WarehouseOrderModel::count(['create' => ['from' => strtotime(date('Y-01-01'))]]); + $this->postData['orderNumber'] = 'PO' . date('Y') . '-' . str_pad($currentCount + 1, 4, '0', STR_PAD_LEFT); + + return true; } - protected function prepareCrudConfig() { - // Fill Users in createBy column - $column = array_search('createBy', array_column($this->columns, 'key')); - $this->columns[$column]['modal']['items'] = array_map(function ($user) { - return ['value' => intval($user->id), 'text' => $user->name]; - }, UserModel::search()); + protected function getArticleDistributorDataAction() { + $data = []; + $article = $this->request->articleId; - } - - protected function createOrderAction() { - ini_set('display_errors', 1); - ini_set('display_startup_errors', 1); - error_reporting(E_ALL); - - $json = json_decode(file_get_contents('php://input'), true); - $orders = $json; - $orderIds = []; - - foreach ($orders as $order) { - $distributor = $order['distributor'][0]; - $orderAmount = $order['orderAmount']; - $orders = $order['orders']; - - $order = [ - 'distributorId' => $distributor['id'], - 'extRef' => null, - 'status' => 'new', - 'trackingNumber' => null, - 'sum' => $orderAmount, - 'create' => time(), - 'createBy' => $this->user->id, - ]; - - $orderId = WarehouseOrderModel::create($order); - $orderIds[] = $orderId; - - foreach ($orders as $orderItem) { - $article = WarehouseArticleModel::get($orderItem['articleId']); - - WarehouseEShopOrderItemModel::create([ - 'orderId' => $orderId, - 'articleId' => $orderItem['articleId'], - 'quantity' => $orderItem['amount'], - 'price' => $article->cheapestPurchasePrice, - ]); + if ($this->request->allDistributor === 'true') { + foreach (WarehouseDistributorModel::getAll() as $distributor) { + $data[] = [ + 'id' => $distributor->id, + 'name' => $distributor->name, + ]; + } + } elseif (!empty($article)) { + foreach (WarehouseArticleDistributorModel::getAll(['articleId' => $this->request->articleId]) as $distributor) { + $data[] = [ + 'id' => $distributor->distributorId, + 'name' => WarehouseDistributorModel::get($distributor->distributorId)->name, + 'purchasePrice' => $distributor->purchasePrice, + 'externalArticleNumber' => $distributor->externalArticleNumber, + ]; } } - self::returnJson(['success' => true, 'message' => $this->infoMessages['create'], 'ids' => $orderIds]); - } - - protected function getOrderItemsAction() { - $orderItems = WarehouseEShopOrderItemModel::getAll(['orderId' => $this->request->id]); - - // also get the article name of the order items - - foreach ($orderItems as $key => $orderItem) { - $article = WarehouseArticleModel::get($orderItem->articleId); - $orderItem->articleName = $article->title; - } - - self::returnJson($orderItems); + self::returnJson($data); } protected function beforeUpdate($postData): bool { diff --git a/application/WarehouseOrder/WarehouseOrderModel.php b/application/WarehouseOrder/WarehouseOrderModel.php index 65f5d80dc..d7ab75997 100644 --- a/application/WarehouseOrder/WarehouseOrderModel.php +++ b/application/WarehouseOrder/WarehouseOrderModel.php @@ -1,28 +1,36 @@ \ No newline at end of file diff --git a/application/WarehouseOrderItem/WarehouseOrderItem.php b/application/WarehouseOrderItem/WarehouseOrderItem.php deleted file mode 100644 index 95e2c10dc..000000000 --- a/application/WarehouseOrderItem/WarehouseOrderItem.php +++ /dev/null @@ -1,9 +0,0 @@ - ['filter' => 'select'], 'modal' => ['type' => 'select', 'items' => []]], ['key' => 'warehouseLocation', 'text' => 'Lagerort', 'required' => false, 'type' => 'varchar'], - ['key' => 'canceled', 'text' => 'Storniert', 'required' => false, 'modal' => ['visible' => false, 'type' => 'select', 'items' => [['value' => 0, 'text' => 'Nein'], ['value' => 1, 'text' => 'Ja']]], 'table' => ['filter' => 'select']], + ['key' => 'canceled', + 'text' => 'Storniert', + 'required' => false, + 'modal' => ['visible' => false, 'type' => 'select', 'items' => [['value' => 0, 'text' => 'Nein'], ['value' => 1, 'text' => 'Ja']]], + 'table' => ['filter' => 'select']], ['key' => 'note', 'text' => 'Notiz', 'required' => false, 'type' => 'textarea'], ['key' => 'actions', 'text' => 'Aktionen', @@ -50,6 +54,8 @@ class WarehouseOrderRequestController extends TTCrud { 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']], ]; + protected array $permissionCheck = ['WarehouseUser']; + protected array $additionalActions = [['key' => 'openHistory', 'title' => 'Historie', 'class' => 'fas fa-history text-primary']]; protected array $infoMessages = ['create' => 'Bestellwunsch wurde erstellt.', @@ -59,10 +65,6 @@ class WarehouseOrderRequestController extends TTCrud { protected array $additionalJSVariables = ['BASE_URL' => '/WarehouseOrderRequest', 'WAREHOUSE_ADMIN' => true]; - public function permissionCheck(): bool { - return $this->user->can(["WarehouseUser"]); - } - protected function prepareCrudConfig() { // Fill Users in createBy column $userArray = array_map(function ($user) { @@ -96,10 +98,10 @@ class WarehouseOrderRequestController extends TTCrud { } protected function customAutoCompleteWare($value) { - if (!is_numeric($value)) return [ 'id' => $value, 'title' => $value]; + if (!is_numeric($value)) return ['id' => $value, 'title' => $value]; $article = WarehouseArticleModel::get(intval($value)); - return [ 'id' => $article->id, 'title' => $article->title ]; + return ['id' => $article->id, 'title' => $article->title]; } protected function beforeUpdate($postData): bool { diff --git a/db/migrations/20250131150000_warehouse_modify_10.php b/db/migrations/20250131150000_warehouse_modify_10.php new file mode 100644 index 000000000..15ee86a77 --- /dev/null +++ b/db/migrations/20250131150000_warehouse_modify_10.php @@ -0,0 +1,57 @@ +getEnvironment() == "thetool") { + // Drop the existing tables + $this->table("WarehouseOrder")->drop()->save(); + $this->table("WarehouseOrderItem")->drop()->save(); + + // Create the new WarehouseOrder table + $table = $this->table("WarehouseOrder", ["id" => false, "primary_key" => ["id"]]); + $table->addColumn("id", "integer", ["identity" => true]) + ->addColumn("orderNumber", "string", ["null" => false, "limit" => 255]) + ->addColumn("delAddrCity", "string", ["null" => false, "limit" => 255]) + ->addColumn("delAddrEMail", "string", ["null" => false, "limit" => 255]) + ->addColumn("delAddrLine", "string", ["null" => false, "limit" => 255]) + ->addColumn("delAddrName", "string", ["null" => false, "limit" => 255]) + ->addColumn("delAddrPLZ", "string", ["null" => false, "limit" => 255]) + ->addColumn("editor", "integer", ["null" => false]) + ->addColumn("note", "text", ["null" => false]) + ->addColumn("positions", "text", ["null" => false]) + ->addColumn("create", "integer", ["null" => false]) + ->addColumn("createBy", "integer", ["null" => false]) + ->create(); + } + } + + public function down(): void { + if ($this->getEnvironment() == "thetool") { + $this->table("WarehouseOrder")->drop()->save(); + + $WarehouseOrder = $this->table("WarehouseOrder", ["signed" => true]); + $WarehouseOrder + ->addColumn('distributorId', 'integer', ['null' => false]) + ->addColumn('intRef', 'string', ['null' => true]) + ->addColumn('extRef', 'string', ['null' => true]) + ->addColumn('status', 'enum', ['values' => ['new', 'accepted', 'sent', 'done'], 'null' => false, 'default' => 'new']) + ->addColumn('sum', 'float', ['null' => true]) + ->addColumn('trackingNumber', 'string', ['null' => true]) + ->addColumn('create', 'integer', ['null' => false, 'default' => 1728541890]) + ->addColumn('createBy', 'integer', ['null' => false, 'default' => 1]) + ->create(); + + $WarehouseOrderItem = $this->table("WarehouseOrderItem", ["signed" => true]); + $WarehouseOrderItem + ->addColumn('orderId', 'integer', ['null' => false]) + ->addColumn('articleId', 'integer', ['null' => false]) + ->addColumn('quantity', 'integer', ['null' => false]) + ->addColumn('price', 'float', ['null' => false]) + ->addForeignKey('orderId', 'WarehouseOrder', 'id') + ->addForeignKey('articleId', 'WarehouseArticle', 'id') + ->create(); + } + } +} diff --git a/lib/TTCrud/TTCrud.php b/lib/TTCrud/TTCrud.php index 484b59c7c..eef311de4 100644 --- a/lib/TTCrud/TTCrud.php +++ b/lib/TTCrud/TTCrud.php @@ -25,11 +25,8 @@ class TTCrud extends mfBaseController { $this->user = $me; $this->layout()->set('me', $me); - if (method_exists($this, 'permissionCheck')) { - $allowed = $this->permissionCheck(); - if (!$allowed) { - $this->redirect("Dashboard"); - } + if (isset($this->permissionCheck) && !$me->can($this->permissionCheck)) { + $this->redirect("Dashboard"); } else if (!$me->is(["Admin"])) { $this->redirect("Dashboard"); } @@ -45,42 +42,36 @@ class TTCrud extends mfBaseController { * @return array */ protected function getCheckArray(): array { - $checkArray = []; - - foreach ($this->columns as $column) { - $checkArray[$column['key']] = ['required' => $column['required'] ?? false, - 'required_length' => $column['required_length'] ?? 0, - 'title' => $column['text'] ?? $column['key'], - 'regex' => $column['regex'] ?? false]; - } - - return $checkArray; + return array_map(fn($column) => [ + 'required' => $column['required'] ?? false, + 'required_length' => $column['required_length'] ?? 0, + 'title' => $column['text'] ?? $column['key'], + 'regex' => $column['regex'] ?? false + ], $this->columns); } + protected function indexAction() { $this->layout()->set('additionalJS', ['js/pages/WarehouseHistory/WarehouseHistoryModal.js']); - $customJsFile = defined('BASEDIR') ? BASEDIR . "/public/js/pages/{$this->mod}/{$this->mod}.js" : null; + $pageName = (defined('BASEDIR') && file_exists(BASEDIR . "/public/js/pages/{$this->mod}/{$this->mod}.js")) + ? $this->mod + : "DefaultCrudView"; - if ($customJsFile && file_exists($customJsFile)) { - $pageName = $this->mod; - } else { - $pageName = "DefaultCrudView"; - } + $JS_VARIABLES = [ + "CRUD_CONFIG" => $this->getCrudConfig(), + "CREATE_URL" => $this::getUrl("{$this->mod}/create"), + "TABLE_URL" => $this::getUrl("{$this->mod}/get"), + "UPDATE_URL" => $this::getUrl("{$this->mod}/update"), + "DELETE_URL" => $this::getUrl("{$this->mod}/delete"), + "USER_ID" => $this->user->id + ]; - $JS_VARIABLES = ["CRUD_CONFIG" => $this->getCrudConfig(), - "CREATE_URL" => $this::getUrl($this->mod . "/create"), - "TABLE_URL" => $this::getUrl($this->mod . "/get"), - "UPDATE_URL" => $this::getUrl($this->mod . "/update"), - "DELETE_URL" => $this::getUrl($this->mod . "/delete"), - "USER_ID" => $this->user->id]; - - if ($this->additionalJSVariables && is_array($this->additionalJSVariables)) { - $JS_VARIABLES = array_merge($JS_VARIABLES, $this->additionalJSVariables); - } + if (!empty($this->additionalJSVariables) && is_array($this->additionalJSVariables)) $JS_VARIABLES = array_merge($JS_VARIABLES, $this->additionalJSVariables); Helper::renderVue($this, $pageName, $this->headerTitle, $JS_VARIABLES); } + /** * Returns the configuration for the CRUD component for the Vue component. * @return array @@ -90,6 +81,13 @@ class TTCrud extends mfBaseController { $this->prepareCrudConfig(); } + $column = array_search('createBy', array_column($this->columns, 'key')); + if ($column !== false) { + $this->columns[$column]['modal']['items'] = array_map(function ($user) { + return ['value' => intval($user->id), 'text' => $user->name]; + }, UserModel::search(['employee' => true])); + } + $columns = array_map(function ($column) { if (isset($column['type']) && (!isset($column['modal']) || !isset($column['modal']['type']))) { @@ -186,7 +184,6 @@ class TTCrud extends mfBaseController { } protected function createAction() { - // if this->model has property createBy, set it to the current user id and create to current epoch time if (property_exists($this->model, 'createBy')) { $this->postData['createBy'] = $this->user->id; } @@ -194,12 +191,12 @@ class TTCrud extends mfBaseController { $this->postData['create'] = time(); } - Helper::validateArray($this->postData, $this->checkArray); - if (method_exists($this, 'beforeCreate') && !$this->beforeCreate($this->postData)) { self::returnJson(['success' => false, 'message' => 'Ein Fehler ist aufgetreten.']); } + Helper::validateArray($this->postData, $this->checkArray); + $id = $this->model::create($this->postData); if (method_exists($this, 'afterCreate')) { @@ -212,12 +209,12 @@ class TTCrud extends mfBaseController { } protected function updateAction() { -// if (property_exists($this->model, 'createBy') && !isset($this->postData['createBy'])) { -// $this->postData['createBy'] = $this->user->id; -// } -// if (property_exists($this->model, 'create') && !isset($this->postData['create'])) { -// $this->postData['create'] = time(); -// } + if (property_exists($this->model, 'create') && isset($this->postData['create'])) { + $this->postData['create'] = $this->model::get($this->postData['id'])->create; + } + if (property_exists($this->model, 'createBy') && isset($this->postData['createBy'])) { + $this->postData['createBy'] = $this->model::get($this->postData['id'])->createBy; + } Helper::validateArray($this->postData, array_merge($this->checkArray, ['id' => ['required' => true]])); diff --git a/public/bundler.php b/public/bundler.php index 721fb6431..9bd9e99eb 100644 --- a/public/bundler.php +++ b/public/bundler.php @@ -47,6 +47,7 @@ $jsFiles = [ "plugins/vue/tt-components/tt-number-range.js", "plugins/vue/tt-components/tt-checkbox.js", "plugins/vue/tt-components/tt-textarea.js", + "plugins/vue/tt-components/tt-position-manager.js", ]; diff --git a/public/js/pages/WarehouseOrder/WarehouseOrder.css b/public/js/pages/WarehouseOrder/WarehouseOrder.css new file mode 100644 index 000000000..878a34a77 --- /dev/null +++ b/public/js/pages/WarehouseOrder/WarehouseOrder.css @@ -0,0 +1,27 @@ +.warehouse-order-modal-positions-entry-container { + display: grid; + grid-template-columns: 2fr 1fr 1fr 0.5fr 1fr 1fr 0.5fr; + grid-gap: 10px; +} + +.warehouse-order-modal-positions-entry-actions { + display: flex; + flex-direction: column; + justify-content: center; + padding-top: 13px; +} + +@media (min-width: 992px) { + .modal-lg, .modal-xl { + /*max width either 90% or 1120px*/ + max-width: min(90vw, 1120px) !important; + } +} + +@media (max-width: 992px) { + .warehouse-order-modal-positions-entry-container { + display: grid; + grid-template-columns: 1fr 1fr !important; + grid-gap: 10px; + } +} \ No newline at end of file diff --git a/public/js/pages/WarehouseOrder/WarehouseOrder.js b/public/js/pages/WarehouseOrder/WarehouseOrder.js index ab63807d5..fcd97373a 100644 --- a/public/js/pages/WarehouseOrder/WarehouseOrder.js +++ b/public/js/pages/WarehouseOrder/WarehouseOrder.js @@ -1,70 +1,169 @@ -// noinspection JSUnusedLocalSymbols +Vue.component('warehouse-order-modal', { + props: { + id: {type: [String, Number], required: true}, + mode: {type: String, default: 'sign'} + }, + template: ` + +
+

Bestelldetails

+ + +
+

Positionen

+ + +
+

Lieferadresse

+
+ + + + + +
+ +
+ +
+
+ `, + + data() { + return { + window: window, + positionsConfig: { + customOrdering: 'distributorId', + fields: { + article: { + type: 'autocomplete', + label: 'Artikel', + apiUrl: '/WarehouseArticle/autoComplete', + customFieldReference: 'WarehouseArticle', + }, + distributorId: {type: 'select', label: 'Lieferant', options: [], customFieldReference: 'WarehouseDistributor'}, + distributorArticleNumber: {type: 'input', label: 'Lieferant Art-Nr.'}, + amount: {type: 'input', label: 'Menge', inputType: 'number'}, + buyPrice: {type: 'input', label: 'Einkaufspreis', inputType: 'number'}, + verwendung: {type: 'input', label: 'Verwendung'}, + }, + validateForm: (formData) => { + const fields = [ + {key: 'amount', message: 'Bitte füllen Sie die Menge aus'}, + {key: 'distributorId', message: 'Bitte füllen Sie den Lieferanten aus'}, + {key: 'article', message: 'Bitte füllen Sie den Artikel aus'}, + {key: 'buyPrice', message: 'Bitte füllen Sie den Einkaufspreis aus'} + ]; + + for (const field of fields) { + if (!formData[field.key]) { + window.notify('error', field.message); + return false; + } + } + + return true; + }, + }, + order: { + delAddrName: 'XINON GmbH', + delAddrLine: 'Fladnitz im Raabtal 150', + delAddrPLZ: '8322', + delAddrCity: 'Studenzen', + delAddrEMail: 'einkauf@xinon.at', + note: '', + editor: window.TT_CONFIG['USER_ID'], + positions: [], + } + } + }, + async mounted() { + if (this.id !== 'create') { + const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getById`, {params: {id: this.id}}); + response.data.positions = JSON.parse(response.data.positions); + this.order = response.data; + } + }, + methods: { + async submit() { + if (this.order.positions.length === 0) return window.notify('error', 'Bitte fügen Sie mindestens eine Position hinzu.'); + + if (this.id === 'create') { + const distributorIds = [...new Set(this.order.positions.map(position => position.distributorId))]; + + for (const distributorId of distributorIds) { + const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/create`, { + ...this.order, + distributorId, + positions: this.order.positions.filter(position => position.distributorId === distributorId) + } + ); + if (response.data.success) window.notify('success', response.data.message ?? 'Bestellung erfolgreich erstellt'); + else window.notify('error', + response.data.errors ? Object.values(response.data.errors).join('
') : response.data.message || 'Ein Fehler ist aufgetreten'); + } + } else { + const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/update`, this.order); + if (response.data.success) window.notify('success', response.data.message ?? 'Bestellung erfolgreich aktualisiert'); + else window.notify('error', + response.data.errors ? Object.values(response.data.errors).join('
') : response.data.message || 'Ein Fehler ist aufgetreten'); + } + this.$emit('close'); + + }, + async fetchDistributors(article) { + const url = `${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getArticleDistributorData`; + const params = typeof article === 'string' ? {allDistributor: true} : {articleId: article}; + + const response = await axios.get(url, {params}); + this.positionsConfig.fields.distributorId.options = response.data.map(distributor => ({ + value: distributor.id, + text: distributor.name, + externalArticleNumber: distributor.externalArticleNumber || null, + purchasePrice: distributor.purchasePrice || null, + })); + }, + async fetchDistributorData(distributorId) { + if (distributorId && typeof this.$refs.positionsManager.formData.article === 'number') { + const distributor = this.positionsConfig.fields.distributorId.options.find(distributor => parseInt(distributor.value) === + parseInt(distributorId)); + this.$refs.positionsManager.updateField('distributorArticleNumber', distributor.externalArticleNumber); + this.$refs.positionsManager.updateField('buyPrice', distributor.purchasePrice); + } + }, + }, +}); + + Vue.component('warehouse-order', { //language=Vue template: ` - - - - - - - - + + + + - `, data() { return { - window: window, historyModal: false, historyModalId: null, observer: null, orderLazyLoad: {}, - } - }, mounted() { - this.observer = new MutationObserver((mutations) => { - const lazyLoadingElements = document.querySelectorAll('.lazy-loading'); - console.log(lazyLoadingElements); - - // check row id and check if it is already defined in orderLazyLoad else alert('loading') - // if it is defined do nothing - - for (const element of lazyLoadingElements) { - if (element.dataset.rowId in this.orderLazyLoad) { - continue; - } - this.loadOrder(element.dataset.rowId); - } - - }) - this.observer.observe(document.querySelector('.tt-table-container'), {childList: true, subtree: true,}); - }, methods: { - async loadOrder(rowId) { - this.orderLazyLoad[rowId] = true; - // use BASE_PATH . /WarehouseOrder/getOrderItems?id= + rowId - const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getOrderItems?id=${rowId}`); - console.log(response.data); - this.orderLazyLoad[rowId] = response.data; - - // force re-render of the table - this.$refs.table.$forceUpdate(); - + window: window, + orderModalId: null, } - }, beforeDestroy() { - this.observer.disconnect(); - } -}) + }, +}) \ No newline at end of file diff --git a/public/plugins/vue/tt-components/css/tt-position-manager.css b/public/plugins/vue/tt-components/css/tt-position-manager.css new file mode 100644 index 000000000..ed770af35 --- /dev/null +++ b/public/plugins/vue/tt-components/css/tt-position-manager.css @@ -0,0 +1,64 @@ +.positions-manager { + padding: 1rem; + font-family: sans-serif; +} + +.positions-manager .form-group { + margin-bottom: 0; +} + +.positions-manager .form-container { + display: flex; + align-items: center; /* Vertically center */ + justify-content: flex-start; + gap: 1rem; + padding-bottom: 1rem; + border-bottom: 1px solid #ddd; +} + +.positions-manager .form-container .button-wrapper { + align-self: flex-end; /* Align button container at the bottom */ +} + +.positions-manager .form-container [class*="tt-input"], +.positions-manager .form-container [class*="tt-checkbox"] { + flex: 1 1 200px; /* Flexible width with a minimum of 200px */ + min-width: 200px; +} + +.positions-manager .form-container .btn { + align-self: flex-end; /* Align button at the bottom of the form area */ +} + +.positions-manager table { + width: 100%; + margin-top: 1rem; + border-collapse: collapse; +} + +.positions-manager table thead th { + background-color: #f4f4f4; + border-bottom: 2px solid #ddd; + padding: 8px; + text-align: left; +} + +.positions-manager table th, +.positions-manager table td { + padding: 8px; + vertical-align: middle; /* Vertically center text */ +} + +.positions-manager table tbody tr:nth-child(even) { + background-color: #f9f9f9; /* Alternate row color */ +} + +.positions-manager table .btn { + margin-right: 0.5rem; +} + +.positions-manager .form-container .btn-sm, +.positions-manager table .btn.btn-sm { + padding: 0.3rem 0.6rem; /* Small button padding */ + font-size: 0.875rem; +} diff --git a/public/plugins/vue/tt-components/tt-autocomplete.js b/public/plugins/vue/tt-components/tt-autocomplete.js index fc8d9817d..fca5d9bda 100644 --- a/public/plugins/vue/tt-components/tt-autocomplete.js +++ b/public/plugins/vue/tt-components/tt-autocomplete.js @@ -42,7 +42,7 @@ Vue.component('tt-autocomplete', { Einträge werden geladen... -