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: `
-
-
-
- {{ window.moment(row.create * 1000).format('DD.MM.YYYY HH:mm:ss') }}
-
-
-
- {{ row.sum.toFixed(2) }} €
-
-
-
-
-
-
-
- -
- {{ item.quantity }}x {{ item.articleName }} - {{ item.price.toFixed(2) }} €
-
-
-
-
-
-
+
+
+
+ Work in Progress
-
`, 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...
-
+
item.value === this.value);
this.displayValue = selectedItem ? selectedItem.text : '';
} else {
- this.$emit('input', '');
+ if (this.returnText === false && !(typeof this.value === 'undefined' || this.value === '')) this.$emit('input', '');
this.displayValue = this.displayValue.replace(this.oldDisplayValue, '');
}
},
onInput(event) {
this.displayValue = event.target.value;
- this.$emit('input', '');
- this.$emit('input', this.returnText ? this.displayValue : '');
+ if (this.returnText) this.$emit('input', this.displayValue);
this.fetchSuggestions();
}, onFocus() {
this.showSuggestions = true;
@@ -136,10 +141,8 @@ Vue.component('tt-autocomplete', {
this.showSuggestions = false;
}, 200);
}, fetchSuggestions() {
- if (this.displayValue.length < 3) {
- this.$set(this, 'displayingItems', []);
- return this.displayingItems = [];
- }
+ this.$set(this, 'displayingItems', []);
+ if (this.displayValue.length < 3) return;
if (!this.apiUrl) {
diff --git a/public/plugins/vue/tt-components/tt-position-manager.js b/public/plugins/vue/tt-components/tt-position-manager.js
new file mode 100644
index 000000000..182f5b04e
--- /dev/null
+++ b/public/plugins/vue/tt-components/tt-position-manager.js
@@ -0,0 +1,163 @@
+Vue.component('tt-positions-manager', {
+ props: {
+ value: {type: Array, required: false},
+ config: {type: Object, required: true},
+ },
+ data() {
+ return {
+ window: window,
+ positions: this.value,
+ formData: {},
+ selectedIndex: null,
+ resolvingFields: {},
+ }
+ },
+ template: `
+
+
+
+
+
+
+ | {{ field.label }} |
+ Actions |
+
+
+
+
+ |
+
+
+
+
+ {{ resolvingFields[index + key] }}
+ {{ formatFieldValue(position[key], field) }}
+ |
+
+
+
+ |
+
+
+
+
+ `,
+ methods: {
+ updateField(key, value) {
+ this.$set(this.formData, key, value);
+ },
+ async saveEntry() {
+ if (this.config.validateForm && !await this.config.validateForm(this.formData)) return;
+
+ if (this.selectedIndex === null) this.positions.push(this.formData);
+ else this.$set(this.positions, this.selectedIndex, this.formData);
+
+ if (this.config.customOrdering) {
+ this.positions.sort((a, b) => a[this.config.customOrdering] - b[this.config.customOrdering]);
+ }
+
+ this.$emit('input', this.positions);
+ this.resetForm();
+ },
+ editEntry(index) {
+ this.selectedIndex = index;
+ this.formData = {...this.positions[index]};
+ },
+ deleteEntry(index) {
+ this.positions.splice(index, 1);
+ this.$emit('input', this.positions);
+ },
+ resetForm() {
+ this.formData = {};
+ this.selectedIndex = null;
+ },
+ formatFieldValue(value, field) {
+ if (field.formatter) return field.formatter(value);
+ return value;
+ },
+ async resolveFields() {
+ for (let i = 0; i < this.positions.length; i++) {
+ for (let key in this.config.fields) {
+ if (this.config.fields[key].customFieldResolver) {
+ this.$set(this.resolvingFields, i + key, true);
+ const textValue = await this.config.fields[key].customFieldResolver(this.positions[i][key]);
+ this.$set(this.resolvingFields, i + key, textValue);
+ } else if (this.config.fields[key].customFieldReference) {
+ this.$set(this.resolvingFields, i + key, true);
+ if (this.config.fields[key].customFieldReference) {
+ const entry = await axios.get(window.TT_CONFIG['BASE_PATH'] +
+ '/' +
+ this.config.fields[key].customFieldReference +
+ '/getById?id=' +
+ this.positions[i][key]);
+ const textValue = entry.data.name ?? entry.data.title ?? entry.data.text ?? '[E] Key not found';
+ console.log(textValue);
+ this.$set(this.resolvingFields, i + key, textValue);
+ } else this.$set(this.resolvingFields, i + key, '');
+ }
+ }
+ }
+ }
+ },
+ created() {
+ if (this.config.customMethods) Object.assign(this, this.config.customMethods);
+ },
+ watch: {
+ positions: {
+ handler() {
+ this.resolveFields().then();
+ },
+ deep: true
+ },
+ value: {
+ handler() {
+ this.positions = this.value;
+ },
+ deep: true
+ }
+ }
+});
\ No newline at end of file