Updated WarehouseOrder and WarehouseOrderRequest
This commit is contained in:
@@ -73,4 +73,19 @@ class FileController extends mfBaseController {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getByIdAction() {
|
||||
$file = new File($this->request->id);
|
||||
|
||||
if (!$file->id) {
|
||||
http_response_code(404);
|
||||
self::returnJson(["error" => "File not found"]);
|
||||
return;
|
||||
}
|
||||
|
||||
self::returnJson([
|
||||
"id" => $file->id,
|
||||
"filename" => $file->orig_filename
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -442,4 +442,10 @@ class UserController extends mfBaseController
|
||||
|
||||
return ["valid_to" => null];
|
||||
}
|
||||
|
||||
protected function getByIdAction() {
|
||||
$id = $this->request->id;
|
||||
$user = new User($id);
|
||||
$this->returnJson($user->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
9
application/WarehouseLog/WarehouseLog.php
Normal file
9
application/WarehouseLog/WarehouseLog.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @property mixed|null $name
|
||||
*/
|
||||
class WarehouseLog extends mfBaseModel
|
||||
{
|
||||
|
||||
}
|
||||
12
application/WarehouseLog/WarehouseLogModel.php
Normal file
12
application/WarehouseLog/WarehouseLogModel.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
class WarehouseLogModel extends TTCrudBaseModel {
|
||||
public int $id;
|
||||
public string $table;
|
||||
public int $rowId;
|
||||
public string $type;
|
||||
public ?string $fileIds;
|
||||
public string $message;
|
||||
public int $create;
|
||||
public int $createBy;
|
||||
}
|
||||
@@ -18,7 +18,15 @@ class WarehouseOrderController extends TTCrud {
|
||||
['key' => 'editor', 'text' => 'Bearbeiter', 'required' => true, 'modal' => ['type' => 'select'], 'table' => ['filter' => 'select']],
|
||||
['key' => 'note', 'text' => 'Notiz', 'required' => false, 'modal' => false, 'table' => false],
|
||||
['key' => 'sum', 'text' => 'Summe', 'required' => false, 'modal' => false, 'table' => ['class' => 'text-right']],
|
||||
['key' => 'status', 'text' => 'Status', 'required' => false, 'modal' => ['type' => 'select', 'items' => []], 'table' => ['filter' => 'select']],
|
||||
['key' => 'status', 'text' => 'Status', 'required' => false, 'modal' => ['type' => 'select', 'items' => [
|
||||
['value' => 'new', 'text' => 'Neu'],
|
||||
['value' => 'accepted', 'text' => 'Akzeptiert'],
|
||||
['value' => 'ordered', 'text' => 'Bestellt'],
|
||||
['value' => 'sent', 'text' => 'Versendet'],
|
||||
['value' => 'partiallyDelivered', 'text' => 'Teilweise geliefert'],
|
||||
['value' => 'fullyDelivered', 'text' => 'Geliefert'],
|
||||
['value' => 'cancelled', 'text' => 'Storniert'],
|
||||
]], 'table' => ['filter' => 'select']],
|
||||
['key' => 'positions', 'text' => 'Positionen', 'required' => true, 'modal' => false, 'table' => false],
|
||||
['key' => 'extReference', 'text' => 'Externe Referenz', 'required' => false, 'modal' => false],
|
||||
['key' => 'createBy', 'text' => 'Erstellt von', 'required' => true, 'modal' => ['type' => 'select'], 'table' => ['filter' => 'select']],
|
||||
@@ -40,21 +48,17 @@ class WarehouseOrderController extends TTCrud {
|
||||
return ['value' => intval($user->id), 'text' => $user->name];
|
||||
}, UserModel::search(['employee' => true]));
|
||||
|
||||
$statusIndex = array_search('status', array_column($this->columns, 'key'));
|
||||
$this->columns[$statusIndex]['modal']['items'] = [
|
||||
['value' => 'new', 'text' => 'Neu'],
|
||||
['value' => 'accepted', 'text' => 'Akzeptiert'],
|
||||
['value' => 'ordered', 'text' => 'Bestellt'],
|
||||
['value' => 'sent', 'text' => 'Versendet'],
|
||||
['value' => 'partiallyDelivered', 'text' => 'Teilweise geliefert'],
|
||||
['value' => 'fullyDelivered', 'text' => 'Geliefert'],
|
||||
['value' => 'cancelled', 'text' => 'Storniert'],
|
||||
];
|
||||
|
||||
$distributorIndex = array_search('distributorId', array_column($this->columns, 'key'));
|
||||
$this->columns[$distributorIndex]['modal']['items'] = array_map(function ($distributor) {
|
||||
return ['value' => intval($distributor->id), 'text' => $distributor->name];
|
||||
}, WarehouseDistributorModel::getAll());
|
||||
|
||||
$this->additionalActions[] = [
|
||||
'key' => 'changeStatus',
|
||||
'title' => 'Status ändern',
|
||||
'class' => 'fas fa-exchange-alt',
|
||||
'color' => 'warning',
|
||||
];
|
||||
}
|
||||
|
||||
protected function beforeCreate(): bool {
|
||||
@@ -116,8 +120,7 @@ class WarehouseOrderController extends TTCrud {
|
||||
|
||||
$headerHtml = file_get_contents(BASEDIR . "/Layout/default/WarehouseOrder/PDF_HEADER.html");
|
||||
$headerHtml = str_replace("{{ basedir }}", BASEDIR, $headerHtml);
|
||||
$headerHtml = str_replace("{{ externalReference }}", count($order['extReference']) > 0 ? "<strong>Ext. Ref.:</strong> ". $order['extReference'] : "", $headerHtml);
|
||||
|
||||
$headerHtml = str_replace("{{ externalReference }}", !empty($order['extReference']) && count($order['extReference']) > 0 ? "<strong>Ext. Ref.:</strong> ". $order['extReference'] : "", $headerHtml);
|
||||
$headerHtml = str_replace("{{ addressLine_header }}", $shouldGenerateEnglisch ? "Supplier" : "Lieferant", $headerHtml);
|
||||
$headerHtml = str_replace("{{ addressLine_1 }}", WarehouseDistributorModel::get($distributorId)->name, $headerHtml);
|
||||
$headerHtml = str_replace("{{ addressLine_2 }}", WarehouseDistributorModel::get($distributorId)->address, $headerHtml);
|
||||
@@ -165,5 +168,110 @@ class WarehouseOrderController extends TTCrud {
|
||||
|
||||
}
|
||||
|
||||
protected function getLogAction() {
|
||||
$orderId = $this->request->orderId;
|
||||
if (empty($orderId)) {
|
||||
self::returnJson(['error' => 'Order ID is required']);
|
||||
return;
|
||||
}
|
||||
|
||||
$logs = WarehouseLogModel::getAll(['table' => 'WarehouseOrder','rowId' => $orderId], ['timestamp' => 'DESC']);
|
||||
self::returnJson($logs);
|
||||
}
|
||||
|
||||
protected function createNewLogAction() {
|
||||
$postData = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (empty($postData['orderId']) || empty($postData['status'])) {
|
||||
self::returnJson(['error' => 'Order ID and Status are required']);
|
||||
return;
|
||||
}
|
||||
|
||||
$log = [
|
||||
"table" => "WarehouseOrder",
|
||||
"rowId" => intval($postData['orderId']),
|
||||
"type" => $postData['status'] === 'noChanges' ? 'noChanges' : 'statusChange',
|
||||
"fileIds" => $postData['fileIds'] ?? null,
|
||||
"message" => $postData['note'] ?? null,
|
||||
"createBy" => intval($this->user->id),
|
||||
"create" => time()
|
||||
];
|
||||
|
||||
try {
|
||||
$order = WarehouseOrderModel::get($log['orderId']);
|
||||
if ($postData['status'] !== 'noChanges') {
|
||||
$oldStatusText = array_values(array_filter($this->columns, fn($c) => $c['key'] === 'status'))[0]['modal']['items'][array_search($order->status, array_column(array_values(array_filter($this->columns, fn($c) => $c['key'] === 'status'))[0]['modal']['items'], 'value'))]['text'];
|
||||
$newStatusText = array_values(array_filter($this->columns, fn($c) => $c['key'] === 'status'))[0]['modal']['items'][array_search($postData['status'], array_column(array_values(array_filter($this->columns, fn($c) => $c['key'] === 'status'))[0]['modal']['items'], 'value'))]['text'];
|
||||
$log['message'] = 'Status wurde geändert von ' . $oldStatusText . ' auf ' . $newStatusText . ($log['message'] ? ': ' . $log['message'] : '');
|
||||
$order->status = $postData['status'];
|
||||
$order = (array) $order;
|
||||
WarehouseOrderModel::update($order);
|
||||
}
|
||||
|
||||
WarehouseLogModel::create($log);
|
||||
self::returnJson(['success' => 'Log entry created']);
|
||||
} catch (Exception $e) {
|
||||
self::returnJson(['error' => 'Error creating log entry']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function uploadFileAction() {
|
||||
if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
||||
self::returnJson(['error' => 'No file uploaded or upload error occurred']);
|
||||
return;
|
||||
}
|
||||
|
||||
$_FILES = ['WarehouseOrder' => $_FILES['file']];
|
||||
|
||||
try {
|
||||
$file = mfUpload::handleFormUpload("WarehouseOrder", false, "/WarehouseOrder");
|
||||
|
||||
// Return the file ID
|
||||
self::returnJson(['success' => true, 'fileId' => $file->id]);
|
||||
} catch (Exception $ex) {
|
||||
self::returnJson(['error' => 'Error uploading file: ' . $ex->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function getLogByIdAction() {
|
||||
$orderId = $this->request->id;
|
||||
if (empty($orderId)) {
|
||||
self::returnJson(['error' => 'Order ID is required']);
|
||||
return;
|
||||
}
|
||||
|
||||
$log = WarehouseLogModel::getAll(['table' => 'WarehouseOrder', 'rowId' => $orderId]);
|
||||
self::returnJson($log);
|
||||
}
|
||||
|
||||
|
||||
protected function afterUpdate($postData) {
|
||||
$this->updateOrderRequestLinkedOrderIds($postData['id']);
|
||||
}
|
||||
|
||||
protected function afterCreate($postData) {
|
||||
$this->updateOrderRequestLinkedOrderIds($postData['id']);
|
||||
}
|
||||
protected function updateOrderRequestLinkedOrderIds($id) {
|
||||
$order = (array) WarehouseOrderModel::get($id);
|
||||
foreach (json_decode($order['positions'], true) as $position) {
|
||||
if (!empty($position['linkedOrderRequestId'])) {
|
||||
$warehouseOrderRequest = (array) WarehouseOrderRequestModel::get($position['linkedOrderRequestId']);
|
||||
if (is_null($warehouseOrderRequest['linkedOrderIds'])) {
|
||||
$warehouseOrderRequest['linkedOrderIds'] = [$id];
|
||||
WarehouseOrderRequestModel::update($warehouseOrderRequest);
|
||||
} else {
|
||||
if (!in_array($id, $warehouseOrderRequest['linkedOrderIds'])) {
|
||||
$warehouseOrderRequest['linkedOrderIds'][] = $id;
|
||||
WarehouseOrderRequestModel::update($warehouseOrderRequest);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
//TODO: migration for extReference
|
||||
/**
|
||||
* Class WarehouseOrderModel
|
||||
*
|
||||
@@ -25,6 +24,7 @@ class WarehouseOrderModel extends TTCrudBaseModel {
|
||||
public string $orderNumber;
|
||||
public ?string $extReference;
|
||||
public int $distributorId;
|
||||
public ?string $status;
|
||||
public string $delAddrCity;
|
||||
public string $delAddrEMail;
|
||||
public string $delAddrLine;
|
||||
|
||||
@@ -1,107 +1,56 @@
|
||||
<?php
|
||||
//TODO: enable switching distributors in the order preview
|
||||
<?php /** @noinspection PhpVoidFunctionResultUsedInspection */
|
||||
|
||||
class WarehouseOrderRequestController extends TTCrud {
|
||||
protected string $headerTitle = 'Bestellwünsche';
|
||||
protected string $createText = 'Bestellwunsch erstellen';
|
||||
protected string $createText = 'Neuer Bestellwunsch';
|
||||
protected string $singleText = 'Bestellwunsch';
|
||||
|
||||
//@formatter:off
|
||||
protected array $columns = [
|
||||
['key' => 'id', 'text' => 'ID', 'modal' => false, 'table' => false],
|
||||
['key' => 'ware',
|
||||
'text' => 'Ware',
|
||||
'required' => true,
|
||||
['key' => 'purpose', 'text' => 'Verwendungszweck', 'required' => true],
|
||||
['key' => 'positions', 'text' => 'Positionen', 'required' => true, 'modal' => ['type' => 'positions-manager', 'config' => [
|
||||
'header' => 'Positionen',
|
||||
'fields' => [
|
||||
'articleId' => [
|
||||
'apiUrl' => '/WarehouseArticle/autoComplete',
|
||||
'type' => 'autocomplete',
|
||||
'table' => ['class' => 'text-nowrap', 'filter' => 'autocomplete'],
|
||||
'modal' => [
|
||||
'apiUrl' => 'WarehouseArticle/autocomplete',
|
||||
'type' => 'autocomplete',
|
||||
'returnText' => true]],
|
||||
['key' => 'anzahl', 'text' => 'Anzahl', 'required' => true, 'type' => 'number'],
|
||||
['key' => 'verwendungszweck', 'text' => 'Verwendungszweck', 'required' => true],
|
||||
['key' => 'create', 'text' => 'Beauftragt am', 'required' => true, 'modal' => false, 'table' => ['filter' => 'datepicker']],
|
||||
['key' => 'createBy',
|
||||
'text' => 'Beauftragt von',
|
||||
'required' => true,
|
||||
'table' => ['filter' => 'select'],
|
||||
'modal' => ['visible' => false, 'type' => 'select', 'items' => []]],
|
||||
['key' => 'distributorId',
|
||||
'text' => 'Lieferant',
|
||||
'required' => false,
|
||||
'type' => 'autocomplete',
|
||||
'table' => ['class' => 'text-nowrap', 'filter' => 'autocomplete'],
|
||||
'modal' => [
|
||||
'apiUrl' => 'WarehouseDistributor/autocomplete',
|
||||
'type' => 'autocomplete']],
|
||||
['key' => 'order', 'text' => 'Bestellt am', 'required' => false, 'type' => 'datepicker', 'table' => ['filter' => 'datepicker']],
|
||||
['key' => 'orderBy', 'text' => 'Bestellt von', 'required' => false, 'table' => ['filter' => 'select'], 'modal' => ['type' => 'select', 'items' => []]],
|
||||
['key' => 'takeOver', 'text' => 'Übernommen am', 'required' => false, 'type' => 'datepicker', 'table' => ['filter' => 'datepicker']],
|
||||
['key' => 'takeOverBy',
|
||||
'text' => 'Übernommen von',
|
||||
'required' => false,
|
||||
'table' => ['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' => 'note', 'text' => 'Notiz', 'required' => false, 'type' => 'textarea'],
|
||||
['key' => 'actions',
|
||||
'text' => 'Aktionen',
|
||||
'required' => false,
|
||||
'modal' => false,
|
||||
'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']],
|
||||
'emitDisplayValue' => true,
|
||||
'customFieldReference' => 'WarehouseArticle',
|
||||
'label' => 'Artikel',
|
||||
],
|
||||
'amount' => ['type' => 'input', 'label' => 'Menge', 'inputType' => 'number'],
|
||||
'purpose' => ['type' => 'input', 'label' => 'Zweck'],
|
||||
],
|
||||
'validateFormOptions' => [
|
||||
['key' => 'articleId', 'message' => 'Bitte füllen Sie den Artikel aus'],
|
||||
['key' => 'amount', 'message' => 'Bitte füllen Sie die Menge aus'],
|
||||
['key' => 'purpose', 'message' => 'Bitte füllen Sie den Zweck aus'],
|
||||
],
|
||||
]], 'table' => false],
|
||||
['key' => 'linkedOrderIds', 'text' => 'Verlinkte Bestellung', 'modal' => false],
|
||||
['key' => 'createBy', 'text' => 'Erstellt von', 'required' => true, 'modal' => ['visible' => false, 'type' => 'select'], 'table' => ['filter' => 'select']],
|
||||
['key' => 'create', 'text' => 'Erstellt am', 'required' => true, 'modal' => false],
|
||||
['key' => 'cancelled', 'text' => 'Storniert', 'modal' => ['visible' => false, 'type' => 'icon-select', 'items' => [
|
||||
['value' => 0, 'text' => 'Bestellwunsch nicht storniert', 'icon' => 'fa-regular fa-circle-check text-success'],
|
||||
['value' => 1, 'text' => 'Bestellwunsch storniert', 'icon' => 'fa-regular fa-circle-xmark text-danger']]], 'table' => ['filter' => 'iconSelect']
|
||||
],
|
||||
['key' => 'actions', 'text' => 'Aktionen', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']],
|
||||
];
|
||||
//@formatter:on
|
||||
|
||||
protected array $permissionCheck = ['WarehouseUser'];
|
||||
|
||||
protected array $additionalActions = [['key' => 'openHistory', 'title' => 'Historie', 'class' => 'fas fa-history text-primary']];
|
||||
|
||||
protected array $infoMessages = ['create' => 'Bestellwunsch wurde erstellt.',
|
||||
'update' => 'Bestellwunsch wurde aktualisiert',
|
||||
'delete' => 'Bestellwunsch wurde gelöscht',
|
||||
'noChanges' => 'Keine Änderungen',];
|
||||
|
||||
protected array $additionalJSVariables = ['BASE_URL' => '/WarehouseOrderRequest', 'WAREHOUSE_ADMIN' => true];
|
||||
protected array $additionalActions = [
|
||||
['key' => 'openHistory', 'title' => 'Historie', 'class' => 'fas fa-history text-primary'],
|
||||
['key' => 'createLog', 'title' => 'Log-Eintrag erstellen', 'class' => 'fas fa-plus text-primary'],
|
||||
];
|
||||
|
||||
protected function prepareCrudConfig() {
|
||||
// Fill Users in createBy column
|
||||
$userArray = array_map(function ($user) {
|
||||
return ['value' => intval($user->id), 'text' => $user->name];
|
||||
}, UserModel::search(['employee' => true]));
|
||||
$createByColumn = array_search('createBy', array_column($this->columns, 'key'));
|
||||
$this->columns[$createByColumn]['modal']['items'] = $userArray;
|
||||
$orderByColumn = array_search('orderBy', array_column($this->columns, 'key'));
|
||||
$this->columns[$orderByColumn]['modal']['items'] = $userArray;
|
||||
$takeOverByColumn = array_search('takeOverBy', array_column($this->columns, 'key'));
|
||||
$this->columns[$takeOverByColumn]['modal']['items'] = $userArray;
|
||||
|
||||
// if this user can WarehouseAdmin is false then set modal false to warehouselocation, takeOverBy, takeOver, orderBy, order
|
||||
if (!$this->user->can(["WarehouseAdmin"])) {
|
||||
$warehouselocationColumn = array_search('warehouseLocation', array_column($this->columns, 'key'));
|
||||
$this->columns[$warehouselocationColumn]['modal']['visible'] = false;
|
||||
$takeOverByColumn = array_search('takeOverBy', array_column($this->columns, 'key'));
|
||||
$this->columns[$takeOverByColumn]['modal']['visible'] = false;
|
||||
$takeOverColumn = array_search('takeOver', array_column($this->columns, 'key'));
|
||||
$this->columns[$takeOverColumn]['modal']['visible'] = false;
|
||||
$orderByColumn = array_search('orderBy', array_column($this->columns, 'key'));
|
||||
$this->columns[$orderByColumn]['modal']['visible'] = false;
|
||||
$orderColumn = array_search('order', array_column($this->columns, 'key'));
|
||||
$this->columns[$orderColumn]['modal']['visible'] = false;
|
||||
}
|
||||
|
||||
$this->additionalJSVariables['user_id'] = $this->user->id;
|
||||
if (!$this->user->can('WarehouseAdmin')) {
|
||||
$this->additionalJSVariables['WAREHOUSE_ADMIN'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function customAutoCompleteWare($value) {
|
||||
if (!is_numeric($value)) return ['id' => $value, 'title' => $value];
|
||||
|
||||
$article = WarehouseArticleModel::get(intval($value));
|
||||
return ['id' => $article->id, 'title' => $article->title];
|
||||
$this->additionalJSVariables = [
|
||||
'user_id' => $this->user->id,
|
||||
'BASE_URL' => '/WarehouseOrderRequest',
|
||||
'WAREHOUSE_ADMIN' => $this->user->can('WarehouseAdmin')
|
||||
];
|
||||
}
|
||||
|
||||
protected function beforeUpdate($postData): bool {
|
||||
@@ -110,62 +59,73 @@ class WarehouseOrderRequestController extends TTCrud {
|
||||
}
|
||||
|
||||
protected function afterCreate($postData): void {
|
||||
if ($_SERVER['HTTP_HOST'] == 'localhost') return;
|
||||
|
||||
if (is_numeric($postData['ware'])) {
|
||||
$article = WarehouseArticleModel::get(intval($postData['ware']));
|
||||
$postData['ware'] = $article->title;
|
||||
}
|
||||
if ($_SERVER['HTTP_HOST'] === 'localhost') return;
|
||||
die("TODO we need this to work with new positions manager");
|
||||
|
||||
$email = new Emailnotification();
|
||||
$postData['ware'] = is_numeric($postData['ware']) ? WarehouseArticleModel::get((int) $postData['ware'])->title : $postData['ware'];
|
||||
$paddedId = str_pad($postData['id'], 5, '0', STR_PAD_LEFT);
|
||||
$email->setSubject("TheTool: Neue Interne Bestellung #$paddedId");
|
||||
|
||||
$body = "Hallo,\n\nes wurde eine neue interne Bestellung erstellt.\n\n";
|
||||
$body .= "Bestellnummer: #$paddedId\n";
|
||||
$body .= "Ware: " . $postData['ware'] . "\n";
|
||||
$body .= "Anzahl: " . $postData['anzahl'] . "\n";
|
||||
$body .= "Verwendungszweck: " . $postData['verwendungszweck'] . "\n";
|
||||
$body .= "Beauftragt von: " . $this->user->name . "\n";
|
||||
$body .= "Beauftragt am: " . date('d.m.Y H:i') . "\n";
|
||||
$body .= "Notiz: " . $postData['note'] . "\n\n";
|
||||
$email->setSubject("TheTool: Neue Interne Bestellung #$paddedId")
|
||||
->setBody(<<<BODY
|
||||
Hallo,
|
||||
|
||||
$email->setBody($body);
|
||||
$email->setFrom(TT_OUTGOING_EMAIL_2FA, TT_OUTGOING_EMAIL_2FA);
|
||||
$email->setTo("einkauf@xinon.at", "Einkauf");
|
||||
$email->send();
|
||||
es wurde eine neue interne Bestellung erstellt.
|
||||
|
||||
Bestellnummer: #$paddedId
|
||||
Ware: {$postData['ware']}
|
||||
Anzahl: {$postData['anzahl']}
|
||||
Verwendungszweck: {$postData['verwendungszweck']}
|
||||
Beauftragt von: {$this->user->name}
|
||||
Beauftragt am: {date('d.m.Y H:i')}
|
||||
Notiz: {$postData['note']}
|
||||
|
||||
BODY
|
||||
)
|
||||
->setFrom(TT_OUTGOING_EMAIL_2FA, TT_OUTGOING_EMAIL_2FA)
|
||||
->setTo("einkauf@xinon.at", "Einkauf")
|
||||
->send();
|
||||
}
|
||||
|
||||
protected function cancelAction() {
|
||||
$id = $this->request->id;
|
||||
$cancel = $this->request->cancel;
|
||||
$id = filter_var($this->request->id, FILTER_VALIDATE_INT);
|
||||
$cancel = filter_var($this->request->cancel, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 1]]);
|
||||
|
||||
if (!is_numeric($id) || !is_numeric($cancel)) {
|
||||
self::returnJson(['error' => 'Invalid request']);
|
||||
if (!$id || $cancel === false) self::returnJson(['error' => 'Ungültige Anfrage']);
|
||||
if (!(WarehouseOrderRequestModel::get($id))) self::returnJson(['error' => 'Bestellwunsch nicht gefunden']);
|
||||
|
||||
WarehouseOrderRequestModel::update(['id' => $id, 'canceled' => $cancel]);
|
||||
self::returnJson(['success' => true]);
|
||||
}
|
||||
|
||||
$order = (array) WarehouseOrderRequestModel::get($id);
|
||||
protected function createNewLogAction() {
|
||||
$postData = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (empty($order)) {
|
||||
self::returnJson(['error' => 'Order not found']);
|
||||
if (empty($postData['orderRequestId']) || empty($postData['note'])) {
|
||||
self::returnJson(['error' => 'Order Request ID is required']);
|
||||
return;
|
||||
}
|
||||
|
||||
// $cancel is either 0 for uncancelling or 1 for cancelling
|
||||
|
||||
if ($cancel == 1) {
|
||||
$order['canceled'] = 1;
|
||||
} else {
|
||||
$order['canceled'] = 0;
|
||||
WarehouseLogModel::create([
|
||||
"table" => "WarehouseOrderRequest",
|
||||
"rowId" => intval($postData['orderRequestId']),
|
||||
"type" => 'noChanges',
|
||||
"message" => $postData['note'],
|
||||
"createBy" => intval($this->user->id),
|
||||
"create" => time()
|
||||
]);
|
||||
self::returnJson(['success' => 'Log entry created']);
|
||||
}
|
||||
|
||||
$order['id'] = $id;
|
||||
|
||||
if (!WarehouseOrderRequestModel::update($order)) {
|
||||
self::returnJson(['error' => 'Error updating order']);
|
||||
protected function getLogByIdAction() {
|
||||
$orderRequestId = $this->request->orderRequestId;
|
||||
if (empty($orderRequestId)) {
|
||||
self::returnJson(['error' => 'Order ID is required']);
|
||||
return;
|
||||
}
|
||||
|
||||
self::returnJson(['success' => true, 'message' => 'Order updated']);
|
||||
|
||||
$log = WarehouseLogModel::getAll(['table' => 'WarehouseOrderRequest', 'rowId' => $orderRequestId]);
|
||||
self::returnJson($log);
|
||||
}
|
||||
|
||||
protected function getHistoryAction() {
|
||||
|
||||
@@ -2,19 +2,12 @@
|
||||
|
||||
class WarehouseOrderRequestModel extends TTCrudBaseModel {
|
||||
public int $id;
|
||||
public int $anzahl;
|
||||
public string $ware;
|
||||
public string $verwendungszweck;
|
||||
public string $create;
|
||||
public int $createBy;
|
||||
public ?int $distributorId;
|
||||
public ?string $order;
|
||||
public ?int $orderBy;
|
||||
public ?string $takeOver;
|
||||
public ?int $takeOverBy;
|
||||
public ?string $warehouseLocation;
|
||||
public string $purpose;
|
||||
public string $positions;
|
||||
public ?string $note;
|
||||
|
||||
public ?int $canceled;
|
||||
public ?string $linkedOrderIds;
|
||||
public ?int $cancelled;
|
||||
public int $create;
|
||||
public int $createBy;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,34 +3,45 @@
|
||||
class WarehouseProjectController extends TTCrud {
|
||||
protected string $headerTitle = 'Projekte';
|
||||
protected string $createText = 'Neues Projekt erstellen';
|
||||
protected string $singleText = 'Projekt';
|
||||
|
||||
//@formatter:off
|
||||
protected array $columns = [
|
||||
['key' => 'title', 'text' => 'Titel', 'required' => true, 'table' => ['class' => 'text-nowrap', 'priority' => 9]],
|
||||
['key' => 'description', 'text' => 'Beschreibung', 'required' => true, 'table' => ['class' => 'text-nowrap']],
|
||||
['key' => 'createBy', 'text' => 'Erstellt von', 'required' => true, 'type' => 'select', 'table' => ['class' => 'text-nowrap', 'filter' => 'select'], 'modal' => ['items' => [], 'type' => 'select']],
|
||||
['key' => 'create', 'text' => 'Erstellt am', 'required' => true, 'table' => ['filter' => 'date', 'class' => 'text-center']],
|
||||
['key' => 'address', 'text' => 'Adresse', 'required' => true, 'type' => 'autocomplete', 'table' => ['class' => 'text-nowrap', 'filter' => false], 'modal' => ['apiUrl' => '/Address/api?do=findAddress', 'items' => '/Address/api?do=findAddress', 'type' => 'autocomplete']],
|
||||
['key' => 'status', 'text' => 'Status', 'required' => true, 'table' => ['filter' => 'select'], 'modal' => [ 'type' => 'select', 'items' => [ ['value' => 'erstellt', 'text' => 'Erstellt'], ['value' => 'in_bearbeitung', 'text' => 'In Bearbeitung'], ['value' => 'erledigt', 'text' => 'Erledigt'], ['value' => 'verrechnet', 'text' => 'Verrechnet']]]]
|
||||
];
|
||||
['key' => 'title', 'text' => 'Titel', 'required' => true],
|
||||
['key' => 'description', 'text' => 'Projektbeschreibung', 'modal' => ['type' => 'textarea']],
|
||||
|
||||
['key' => 'startDate', 'text' => 'Startdatum', 'required' => true, 'modal' => ['type' => 'datepicker']],
|
||||
['key' => 'endDate', 'text' => 'Enddatum', 'required' => true, 'modal' => ['type' => 'datepicker']],
|
||||
['key' => 'positions', 'text' => 'Positionen', 'required' => true, 'modal' => ['type' => 'positions-manager', 'config' => [
|
||||
'header' => 'Positionen',
|
||||
'fields' => [
|
||||
'articleId' => ['apiUrl' => '/WarehouseArticle/autoComplete','type' => 'autocomplete','customFieldReference' => 'WarehouseArticle','label' => 'Artikel'],
|
||||
'amount' => ['type' => 'input', 'label' => 'Menge', 'inputType' => 'number'],
|
||||
'purpose' => ['type' => 'input', 'label' => 'Zweck'],
|
||||
],
|
||||
'validateFormOptions' => [
|
||||
['key' => 'articleId', 'message' => 'Bitte füllen Sie den Artikel aus'],
|
||||
['key' => 'amount', 'message' => 'Bitte füllen Sie die Menge aus'],
|
||||
['key' => 'purpose', 'message' => 'Bitte füllen Sie den Zweck aus'],
|
||||
],
|
||||
]], 'table' => false],
|
||||
['key' => 'linkedOrderIds', 'text' => 'Verlinkte Bestellung', 'modal' => false],
|
||||
//
|
||||
['key' => 'assignedPersons', 'text' => 'Zugewiesene Personen', 'modal' => ['type' => 'positions-manager', 'config' => [
|
||||
'header' => 'Zugewiesene Personen',
|
||||
'fields' => [
|
||||
'userId' => ['apiUrl' => '/WarehouseShippingNote/userAutoComplete','type' => 'autocomplete','label' => 'Person','customFieldReference' => 'User']
|
||||
],
|
||||
'validateFormOptions' => [
|
||||
['key' => 'userId', 'message' => 'Bitte füllen Sie die Person aus'],
|
||||
],
|
||||
]], 'table' => false],
|
||||
|
||||
protected array $additionalActions = [
|
||||
];
|
||||
|
||||
protected array $infoMessages = [
|
||||
'create' => 'Projekt wurde erstellt',
|
||||
'update' => 'Projekt wurde aktualisiert',
|
||||
'delete' => 'Projekt wurde gelöscht',
|
||||
'noChanges' => 'Keine Änderungen',
|
||||
['key' => 'storageLocation', 'text' => 'Lagerort', 'modal' => ['type' => 'input']],
|
||||
['key' => 'note', 'text' => 'Notiz', 'modal' => ['type' => 'textarea']],
|
||||
['key' => 'createBy', 'text' => 'Erstellt von', 'required' => true, 'modal' => ['visible' => false, 'type' => 'select'], 'table' => ['filter' => 'select']],
|
||||
['key' => 'create', 'text' => 'Erstellt', 'required' => true, 'modal' => false],
|
||||
['key' => 'actions', 'text' => 'Aktionen', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center']],
|
||||
];
|
||||
//@formatter:on
|
||||
|
||||
public function prepareCrudConfig() {
|
||||
$users = array_map(function($user) {
|
||||
return ['value' => $user->id, 'text' => $user->name];
|
||||
}, UserModel::search(['employee' => true]));
|
||||
|
||||
$this->columns[1]['modal']['items'] = $users;
|
||||
}
|
||||
}
|
||||
61
db/migrations/20250306100000_warehouse_modify_13.php
Normal file
61
db/migrations/20250306100000_warehouse_modify_13.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class WarehouseModify13 extends AbstractMigration {
|
||||
public function up(): void {
|
||||
if ($this->getEnvironment() == "thetool") {
|
||||
// Add status column to WarehouseOrder
|
||||
$table = $this->table("WarehouseOrder");
|
||||
$table->addColumn("status", "enum", [
|
||||
'values' => ['new','accepted','ordered','sent','partiallyDelivered','fullyDelivered','cancelled'],
|
||||
'null' => true,
|
||||
'default' => null
|
||||
])
|
||||
->save();
|
||||
|
||||
// Recreate WarehouseOrderRequest
|
||||
if ($this->hasTable('WarehouseOrderRequest')) {
|
||||
$this->table('WarehouseOrderRequest')->drop()->save();
|
||||
}
|
||||
|
||||
$orderRequest = $this->table('WarehouseOrderRequest', ['id' => 'id', 'signed' => false]);
|
||||
$orderRequest->addColumn('purpose', 'text')
|
||||
->addColumn('positions', 'text')
|
||||
->addColumn('note', 'text', ['null' => true])
|
||||
->addColumn('linkedOrderIds', 'text', ['null' => true])
|
||||
->addColumn('cancelled', 'integer', ['default' => 0])
|
||||
->addColumn('create', 'integer')
|
||||
->addColumn('createBy', 'integer')
|
||||
->create();
|
||||
|
||||
// Create WarehouseLog if not exists
|
||||
if (!$this->hasTable('WarehouseLog')) {
|
||||
$log = $this->table('WarehouseLog', ['id' => 'id', 'signed' => false]);
|
||||
$log->addColumn('table', 'string', ['limit' => 255])
|
||||
->addColumn('rowId', 'integer')
|
||||
->addColumn('type', 'enum', ['values' => ['noChanges','statusChange']])
|
||||
->addColumn('fileIds', 'text', ['null' => true])
|
||||
->addColumn('message', 'string', ['limit' => 255])
|
||||
->addColumn('create', 'integer')
|
||||
->addColumn('createBy', 'integer')
|
||||
->addIndex(['rowId'], ['name' => 'orderId'])
|
||||
->addIndex(['createBy'], ['name' => 'createBy'])
|
||||
->create();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
if ($this->getEnvironment() == "thetool") {
|
||||
// Remove status column
|
||||
$table = $this->table("WarehouseOrder");
|
||||
$table->removeColumn("status")
|
||||
->save();
|
||||
|
||||
// Drop new tables
|
||||
$this->table('WarehouseOrderRequest')->drop()->save();
|
||||
$this->table('WarehouseLog')->drop()->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ RUN apt install wget libfontenc1 xfonts-75dpi xfonts-base xfonts-encodings xfont
|
||||
|
||||
# Install apache2 and PHP and PHP modules
|
||||
RUN apt update && \
|
||||
apt install -y apache2 curl cron unzip php8.2 php8.2-imap php8.2-curl php8.2-cli php8.2-mysqli php8.2-gd php8.2-zip php8.2-dom php8.2-mbstring && \
|
||||
apt install -y poppler-utils apache2 curl cron unzip php8.2 php8.2-imap php8.2-curl php8.2-cli php8.2-mysqli php8.2-gd php8.2-zip php8.2-dom php8.2-mbstring && \
|
||||
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@@ -37,6 +37,7 @@ class TTCrud extends mfBaseController {
|
||||
$this->model = new $modelName();
|
||||
$this->postData = json_decode(file_get_contents('php://input'), true);
|
||||
$this->checkArray = $this->getCheckArray();
|
||||
$this->infoMessages = $this->getInfoMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,6 +294,24 @@ class TTCrud extends mfBaseController {
|
||||
if (method_exists($this, 'getByIdParse') && !isset($_GET['disableParse'])) $data = $this->getByIdParse($data);
|
||||
self::returnJson($data);
|
||||
}
|
||||
|
||||
private function getInfoMessages(): array {
|
||||
if (isset($this->infoMessages) && is_array($this->infoMessages)) {
|
||||
return $this->infoMessages;
|
||||
}
|
||||
|
||||
if (isset($this->singleText) && is_string($this->singleText)) {
|
||||
return ['create' => $this->singleText . ' wurde erstellt.',
|
||||
'update' => $this->singleText . ' wurde aktualisiert',
|
||||
'delete' => $this->singleText . ' wurde gelöscht',
|
||||
'noChanges' => 'Keine Änderungen'];
|
||||
}
|
||||
|
||||
return ['create' => 'Eintrag wurde erstellt.',
|
||||
'update' => 'Eintrag wurde aktualisiert',
|
||||
'delete' => 'Eintrag wurde gelöscht',
|
||||
'noChanges' => 'Keine Änderungen'];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -149,7 +149,6 @@ class mfUpload {
|
||||
throw new Exception ("Not enough data to build savepath!",605);
|
||||
}
|
||||
}
|
||||
|
||||
if(!$this->upload->move_upload($this->savepath."/".$this->filename)) {
|
||||
throw new Exception ("Unable to move temp file: ".$this->upload->errormessage,605);
|
||||
}
|
||||
|
||||
@@ -50,6 +50,14 @@ class mfUpload_TmpFile {
|
||||
|
||||
public function move_upload($path) {
|
||||
if($path && $this->tmp_name) {
|
||||
// check if all directories exist needed for the path
|
||||
$dir = dirname($path);
|
||||
if(!is_dir($dir)) {
|
||||
if(!mkdir($dir, 0777, true)) {
|
||||
$this->errormessage = "Cannot create directory $dir.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(move_uploaded_file($this->tmp_name, $path)) {
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -26,6 +26,83 @@
|
||||
}
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 0.5fr 0.5fr 1fr 2fr 0.5fr;
|
||||
grid-gap: 10px;
|
||||
}
|
||||
.grid-container.header {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.upload-success-alert {
|
||||
background-color: #d4edda;
|
||||
border: 1px solid #c3e6cb;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.alert-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 18px;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.alert-header i {
|
||||
margin-right: 10px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.file-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin-bottom: 8px;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.file-item:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.file-item i {
|
||||
margin-right: 10px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
flex-grow: 1;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.remove-btn {
|
||||
background-color: #dc3545;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.remove-btn:hover {
|
||||
background-color: #c82333;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Expanded Row Styling */
|
||||
.order-summary {
|
||||
|
||||
@@ -1,3 +1,174 @@
|
||||
Vue.component('change-status-modal', {
|
||||
props: {
|
||||
orderId: {type: Number, required: true},
|
||||
type: {type: String, default: 'accept'}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
order: null,
|
||||
newStatus: 'noChanges',
|
||||
note: '',
|
||||
file: null,
|
||||
uploadedFiles: []
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getById`, {params: {id: this.orderId}});
|
||||
// if order.status is canceled emit close event and window.notify('error', 'Bestellung wurde storniert')
|
||||
if (response.data.status === 'cancelled') {
|
||||
this.$emit('close');
|
||||
window.notify('error', 'Bestellung wurde storniert');
|
||||
}
|
||||
this.order = response.data;
|
||||
|
||||
|
||||
},
|
||||
computed: {
|
||||
availableStatuses() {
|
||||
switch (this.order.status) {
|
||||
case 'new':
|
||||
return [
|
||||
{value: 'noChanges', text: 'Keine Änderungen'},
|
||||
{value: 'accepted', text: 'Akzeptiert'},
|
||||
{value: 'cancelled', text: 'Storniert'},
|
||||
];
|
||||
case 'accepted':
|
||||
return [
|
||||
{value: 'noChanges', text: 'Keine Änderungen'},
|
||||
{value: 'ordered', text: 'Bestellt'},
|
||||
{value: 'cancelled', text: 'Storniert'},
|
||||
];
|
||||
case 'ordered':
|
||||
return [
|
||||
{value: 'noChanges', text: 'Keine Änderungen'},
|
||||
{value: 'sent', text: 'Versendet'},
|
||||
{value: 'cancelled', text: 'Storniert'},
|
||||
];
|
||||
case 'sent':
|
||||
return [
|
||||
{value: 'noChanges', text: 'Keine Änderungen'},
|
||||
{value: 'partiallyDelivered', text: 'Teilweise geliefert'},
|
||||
{value: 'fullyDelivered', text: 'Geliefert'},
|
||||
{value: 'cancelled', text: 'Storniert'},
|
||||
];
|
||||
case 'partiallyDelivered':
|
||||
return [
|
||||
{value: 'noChanges', text: 'Keine Änderungen'},
|
||||
{value: 'fullyDelivered', text: 'Geliefert'},
|
||||
{value: 'cancelled', text: 'Storniert'},
|
||||
];
|
||||
case 'fullyDelivered':
|
||||
return [
|
||||
{value: 'noChanges', text: 'Keine Änderungen'},
|
||||
{value: 'cancelled', text: 'Storniert'},
|
||||
];
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async handleFileUpload(event) {
|
||||
const files = event.target.files;
|
||||
if (!files.length) return;
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
try {
|
||||
const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/uploadFile`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
this.uploadedFiles.push({
|
||||
id: response.data.fileId,
|
||||
name: file.name
|
||||
});
|
||||
window.notify('success', `File "${file.name}" uploaded successfully`);
|
||||
} else {
|
||||
window.notify('error', `File "${file.name}" upload failed: ${response.data.error || 'Unknown error'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
window.notify('error', `Error uploading file "${file.name}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the file input
|
||||
event.target.value = '';
|
||||
},
|
||||
removeFile: index => this.uploadedFiles.splice(index, 1),
|
||||
async submit() {
|
||||
const fileIds = this.uploadedFiles.map(file => file.id);
|
||||
const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/createNewLogAction`, {
|
||||
orderId: this.order.id,
|
||||
status: this.newStatus,
|
||||
note: this.note,
|
||||
fileIds: JSON.stringify(fileIds)
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
this.$emit('close');
|
||||
window.notify('success', response.data.message ?? 'Status erfolgreich geändert');
|
||||
} else {
|
||||
window.notify('error',
|
||||
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten');
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<tt-modal :show="true" @submit="submit" @update:show="$emit('close')" title="Status ändern">
|
||||
<tt-loader :absolute="false" v-if="!order"/>
|
||||
<template v-else>
|
||||
<tt-select label="Neuer Status" v-model="newStatus" :options="availableStatuses" sm row/>
|
||||
|
||||
<div class="form-group" style="margin: 10px 0">
|
||||
<label>Dateiupload (Mehrere)</label>
|
||||
<input type="file" class="form-control" @change="handleFileUpload" multiple/>
|
||||
</div>
|
||||
|
||||
<div v-if="uploadedFiles.length" class="upload-success-alert">
|
||||
<div class="alert-header">
|
||||
<i class="fa fa-check-circle" aria-hidden="true"></i>
|
||||
<span v-if="uploadedFiles.length === 1">Datei erfolgreich hochgeladen</span>
|
||||
<span v-else>Dateien erfolgreich hochgeladen</span>
|
||||
</div>
|
||||
<ul class="file-list">
|
||||
<li v-for="(file, index) in uploadedFiles" :key="file.id" class="file-item">
|
||||
<i class="fa fa-file" aria-hidden="true"></i>
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
<button type="button" class="remove-btn" @click="removeFile(index)">
|
||||
<i class="fa fa-times" aria-hidden="true"></i>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<tt-textarea label="Bemerkung*" v-model="note" sm/>
|
||||
|
||||
<div v-if="newStatus === 'partiallyDelivered' || newStatus === 'fullyDelivered'">
|
||||
<h4>Positionen</h4>
|
||||
<div style="display: grid; grid-gap: 10px; grid-template-columns: 1fr 1fr 1fr;margin-top: 24px">
|
||||
<div><strong>Artikel</strong></div>
|
||||
<div><strong>Menge</strong></div>
|
||||
<div><strong>Geliefert?</strong></div>
|
||||
<template v-for="position in order.positions">
|
||||
<div>{{ position.articleName }}</div>
|
||||
<div>{{ position.amount }}</div>
|
||||
<div><input type="checkbox" v-model="position.delivered"/></div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</tt-modal>
|
||||
|
||||
`
|
||||
});
|
||||
|
||||
Vue.component('warehouse-order-modal', {
|
||||
props: {
|
||||
id: {type: [String, Number], required: true},
|
||||
@@ -6,6 +177,7 @@ Vue.component('warehouse-order-modal', {
|
||||
template: `
|
||||
<tt-modal :show="true"
|
||||
@submit="submit"
|
||||
@delete="deleteOrder"
|
||||
:delete="id !== 'create'"
|
||||
:title="id === 'create' ? 'Bestellung erstellen' : \`Bestellung #\${id} bearbeiten\`"
|
||||
@update:show="$emit('close')">
|
||||
@@ -94,13 +266,31 @@ Vue.component('warehouse-order-modal', {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (this.id === 'create') return;
|
||||
if (this.id !== 'create') {
|
||||
const {data} = await axios.get(`${window.TT_CONFIG.BASE_PATH}/WarehouseOrder/getById?disableParse`, {params: {id: this.id}});
|
||||
this.order = {...data, positions: JSON.parse(data.positions)};
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(this.id);
|
||||
const orderRequest = JSON.parse(localStorage.getItem('WarehouseOrder_create'));
|
||||
if (!orderRequest) return;
|
||||
|
||||
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getById?disableParse`, {params: {id: this.id}});
|
||||
response.data.positions = JSON.parse(response.data.positions);
|
||||
this.order = response.data;
|
||||
const positions = JSON.parse(orderRequest.positions);
|
||||
this.order.positions = await Promise.all(positions.map(async p => {
|
||||
const distributor = (await axios.get(`${window.TT_CONFIG.BASE_PATH}/WarehouseOrder/getArticleDistributorData`,
|
||||
{params: {articleId: p.articleId}})).data[0];
|
||||
return {
|
||||
article: p.articleId,
|
||||
amount: p.amount,
|
||||
buyPrice: distributor.purchasePrice,
|
||||
distributorId: distributor.id,
|
||||
distributorArticleNumber: distributor.externalArticleNumber,
|
||||
verwendung: `${p.purpose} [Bestellwunsch: #${orderRequest.id}]`,
|
||||
linkedOrderRequestId: orderRequest.id
|
||||
};
|
||||
}));
|
||||
|
||||
localStorage.removeItem('WarehouseOrder_create');
|
||||
},
|
||||
methods: {
|
||||
async submit() {
|
||||
@@ -131,6 +321,14 @@ Vue.component('warehouse-order-modal', {
|
||||
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten');
|
||||
}
|
||||
},
|
||||
async deleteOrder() {
|
||||
if (!window.confirm('Bestellung wirklich löschen?')) return;
|
||||
const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/delete`, {id: this.id});
|
||||
if (response.data.success) {
|
||||
this.$emit('close');
|
||||
window.notify('success', response.data.message || 'Bestellung erfolgreich gelöscht');
|
||||
} else window.notify('error', response.data.message || 'Ein Fehler ist aufgetreten');
|
||||
},
|
||||
async fetchDistributors(article) {
|
||||
const url = `${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getArticleDistributorData`;
|
||||
const params = typeof article === 'string' ? {allDistributor: true} : {articleId: article};
|
||||
@@ -152,96 +350,130 @@ Vue.component('warehouse-order-modal', {
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'order.positions': {
|
||||
handler(newPositions) {
|
||||
if (this.id !== 'create' && new Set(newPositions.map(p => p.distributorId)).size > 1) {
|
||||
window.notify('error', 'Eine bestehende Bestellung kann nur Positionen vom gleichen Lieferanten enthalten.');
|
||||
this.order.positions = newPositions.filter(p => p.distributorId === this.order.distributorId);
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
}
|
||||
,
|
||||
});
|
||||
|
||||
Vue.component('tt-file', {
|
||||
props: ['id'],
|
||||
data: () => ({file: null}),
|
||||
async mounted() {
|
||||
const response = await axios.get(`${window.TT_CONFIG.BASE_PATH}/File/getById`, {params: {id: this.id}});
|
||||
this.file = response.data;
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<a :href="'/File/download?id=' + id" target="_blank" v-if="file">{{ file.filename }}</a>
|
||||
<template v-else>
|
||||
<div class="spinner-border spinner-border-sm text-primary" role="status"><span class="sr-only">Loading...</span></div>
|
||||
</template>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
|
||||
|
||||
Vue.component('warehouse-order-detail', {
|
||||
//language=Vue
|
||||
template: `
|
||||
<tt-card>
|
||||
<template v-slot:header><h4>Bestellungsdetails für #{{ loading ? 'Laden...' : order.orderNumber }}</h4></template>
|
||||
|
||||
<template v-if="loading">
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<div class="spinner-border spinner-border-sm text-primary" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
<div class="spinner-border spinner-border-sm text-primary" role="status"><span class="sr-only">Loading...</span></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<h3>Lieferadresse</h3>
|
||||
<div>{{order.delAddrName}}</div>
|
||||
<div>{{order.delAddrEMail}}</div>
|
||||
<div>{{order.delAddrLine}}</div>
|
||||
<div>{{order.delAddrPLZ}} {{order.delAddrCity}}</div>
|
||||
|
||||
<div style="display: grid; grid-gap: 10px; grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;margin-top: 24px">
|
||||
<div><strong>Artikel</strong></div>
|
||||
<div><strong>Menge</strong></div>
|
||||
<div><strong>Preis</strong></div>
|
||||
<div><strong>Lieferant</strong></div>
|
||||
<div><strong>Verwendung</strong></div>
|
||||
<div><strong>Summe</strong></div>
|
||||
<h3>Positionen</h3>
|
||||
<div class="grid-container header">
|
||||
<div v-for="header in ['Artikel', 'Menge', 'Preis', 'Lieferant', 'Verwendung', 'Summe']"><strong>{{ header }}</strong></div>
|
||||
</div>
|
||||
<div class="grid-container" v-for="p in order.positions">
|
||||
<div>{{ p.articleName }}</div>
|
||||
<div>{{ p.amount }}</div>
|
||||
<div>{{ p.buyPrice }}</div>
|
||||
<div>{{ p.distributorName }}</div>
|
||||
<div>{{ p.verwendung }}</div>
|
||||
<div>{{ p.amount * p.buyPrice }}</div>
|
||||
</div>
|
||||
<template v-if="orderLog?.length > 0">
|
||||
<hr>
|
||||
<h3>Log</h3>
|
||||
<div v-for="log in orderLog">
|
||||
{{ formatDate(log.create) }} ({{ getUserName(log.createBy) }}) | {{ log.message }}
|
||||
<!-- if log.fileIds exists and it is a array of ids use <tt-file :id=> to show the file-->
|
||||
|
||||
<div style="display: grid; grid-gap: 10px; grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;" v-for="position in order.positions">
|
||||
<div>{{ position.articleName }}</div>
|
||||
<div>{{ position.amount }}</div>
|
||||
<div>{{ position.buyPrice }}</div>
|
||||
<div>{{ position.distributorName }}</div>
|
||||
<div>{{ position.verwendung }}</div>
|
||||
<div>{{ position.amount * position.buyPrice }}</div>
|
||||
<template v-if="log.fileIds">
|
||||
<div v-for="file in JSON.parse(log.fileIds)">
|
||||
<tt-file :id="file"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<hr>
|
||||
<h3>Lieferadresse</h3>
|
||||
<div v-for="field in ['delAddrName', 'delAddrEMail', 'delAddrLine']">{{ order[field] }}</div>
|
||||
<div>{{ order.delAddrPLZ }} {{ order.delAddrCity }}</div>
|
||||
</template>
|
||||
</tt-card>
|
||||
`,
|
||||
props: {
|
||||
id: {type: [String, Number], required: true}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
window: window,
|
||||
order: {},
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
props: ['id'],
|
||||
data: () => ({order: {}, orderLog: null, loading: true}),
|
||||
async mounted() {
|
||||
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getById`, {params: {id: this.id}});
|
||||
this.order = response.data;
|
||||
const [orderResponse, logResponse] = await Promise.all([
|
||||
axios.get(`${window.TT_CONFIG.BASE_PATH}/WarehouseOrder/getById`, {params: {id: this.id}}),
|
||||
axios.get(`${window.TT_CONFIG.BASE_PATH}/WarehouseOrder/getLogById`, {params: {id: this.id}})
|
||||
]);
|
||||
this.order = orderResponse.data;
|
||||
this.orderLog = logResponse.data;
|
||||
this.loading = false;
|
||||
},
|
||||
methods: {
|
||||
formatDate: date => window.moment(date * 1000).format('DD.MM.YYYY HH:mm'),
|
||||
getUserName: id => window.TT_CONFIG.CRUD_CONFIG.columns.find(col => col.key === 'createBy')?.modal.items.find(u => u.value === id)?.text
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('warehouse-order', {
|
||||
template: `
|
||||
<tt-card>
|
||||
<warehouse-order-modal v-if="orderModalId" :id="orderModalId" @close="closeOrderModal"/>
|
||||
<button @click="orderModalId = 'create'" class="btn btn-primary">Bestellung erstellen</button>
|
||||
<warehouse-order-modal v-if="orderModalId" :id="orderModalId" @close="closeModal"/>
|
||||
<change-status-modal v-if="changeStatusModalId" :orderId="changeStatusModalId" @close="closeModal"/>
|
||||
<tt-button text="Bestellung erstellen" @click="orderModalId = 'create'" additional-class="btn-primary"/>
|
||||
<tt-table-crud emit-edit
|
||||
@openpdf="window.open(window.TT_CONFIG['BASE_PATH'] + '/WarehouseOrder/createPDF?id=' + $event.id)"
|
||||
@openpdf="openPDF"
|
||||
@changeStatus="changeStatusModalId = $event.id"
|
||||
@edit="orderModalId = $event.id" ref="table">
|
||||
<template v-slot:expandedRow="{ row }">
|
||||
<warehouse-order-detail :id="row['id']"/>
|
||||
<warehouse-order-detail :id="row.id"/>
|
||||
</template>
|
||||
|
||||
<template v-slot:sum="{ row }">{{ calculateSum(JSON.parse(row["positions"])).toFixed(2)}} €</template>
|
||||
<template v-slot:sum="{ row }">{{ calculateSum(JSON.parse(row["positions"])).toFixed(2) }} €</template>
|
||||
</tt-table-crud>
|
||||
</tt-card>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
window: window,
|
||||
data: () => ({
|
||||
orderModalId: null,
|
||||
}
|
||||
changeStatusModalId: null
|
||||
}),
|
||||
mounted() {
|
||||
if (JSON.parse(localStorage.getItem('WarehouseOrder_create'))) this.orderModalId = 'create';
|
||||
},
|
||||
methods: {
|
||||
closeOrderModal() {
|
||||
closeModal() {
|
||||
this.orderModalId = null;
|
||||
this.changeStatusModalId = null;
|
||||
this.$refs.table.$refs.table.refreshTable();
|
||||
},
|
||||
calculateSum(positions) {
|
||||
return positions.reduce((sum, position) => sum + position.amount * position.buyPrice, 0);
|
||||
}
|
||||
calculateSum: positions => positions.reduce((sum, {amount, buyPrice}) => sum + amount * buyPrice, 0),
|
||||
openPDF: order => window.open(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/createPDF?id=${order.id}`)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
.WarehouseOrderRequestDetailTable {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
max-width: 500px;
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.WarehouseOrderRequestDetailTable > div {
|
||||
padding: 12px 15px;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
color: #ffffff;
|
||||
background-color: #4a90e2;
|
||||
}
|
||||
|
||||
.WarehouseOrderRequestDetailTable > div:nth-child(3n+1),
|
||||
.WarehouseOrderRequestDetailTable > div:nth-child(3n+2),
|
||||
.WarehouseOrderRequestDetailTable > div:nth-child(3n+3) {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.WarehouseOrderRequestDetailTable > div:nth-child(n+4) {
|
||||
background-color: #ffffff;
|
||||
color: #333333;
|
||||
font-weight: normal;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.WarehouseOrderRequestDetailTable > div:nth-child(n+4):nth-child(6n+4),
|
||||
.WarehouseOrderRequestDetailTable > div:nth-child(n+4):nth-child(6n+5),
|
||||
.WarehouseOrderRequestDetailTable > div:nth-child(n+4):nth-child(6n+6) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.WarehouseOrderRequestDetailTable > div:last-child,
|
||||
.WarehouseOrderRequestDetailTable > div:nth-last-child(2),
|
||||
.WarehouseOrderRequestDetailTable > div:nth-last-child(3) {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
min-height: unset !important;
|
||||
}
|
||||
@@ -1,127 +1,247 @@
|
||||
window.localStorage.setItem('tt-table-WarehouseOrderRequest', JSON.stringify({
|
||||
filters: {
|
||||
takeOverBy: null,
|
||||
canceled: 0
|
||||
}
|
||||
}));
|
||||
|
||||
window.TT_CONFIG["CRUD_CONFIG"]["additionalActions"] = [
|
||||
...window.TT_CONFIG["CRUD_CONFIG"]["additionalActions"],
|
||||
{
|
||||
"key": "cancel",
|
||||
"title": "Bestellwunsch stornieren",
|
||||
"class": "fas fa-times text-danger",
|
||||
"condition": (row) => window.TT_CONFIG['WAREHOUSE_ADMIN'] === '1' && row.canceled === 0,
|
||||
key: "cancelRequest",
|
||||
title: "Bestellwunsch stornieren",
|
||||
class: "fas fa-times text-danger",
|
||||
condition: (row) => window.TT_CONFIG['WAREHOUSE_ADMIN'] === '1' && row.cancelled === 0,
|
||||
},
|
||||
{
|
||||
"key": "uncancel",
|
||||
"title": "Bestellwunsch wiederherstellen",
|
||||
"class": "fas fa-check text-success",
|
||||
"condition": (row) => window.TT_CONFIG['WAREHOUSE_ADMIN'] === '1' && row.canceled === 1,
|
||||
key: "uncancelRequest",
|
||||
title: "Bestellwunsch wiederherstellen",
|
||||
class: "fas fa-check text-success",
|
||||
condition: (row) => window.TT_CONFIG['WAREHOUSE_ADMIN'] === '1' && row.cancelled === 1,
|
||||
},
|
||||
{
|
||||
key: "createOrder",
|
||||
title: "Bestellung erstellen",
|
||||
class: "fas fa-plus text-success",
|
||||
condition: (row) => window.TT_CONFIG['WAREHOUSE_ADMIN'] === '1'
|
||||
&& row.cancelled === 0 && (!row.linkedOrderIds || row.linkedOrderIds.length === 0)
|
||||
&& JSON.parse(row.positions).filter(position => position.articleId_text).length === 0,
|
||||
}
|
||||
]
|
||||
|
||||
Vue.component('add-log-modal', {
|
||||
props: {
|
||||
orderRequestId: {type: Number, required: true},
|
||||
type: {type: String, default: 'accept'}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
orderRequest: null,
|
||||
note: '',
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrderRequest/getById`, {params: {id: this.orderRequestId}});
|
||||
this.orderRequest = response.data;
|
||||
|
||||
if (this.orderRequest.cancelled === 1) {
|
||||
this.$emit('close');
|
||||
window.notify('error', 'Bestellwunsch wurde storniert');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async submit() {
|
||||
const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrderRequest/createNewLogAction`, {
|
||||
orderRequestId: this.orderRequestId,
|
||||
note: this.note,
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
this.$emit('close');
|
||||
window.notify('success', 'Log-Eintrag erstellt');
|
||||
} else {
|
||||
window.notify('error',
|
||||
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten');
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<tt-modal :show="true" :delete="false" @submit="submit" @update:show="$emit('close')" title="Status ändern">
|
||||
<tt-loader :absolute="false" v-if="!orderRequest"/>
|
||||
<template v-else>
|
||||
<tt-textarea label="Bemerkung*" v-model="note" sm/>
|
||||
</template>
|
||||
</tt-modal>
|
||||
|
||||
`
|
||||
})
|
||||
|
||||
Vue.component('order-request-log', {
|
||||
props: {orderRequestId: {type: Number, required: true}},
|
||||
data: () => ({
|
||||
logs: []
|
||||
}),
|
||||
async mounted() {
|
||||
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrderRequest/getLogById`, {params: {orderRequestId: this.orderRequestId}});
|
||||
this.logs = response.data;
|
||||
|
||||
const response2 = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrderRequest/getById`, {params: {id: this.orderRequestId}});
|
||||
// check if linkedOrderIds is set and if set length > 0 and if so, get the linked orders logs
|
||||
// and add them to the logs array and sort them by create date
|
||||
|
||||
// if response2.data.linkedOrderIds is a string try to parse it
|
||||
if (typeof response2.data.linkedOrderIds === 'string') {
|
||||
try {
|
||||
response2.data.linkedOrderIds = JSON.parse(response2.data.linkedOrderIds);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (response2.data.linkedOrderIds && response2.data.linkedOrderIds.length > 0) {
|
||||
const linkedOrdersLogs = await Promise.all(
|
||||
response2.data.linkedOrderIds.map(async (id) => {
|
||||
const res1 = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getById`, {params: {id}});
|
||||
const res2 = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getLogById`, {params: {id}});
|
||||
|
||||
return res2.data.map(log => {
|
||||
log.message = `${res1.data.orderNumber} - ${log.message}`;
|
||||
return log;
|
||||
})
|
||||
})
|
||||
);
|
||||
this.logs = this.logs.concat(...linkedOrdersLogs).sort((a, b) => b.create - a.create);
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
methods: {
|
||||
formatDate: date => window.moment(date * 1000).format('DD.MM.YYYY HH:mm'),
|
||||
getUserName: id => window.TT_CONFIG.CRUD_CONFIG.columns.find(col => col.key === 'createBy')?.modal.items.find(u => u.value === id)?.text
|
||||
},
|
||||
//language=Vue
|
||||
template: `
|
||||
<div>
|
||||
<template v-if="logs.length > 0">
|
||||
<hr>
|
||||
<h3>Log</h3>
|
||||
<div v-for="log in logs" :key="log.id" class="alert alert-light">
|
||||
{{ formatDate(log.create) }} ({{ getUserName(log.createBy) }}) | {{ log.message }}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
|
||||
|
||||
Vue.component('linked-order-status', {
|
||||
props: ['linkedOrders'],
|
||||
data: () => ({
|
||||
orders: [],
|
||||
statusTranslations: {
|
||||
new: 'Neu',
|
||||
accepted: 'Akzeptiert',
|
||||
ordered: 'Bestellt',
|
||||
sent: 'Versendet',
|
||||
partiallyDelivered: 'Teilweise geliefert',
|
||||
fullyDelivered: 'Geliefert',
|
||||
cancelled: 'Storniert',
|
||||
}
|
||||
}),
|
||||
async mounted() {
|
||||
this.orders = await Promise.all(
|
||||
JSON.parse(this.linkedOrders).map(id => axios.get(`${window.TT_CONFIG.BASE_PATH}/WarehouseOrder/getById?id=${id}`).then(response => response.data))
|
||||
);
|
||||
},
|
||||
//language=Vue
|
||||
template: `
|
||||
<div>
|
||||
<span v-for="order in orders"
|
||||
:key="order.id"
|
||||
class="badge badge-pill badge-primary mr-1">{{ order.orderNumber }} - {{ statusTranslations[order.status] }}</span>
|
||||
</div>`
|
||||
});
|
||||
|
||||
Vue.component('warehouse-order-request-detail', {
|
||||
props: {
|
||||
positions: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
//language=Vue
|
||||
template: `
|
||||
<div style="display: flex; justify-content: center; margin-bottom: 10px">
|
||||
<div class="WarehouseOrderRequestDetailTable">
|
||||
<div>ARTIKEL</div>
|
||||
<div>MENGE</div>
|
||||
<div>ZWECK</div>
|
||||
<template v-for="position in positions">
|
||||
<div>
|
||||
<tt-resolver v-if="position.articleId" reference="WarehouseArticle" :value="position.articleId"/>
|
||||
<span v-else>{{ position.articleId_text }}</span>
|
||||
</div>
|
||||
<div>{{ position.amount }}</div>
|
||||
<div>{{ position.purpose }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
|
||||
|
||||
Vue.component('warehouse-order-request', {
|
||||
//language=Vue
|
||||
template: `
|
||||
<tt-card>
|
||||
<tt-table-crud @openHistory="historyModal = true; historyModalId = $event.id"
|
||||
@cancel="cancelOrderRequest($event, '1')"
|
||||
@uncancel="cancelOrderRequest($event, '0')"
|
||||
<tt-table-crud @openHistory="openHistory"
|
||||
@cancelRequest="cancelRequest"
|
||||
@uncancelRequest="uncancelRequest"
|
||||
@createLog="createLog"
|
||||
@createOrder="createOrder"
|
||||
ref="crud">
|
||||
<!-- <slot name="table-top-buttons"></slot> add checkbox "Ausgeblendete Bestellungen anzeigen-->
|
||||
|
||||
<template v-slot:table-top-buttons>
|
||||
<div class="d-flex">
|
||||
<tt-button
|
||||
class="mr-2"
|
||||
@click="showHiddenRequests = !showHiddenRequests"
|
||||
:text="showHiddenRequests ? 'Erledigte Bestellungen ausblenden' : 'Erledigte Bestellungen anzeigen'"
|
||||
:additional-class="showHiddenRequests ? 'btn-danger' : 'btn-primary'"/>
|
||||
<tt-button @click="showCanceledRequests = !showCanceledRequests"
|
||||
:text="showCanceledRequests ? 'Stornierte Bestellungen ausblenden' : 'Stornierte Bestellungen anzeigen'"
|
||||
:additional-class="showCanceledRequests ? 'btn-danger' : 'btn-primary'"/>
|
||||
</div>
|
||||
<template #linkedorderids="{row}">
|
||||
<linked-order-status :linkedOrders="row.linkedOrderIds" v-if="row.linkedOrderIds"/>
|
||||
</template>
|
||||
|
||||
<template v-slot:create="{ row }">
|
||||
{{ row.create ? window.moment(row.create * 1000).format('DD.MM.YYYY') : '' }}
|
||||
</template>
|
||||
|
||||
<template v-slot:order="{ row }">
|
||||
{{ row.order ? window.moment(row.order * 1000).format('DD.MM.YYYY') : '' }}
|
||||
</template>
|
||||
|
||||
<template v-slot:takeover="{ row }">
|
||||
{{ row.takeOver ? window.moment(row.takeOver * 1000).format('DD.MM.YYYY') : '' }}
|
||||
</template>
|
||||
|
||||
<template v-slot:note="{ row }">
|
||||
<span v-if="row.note && row.note.length > 45" :title="row.note">{{ row.note.substring(0, 45) }}...</span>
|
||||
<template #note="{row}">
|
||||
<span v-if="row.note?.length > 45" :title="row.note">{{ row.note.substring(0, 45) }}...</span>
|
||||
<span v-else>{{ row.note }}</span>
|
||||
</template>
|
||||
|
||||
<template #expandedRow="{row}">
|
||||
<warehouse-order-request-detail :positions="JSON.parse(row['positions'])"/>
|
||||
<order-request-log :orderRequestId="row.id"/>
|
||||
</template>
|
||||
</tt-table-crud>
|
||||
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
|
||||
<add-log-modal v-if="addLogModalId" :orderRequestId="addLogModalId" @close="addLogModal = false; addLogModalId = null"/>
|
||||
</tt-card>
|
||||
`, data() {
|
||||
return {
|
||||
window: window, historyModal: false, historyModalId: null, showHiddenRequests: false, showCanceledRequests: false
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (this.window.TT_CONFIG.WAREHOUSE_ADMIN !== '1') return
|
||||
|
||||
this.$refs.crud.$watch('crudModal', (value) => {
|
||||
return
|
||||
if (value) {
|
||||
// if id is not 'create' then check if order is set and if not set it to current date
|
||||
// if order is set then check if takeover is set and if not set it to current date
|
||||
if (!this.$refs.crud.crudModalData.id) return
|
||||
|
||||
if (!this.$refs.crud.crudModalData.order) {
|
||||
this.$refs.crud.crudModalData.order = window.moment().unix()
|
||||
this.$refs.crud.crudModalData.orderBy = window.TT_CONFIG.user_id
|
||||
this.$refs.crud.$refs["order-modal-input"][0].setStartDate(window.moment().format('MM/DD/YYYY'))
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.$refs.crud.crudModalData.takeover) {
|
||||
this.$refs.crud.crudModalData.takeover = window.moment().unix
|
||||
this.$refs.crud.crudModalData.takeOverBy = window.TT_CONFIG.user_id
|
||||
this.$refs.crud.$refs["takeover-modal-input"][0].setStartDate(window.moment().format('MM/DD/YYYY'))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
`,
|
||||
data: () => ({
|
||||
window,
|
||||
historyModal: false,
|
||||
historyModalId: null,
|
||||
addLogModal: false,
|
||||
addLogModalId: null,
|
||||
showHiddenRequests: false,
|
||||
showCanceledRequests: false,
|
||||
orderRequestModalId: null
|
||||
}),
|
||||
methods: {
|
||||
// portected function cancelAction() {
|
||||
// $id = $this->request->id;
|
||||
// $cancel = $this->request->cancel;
|
||||
async cancelOrderRequest(row, cancel) {
|
||||
if (!window.confirm('Bestellwunsch wirklich stornieren?')) return
|
||||
|
||||
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseOrderRequest/cancel?id=' + row.id + '&cancel=' + cancel);
|
||||
if (response.data.success) {
|
||||
this.window.notify('success', response.data.message || 'Erfolgreich aktualisiert')
|
||||
this.$refs.crud.$refs.table.refreshTable()
|
||||
return
|
||||
}
|
||||
this.window.notify('error', response.data.message || 'Fehler beim aktualisieren')
|
||||
}
|
||||
openHistory(e) {
|
||||
this.historyModal = true;
|
||||
this.historyModalId = e.id;
|
||||
},
|
||||
watch: {
|
||||
async showHiddenRequests(value) {
|
||||
this.showCanceledRequests = false
|
||||
this.$refs.crud.$refs.table.$set(this.$refs.crud.$refs.table.filters, 'canceled', '')
|
||||
this.$refs.crud.$refs.table.$set(this.$refs.crud.$refs.table.filters, 'takeOverBy', value ? '' : null)
|
||||
async cancelRequest(row, cancel) {
|
||||
if (!confirm('Bestellwunsch wirklich stornieren?')) return;
|
||||
const res = await axios.get(`${window.TT_CONFIG.BASE_PATH}/WarehouseOrderRequest/cancel?id=${row.id}&cancel=${cancel}`);
|
||||
window.notify(res.data.success ? 'success' : 'error',
|
||||
res.data.message || (res.data.success ? 'Erfolgreich aktualisiert' : 'Fehler beim aktualisieren'));
|
||||
if (res.data.success) this.$refs.crud.$refs.table.refreshTable();
|
||||
},
|
||||
async showCanceledRequests(value) {
|
||||
this.showHiddenRequests = false
|
||||
this.$refs.crud.$refs.table.$set(this.$refs.crud.$refs.table.filters, 'canceled', value ? '1' : '')
|
||||
this.$refs.crud.$refs.table.$set(this.$refs.crud.$refs.table.filters, 'takeOverBy', '')
|
||||
async createLog(row) {
|
||||
this.addLogModal = true;
|
||||
this.addLogModalId = row.id;
|
||||
},
|
||||
uncancelRequest(row) {
|
||||
this.cancelRequest(row, '0');
|
||||
},
|
||||
async createOrder(row) {
|
||||
const res = await axios.get(`${window.TT_CONFIG.BASE_PATH}/WarehouseOrderRequest/getById?id=${row.id}`);
|
||||
if (res.data?.positions && typeof res.data.positions === 'string') {
|
||||
localStorage.setItem('WarehouseOrder_create', JSON.stringify(res.data));
|
||||
window.location.href = `${window.TT_CONFIG.BASE_PATH}/WarehouseOrder`;
|
||||
} else window.notify('error', res.data.message || 'Fehler beim erstellen der Bestellung');
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,161 +1,11 @@
|
||||
Vue.component('warehouse-project-modal', {
|
||||
props: {
|
||||
id: { type: [String, Number], required: true },
|
||||
mode: { type: String, default: 'edit' }
|
||||
},
|
||||
template: `
|
||||
<tt-modal :show="true"
|
||||
@submit="submit"
|
||||
:delete="id !== 'create'"
|
||||
:title="id === 'create' ? 'Projekt erstellen' : \`Projekt #\${id} bearbeiten\`"
|
||||
@update:show="$emit('close')">
|
||||
<div style="width: 99%">
|
||||
<h4 class="text-center">Projektübersicht</h4>
|
||||
<tt-input label="Projektnummer" v-model="project.projectNumber" sm row disabled />
|
||||
<tt-textarea label="Um was handelt es sich?" v-model="project.description" sm row/>
|
||||
|
||||
<hr>
|
||||
<h4 class="text-center">Zeitraum</h4>
|
||||
<div style="display: grid; grid-gap: 10px; grid-template-columns: 1fr 1fr;">
|
||||
<tt-date-picker label="Startdatum" v-model="project.startDate" sm/>
|
||||
<tt-date-picker label="Enddatum" v-model="project.endDate" sm/>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h4 class="text-center">Beteiligte Personen</h4>
|
||||
<tt-select label="Personen (XINON MT)"
|
||||
:options="participantsOptions"
|
||||
v-model="project.participants"
|
||||
sm row />
|
||||
<tt-textarea label="Freitext für weitere Personen" v-model="project.additionalParticipants" sm row/>
|
||||
|
||||
<hr>
|
||||
<h4 class="text-center">Projektübersicht</h4>
|
||||
<tt-input label="Gesamtsumme des Projekts (€)" v-model.number="project.totalSum" sm row type="number"/>
|
||||
<tt-positions-manager
|
||||
ref="positionsManager"
|
||||
v-model="project.positions"
|
||||
:config="positionsConfig"
|
||||
@updateField-article="fetchArticleData"
|
||||
/>
|
||||
|
||||
<hr>
|
||||
<h4 class="text-center">Lagerort</h4>
|
||||
<tt-input label="Lagerort für dieses Projekt" v-model="project.storageLocation" sm row/>
|
||||
|
||||
<hr>
|
||||
<tt-textarea label="Notizen" v-model="project.notes" sm row/>
|
||||
</div>
|
||||
</tt-modal>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
window: window,
|
||||
participantsOptions: [
|
||||
{ value: 1, text: 'Person A' },
|
||||
{ value: 2, text: 'Person B' },
|
||||
{ value: 3, text: 'Person C' }
|
||||
// Add more participants as needed
|
||||
],
|
||||
positionsConfig: {
|
||||
fields: {
|
||||
article: {
|
||||
type: 'autocomplete',
|
||||
label: 'Artikel',
|
||||
apiUrl: '/WarehouseArticle/autoComplete',
|
||||
customFieldReference: 'WarehouseArticle',
|
||||
},
|
||||
hoursRequired: { type: 'input', label: 'Benötigte Stunden', inputType: 'number' },
|
||||
amountRequired: { type: 'input', label: 'Benötigte Menge', inputType: 'number' },
|
||||
description: { type: 'textarea', label: 'Beschreibung' }
|
||||
},
|
||||
validateForm(formData) {
|
||||
const requiredFields = ['article', 'hoursRequired', 'amountRequired'];
|
||||
for (const field of requiredFields) {
|
||||
if (!formData[field]) {
|
||||
window.notify('error', `Bitte füllen Sie ${this.positionsConfig.fields[field].label} aus`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
project: {
|
||||
projectNumber: '',
|
||||
description: '',
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
participants: [],
|
||||
additionalParticipants: '',
|
||||
totalSum: 0,
|
||||
positions: [],
|
||||
storageLocation: '',
|
||||
notes: ''
|
||||
}
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
if (this.id !== 'create') {
|
||||
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseProject/getById`, { params: { id: this.id } });
|
||||
this.project = response.data;
|
||||
} else {
|
||||
this.project.projectNumber = await this.generateProjectNumber();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async submit() {
|
||||
if (!this.project.description) return window.notify('error', 'Bitte geben Sie eine Beschreibung ein.');
|
||||
|
||||
const url = this.id === 'create'
|
||||
? `${window.TT_CONFIG["BASE_PATH"]}/WarehouseProject/create`
|
||||
: `${window.TT_CONFIG["BASE_PATH"]}/WarehouseProject/update`;
|
||||
|
||||
const response = await axios.post(url, this.project);
|
||||
|
||||
if (response.data.success) {
|
||||
window.notify('success', response.data.message ?? 'Projekt erfolgreich gespeichert');
|
||||
this.$emit('close');
|
||||
} else {
|
||||
window.notify('error', response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten');
|
||||
}
|
||||
},
|
||||
async fetchArticleData(article) {
|
||||
if (typeof article === 'number') {
|
||||
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseArticle/getById`, { params: { id: article } });
|
||||
this.$refs.positionsManager.updateField('description', response.data.description);
|
||||
}
|
||||
},
|
||||
async generateProjectNumber() {
|
||||
const currentCount = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseProject/count`);
|
||||
return `PRJ-${new Date().getFullYear()}-${String(currentCount.data + 1).padStart(4, '0')}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Vue.component('warehouse-project', {
|
||||
template: `
|
||||
<tt-card>
|
||||
<warehouse-project-modal v-if="projectModalId" :id="projectModalId" @close="projectModalId = null;$refs.table.$refs.table.refreshTable()"/>
|
||||
<button @click="projectModalId = 'create'" class="btn btn-primary">Angebot erstellen</button>
|
||||
<tt-table-crud emit-edit @edit="projectModalId = $event.id" ref="table">
|
||||
<template v-slot:expandedRow="{ row }">
|
||||
<div>
|
||||
<h5>Notizen</h5>
|
||||
<p>{{ row.notes }}</p>
|
||||
<h5>Verlauf</h5>
|
||||
<ul>
|
||||
<li v-for="entry in row.journal">{{ entry.date }} - {{ entry.description }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<tt-table-crud ref="table">
|
||||
</tt-table-crud>
|
||||
</tt-card>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
window: window,
|
||||
projectModalId: null,
|
||||
}
|
||||
return {window: window}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -79,6 +79,7 @@ Vue.component('tt-autocomplete', {
|
||||
sm: {type: Boolean, default: true},
|
||||
row: {type: Boolean, default: false},
|
||||
returnText: {type: Boolean, default: false},
|
||||
emitDisplayValue: {type: Boolean, default: false},
|
||||
}, data() {
|
||||
return {
|
||||
window,
|
||||
@@ -97,6 +98,7 @@ Vue.component('tt-autocomplete', {
|
||||
},
|
||||
methods: {
|
||||
setOldDisplayValue(newValue, oldValue) {
|
||||
if (this.emitDisplayValue && newValue) this.$emit('displayValue', newValue);
|
||||
this.oldDisplayValue = oldValue;
|
||||
},
|
||||
async updateDisplayValue(newValue, oldValue) {
|
||||
|
||||
@@ -25,9 +25,10 @@ Vue.component('tt-resolver', {
|
||||
}
|
||||
})
|
||||
|
||||
Vue.component('tt-positions-manager', {
|
||||
Vue.component('tt-positions-manager',
|
||||
{
|
||||
props: {
|
||||
value: {type: Array, required: false},
|
||||
value: {type: [Array, String], required: false},
|
||||
config: {type: Object, required: true},
|
||||
groupMode: {type: Boolean, default: false},
|
||||
},
|
||||
@@ -38,11 +39,13 @@ Vue.component('tt-positions-manager', {
|
||||
formData: {},
|
||||
groupName: '',
|
||||
selectedIndex: null,
|
||||
resolvingFields: {},
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="positions-manager">
|
||||
<template v-if="config['header']">
|
||||
<h4 class="text-center">{{ config["header"] }}</h4>
|
||||
</template>
|
||||
<div class="form-container">
|
||||
<template v-for="(field, key) in config.fields">
|
||||
<slot :name="key" v-bind:field="field" v-bind:value="formData[key]">
|
||||
@@ -57,8 +60,10 @@ Vue.component('tt-positions-manager', {
|
||||
<tt-autocomplete
|
||||
v-else-if="field.type === 'autocomplete'"
|
||||
:label="field.label"
|
||||
:emit-display-value="field.emitDisplayValue || false"
|
||||
v-model="formData[key]"
|
||||
@input="$emit('updateField-' + key, $event); window.console.log($event)"
|
||||
@input="delete formData[key + '_text']; $emit('updateField-' + key, $event)"
|
||||
@displayValue="delete formData[key];formData[key + '_text'] = $event"
|
||||
:api-url="window.TT_CONFIG['BASE_PATH'] + field.apiUrl"
|
||||
sm
|
||||
/>
|
||||
@@ -79,7 +84,7 @@ Vue.component('tt-positions-manager', {
|
||||
<tt-select
|
||||
v-else-if="field.type === 'select'"
|
||||
:label="field.label"
|
||||
@input="$emit('updateField-' + key, $event); window.console.log('updatefield-' + key, $event)"
|
||||
@input="$emit('updateField-' + key, $event)"
|
||||
sm
|
||||
v-model="formData[key]"
|
||||
:options="field.options"
|
||||
@@ -101,55 +106,38 @@ Vue.component('tt-positions-manager', {
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="field in config.fields">{{ field.label }}</th>
|
||||
<th>Actions</th>
|
||||
<th v-for="field in config['fields']">{{ field.label }}</th>
|
||||
<th style="text-align: right;padding-right: 24px">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-if="groupMode">
|
||||
<template v-for="(groupPositions, groupName) in groupedPositions">
|
||||
<tr>
|
||||
|
||||
<template v-for="(group, groupName) in positionsToRender">
|
||||
<tr v-if="groupMode">
|
||||
<td colspan="100%">
|
||||
<h4 style="text-align: center;">{{ groupName }}</h4>
|
||||
</td>
|
||||
<tr v-for="(position, index) in groupPositions" :key="groupName + index">
|
||||
</tr>
|
||||
<tr v-for="(position, index) in group" :key="groupMode ? groupName + index : index">
|
||||
<td v-for="(field, key) in config.fields">
|
||||
<tt-resolver v-if="field.customFieldReference" :reference="field.customFieldReference" :value="position[key]"/>
|
||||
<span v-else>{{ formatFieldValue(position[key], field) }}</span>
|
||||
<tt-resolver
|
||||
v-if="field.customFieldReference && position[key]"
|
||||
:reference="field.customFieldReference"
|
||||
:value="position[key]"
|
||||
/>
|
||||
<span v-else>{{ formatFieldValue(position[key] ?? position[key + '_text'], field) }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<select v-model="position._group" @change="$set(position, '_group', $event.target.value)">
|
||||
<td class="d-flex justify-content-end">
|
||||
<select v-if="groupMode" v-model="position._group" @change="$set(position, '_group', $event.target.value)">
|
||||
<option v-for="group in allGroups" :value="group">{{ group }}</option>
|
||||
</select>
|
||||
<button @click="editEntry(index)" class="btn btn-sm btn-primary">Editieren</button>
|
||||
<button @click="deleteEntry(index)" class="btn btn-sm btn-danger">Löschen</button>
|
||||
<tt-button @click="editEntry(index)" sm additional-class="btn-primary" icon="fa fa-edit"/>
|
||||
<tt-button @click="deleteEntry(index)" sm additional-class="btn-danger" icon="fa fa-trash"/>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<tr v-for="(position, index) in positions" :key="index">
|
||||
<td v-for="(field, key) in config.fields">
|
||||
<template v-if="resolvingFields[index + key] === true">
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<div class="spinner-border spinner-border-sm text-primary" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<span v-else-if="resolvingFields[index + key]">{{ resolvingFields[index + key] }}</span>
|
||||
<span v-else>{{ formatFieldValue(position[key], field) }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<select v-model="position._group" @change="position._group = $event">
|
||||
<option v-for="group in allGroups" :value="group">{{ group }}</option>
|
||||
</select>
|
||||
<button @click="editEntry(index)" class="btn btn-sm btn-primary">Editieren</button>
|
||||
<button @click="deleteEntry(index)" class="btn btn-sm btn-danger">Löschen</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -158,8 +146,20 @@ Vue.component('tt-positions-manager', {
|
||||
updateField(key, value) {
|
||||
this.$set(this.formData, key, value);
|
||||
},
|
||||
defaultValidateForm(formData) {
|
||||
console.log(this.config["validateFormOptions"], formData);
|
||||
for (const field of this.config["validateFormOptions"]) {
|
||||
if (!(formData[field.key] || formData[field.key + '_text'])) {
|
||||
window.notify('error', field.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
async saveEntry() {
|
||||
if (this.config.validateForm && !await this.config.validateForm(this.formData)) return;
|
||||
if (this.config.hasOwnProperty('validateFormOptions') && !this.defaultValidateForm(this.formData)) return;
|
||||
else 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);
|
||||
@@ -191,32 +191,19 @@ Vue.component('tt-positions-manager', {
|
||||
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.positions[i][key]) {
|
||||
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, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
//TODO: cleanup
|
||||
created() {
|
||||
if (this.config.customMethods) Object.assign(this, this.config.customMethods);
|
||||
if (this.config["customMethods"]) Object.assign(this, this.config.customMethods);
|
||||
if (!this.positions) this.positions = [];
|
||||
if (typeof this.positions === 'string') {
|
||||
try {
|
||||
this.positions = JSON.parse(this.positions);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.positions = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
groupedPositions() {
|
||||
@@ -228,17 +215,14 @@ Vue.component('tt-positions-manager', {
|
||||
}
|
||||
return groups;
|
||||
},
|
||||
positionsToRender() {
|
||||
return this.groupMode ? this.groupedPositions : { '': this.positions };
|
||||
},
|
||||
allGroups() {
|
||||
return Object.keys(this.groupedPositions);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
positions: {
|
||||
handler() {
|
||||
this.resolveFields().then();
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
value: {
|
||||
handler() {
|
||||
this.positions = this.value;
|
||||
@@ -246,4 +230,4 @@ Vue.component('tt-positions-manager', {
|
||||
deep: true
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user