added new warehouseorder features

This commit is contained in:
Luca Haid
2025-03-07 08:04:36 +01:00
parent d24c1cc5dc
commit 7e3b1af54a
11 changed files with 460 additions and 119 deletions

View File

@@ -4,6 +4,7 @@
* @var WarehouseOrderModel $order
* @var Array $positions
* @var Array $textElements
* @var string $distributorCountryText
*/
$this->setReturnValue(['filename' => $order["id"] . ".pdf"]);
@@ -16,6 +17,7 @@ $texts = [
'total' => 'Total',
'taxFree' => 'Please provide tax-free delivery according to § 6a UStG (Austrian Sales tax law).<br>Our VAT ID number: ATU68711968. Delivery to Austria.',
'orderConfirmation' => 'Please send the order confirmation to einkauf@xinon.at',
'sendShippingNote' => 'Please send the delivery note with our shipping note and neutral packaging.',
'table' => [
'pos' => 'POS',
'article' => 'Article',
@@ -32,6 +34,7 @@ $texts = [
'total' => 'Gesamt',
'taxFree' => 'Bitte um steuerfreie Lieferung gemäß § 6a UStG.<br> Unsere UID-Nr.: ATU68711968. Lieferung nach Österreich.',
'orderConfirmation' => 'Wir bitten um Zusendung der Auftragsbestätigung für die Bestellung an einkauf@xinon.at.',
'sendShippingNote' => 'Bitte senden Sie den Lieferschein mit unserem Versandschein und neutraler Verpackung.',
'table' => [
'pos' => 'POS',
'article' => 'Artikel',
@@ -182,10 +185,17 @@ $text = $texts[in_array($distributorCountryText, ["Österreich", "Deutschland",
<div>
<?php if ($distributorCountryText !== "Österreich"): ?>
<?= $text['taxFree'] ?>
<br><?= $text['taxFree'] ?> <br><br>
<?php endif; ?>
<?= $text['orderConfirmation'] ?>
<?php if ($order['sendShippingNote'] > 0): ?>
<br><br>
<?= $text['sendShippingNote'] ?>
<?php endif; ?>
</div>
</body>
</html>

View File

@@ -89,85 +89,210 @@ class WarehouseOrderController extends TTCrud {
return $order;
}
protected function createPDFAction() {
protected function createPDFAction($returnFilename = false) {
$order = (array) WarehouseOrderModel::get($this->request->id);
$order['positions'] = json_decode($order['positions'], true);
// check if all positions have the same distributor
$distributorId = $order['positions'][0]['distributorId'];
foreach ($order['positions'] as $key => $position) {
if ($position['distributorId'] !== $distributorId) {
self::returnJson(['error' => 'Die Bestellung enthält Positionen von verschiedenen Lieferanten.']);
}
// we need to get the article name and distributor name for the pdf
$position['distributorName'] = WarehouseDistributorModel::get($position['distributorId'])->name;
$position['articleName'] = WarehouseArticleModel::get($position['article'])->title;
$position['articleDescription'] = WarehouseArticleModel::get($position['article'])->description;
$order['positions'][$key] = $position;
// Validate distributor consistency
$distributorIds = array_column($order['positions'], 'distributorId');
if (count(array_unique($distributorIds)) > 1) {
self::returnJson(['error' => 'Die Bestellung enthält Positionen von verschiedenen Lieferanten.']);
}
$pdf_vars = ['order' => $order,
'distributor' => WarehouseDistributorModel::get($distributorId),
'distributorCountryText' => (new Country(WarehouseDistributorModel::get($distributorId)->countryId))->name,
"bank_iban" => TT_INVOICE_BANK_IBAN,
"bank_bic" => TT_INVOICE_BANK_BIC,
"bank_bank" => TT_INVOICE_BANK_BANK,
"bank_owner" => TT_INVOICE_BANK_OWNER];
// Prepare data
$distributor = WarehouseDistributorModel::get($distributorIds[0]);
$countryText = (new Country($distributor->countryId))->name;
$isGermanSpeaking = in_array($countryText, ['Österreich', 'Deutschland', 'Schweiz']);
$isDefaultAddress = $order['delAddrLine'] === "Fladnitz im Raabtal 150";
$countryText = (new Country(WarehouseDistributorModel::get($distributorId)->countryId))->name;
$shouldGenerateEnglisch = !in_array($countryText, ['Österreich', 'Deutschland', 'Schweiz']);
// Enhance positions
foreach ($order['positions'] as &$position) {
$article = WarehouseArticleModel::get($position['article']);
$position += [
'distributorName' => $distributor->name,
'articleName' => $article->title,
'articleDescription' => $article->description
];
}
$headerHtml = file_get_contents(BASEDIR . "/Layout/default/WarehouseOrder/PDF_HEADER.html");
$headerHtml = str_replace("{{ basedir }}", BASEDIR, $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);
$headerHtml = str_replace("{{ addressLine_3 }}", WarehouseDistributorModel::get($distributorId)->plz . " " . WarehouseDistributorModel::get($distributorId)->city, $headerHtml);
$headerHtml = str_replace("{{ addressLine_4 }}", $countryText, $headerHtml);
// Prepare PDF variables
$pdfVars = [
'order' => $order,
'distributor' => $distributor,
'distributorCountryText' => $countryText,
'bank' => [
'iban' => TT_INVOICE_BANK_IBAN,
'bic' => TT_INVOICE_BANK_BIC,
'bank' => TT_INVOICE_BANK_BANK,
'owner' => TT_INVOICE_BANK_OWNER
]
];
$headerHtml = str_replace("{{ billingAddressLine_header }}", $shouldGenerateEnglisch ? "Billing Address" : "Rechnungsadresse", $headerHtml);
$headerHtml = str_replace("{{ billingAddressLine_1 }}", "Xinon GmbH", $headerHtml);
$headerHtml = str_replace("{{ billingAddressLine_2 }}", "Fladnitz im Raabtal 150", $headerHtml);
$headerHtml = str_replace("{{ billingAddressLine_3 }}", "A-8322 Studenzen", $headerHtml);
$headerHtml = str_replace("{{ billingAddressLine_4 }}", "UID: ATU68711968", $headerHtml);
$headerHtml = str_replace("{{ billingAddressLine_5 }}", "EORI-Nr.: ATEOS1000085074", $headerHtml);
$headerHtml = str_replace("{{ billingAddressLine_6 }}", "<strong>Referenz: ". $order["orderNumber"] . "</strong>", $headerHtml);
$chk = $order['delAddrLine'] == "Fladnitz im Raabtal 150";
$headerHtml = str_replace("{{ shippingAddressLine_header }}", $chk ? "" : ($shouldGenerateEnglisch ? "Shipping Address" : "Lieferadresse"), $headerHtml);
$headerHtml = str_replace("{{ shippingAddressLine_1 }}", $chk ? "" : $order['delAddrName'], $headerHtml);
$headerHtml = str_replace("{{ shippingAddressLine_2 }}", $chk ? "" : $order['delAddrLine'], $headerHtml);
$headerHtml = str_replace("{{ shippingAddressLine_3 }}", $chk ? "" : $order['delAddrPLZ'] . " " . $order['delAddrCity'], $headerHtml);
$headerHtml = str_replace("{{ shippingAddressLine_4 }}", $chk ? "" : $order['delAddrEMail'], $headerHtml);
// Generate HTML templates
$replacements = [
'basedir' => BASEDIR,
'externalReference' => !empty($order['extReference']) ?
"<strong>Ext. Ref.:</strong> ".$order['extReference'] : "",
'isGermanSpeaking' => $isGermanSpeaking, // Required for conditional logic
'isDefaultAddress' => $isDefaultAddress, // Needed for address display rules
'distributor' => $distributor,
'countryText' => $countryText,
'order' => $order, // Full order data access
'orderNumber' => $order['orderNumber'],
'bank_iban' => TT_INVOICE_BANK_IBAN_FORMATTED, // Direct footer access
'bank_bic' => TT_INVOICE_BANK_BIC,
'bank_bank' => TT_INVOICE_BANK_BANK,
'bank_owner' => TT_INVOICE_BANK_OWNER
];
// put header and footer into temp files
$headerFile = BASEDIR . "/var/temp/order_header-" . date("U") . "-" . rand(1000, 9999) . ".html";
file_put_contents($headerFile, $headerHtml);
$footerHtml = file_get_contents(BASEDIR . "/Layout/default/WarehouseOrder/PDF_FOOTER.html");
$footerHtml = str_replace("{{ bank_iban }}", TT_INVOICE_BANK_IBAN_FORMATTED, $footerHtml);
$footerHtml = str_replace("{{ bank_bic }}", TT_INVOICE_BANK_BIC, $footerHtml);
$footerHtml = str_replace("{{ bank_bank }}", TT_INVOICE_BANK_BANK, $footerHtml);
$footerHtml = str_replace("{{ bank_owner }}", TT_INVOICE_BANK_OWNER, $footerHtml);
file_put_contents($headerFile, $this->generateTemplate('PDF_HEADER', $replacements));
$footerFile = BASEDIR . "/var/temp/order_footer-" . date("U") . "-" . rand(1000, 9999) . ".html";
file_put_contents($footerFile, $footerHtml);
file_put_contents($footerFile, $this->generateTemplate('PDF_FOOTER', $replacements));
// Generate PDF
$pdf = new PdfForm("WarehouseOrder/PDF_MAIN", $pdfVars);
$filename = $pdf->render("--header-html $headerFile --footer-html $footerFile");
$pdf = new PdfForm("WarehouseOrder/PDF_MAIN", $pdf_vars);
$wkhtmltopdfArgs = "--header-html $headerFile --footer-html $footerFile";
$filename = $pdf->render($wkhtmltopdfArgs);
if ($returnFilename === true) return $filename;
// return the pdf and die so the client sees the pdf not the filename
// Output PDF
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Disposition: inline; filename="' . basename($filename) . '"');
readfile($filename);
exit;
}
protected function sendEmailAction() {
$pdfFilename = $this->createPDFAction(true);
// Get order details for email content
$order = (array) WarehouseOrderModel::get($this->request->id);
$orderNumber = $order['orderNumber'];
$order['positions'] = json_decode($order['positions'], true);
$distributorName = WarehouseDistributorModel::get($order['distributorId'])->name;
// Instantiate PHPMailer
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
try {
// Server settings
$mail->isSMTP();
$mail->Host = TT_WAREHOUSE_ORDER_SMTP_HOST;
$mail->SMTPAuth = true;
$mail->Username = TT_WAREHOUSE_ORDER_SMTP_USER;
$mail->Password = TT_WAREHOUSE_ORDER_SMTP_PASS;
$mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
// Recipients
$mail->setFrom('einkauf@xinon.at', 'XINON Einkauf');
$mail->addAddress($this->request->email, $this->request->email);
// $mail->addCC('cc@example.com'); // Uncomment for CC
// Content
$mail->isHTML(true);
$mail->Subject = "Neue Bestellung #$orderNumber";
$mail->Body = "<!DOCTYPE html>
<html>
<head>
<title>XINON E-Mail Template</title>
<meta charset='utf-8'/>
</head>
<body>
<h2>Neue Bestellung</h2>
<p>
Bestellnummer: <strong>$orderNumber</strong><br>
Datum: " . date('d.m.Y H:i', $order['create']) . "<br>
Lieferant: <strong>$distributorName</strong>
</p>
<p>Die Bestellung liegt als PDF-Anhang bei.</p>
</body>
</html>";
// Attach PDF
$mail->addAttachment($pdfFilename, "Bestellung_$orderNumber.pdf");
if ($order['sendShippingNote'] > 0) {
$shippingNoteController = new WarehouseShippingNoteController();
$shippingNoteFilename = $shippingNoteController->createPDFAction(true, $this->request->shippingNote);
$mail->addAttachment($shippingNoteFilename, "Lieferschein_$orderNumber.pdf");
}
$mail->send();
$log = [
"table" => "WarehouseOrder",
"rowId" => $this->request->id,
"type" => "noChanges",
"message" => "Bestellung per Mail versendet",
"createBy" => intval($this->user->id),
"create" => time()
];
WarehouseLogModel::create($log);
// Cleanup temporary files
if (file_exists($pdfFilename)) {
unlink($pdfFilename);
}
self::returnJson(['success' => true, 'message' => 'E-Mail erfolgreich versendet']);
exit;
} catch (Exception $e) {
http_response_code(500);
self::returnJson([
'success' => false,
'message' => "E-Mail konnte nicht versendet werden: {$mail->ErrorInfo}",
]);
exit;
}
}
private function generateTemplate(string $type, array $data): string {
$template = file_get_contents(BASEDIR . "/Layout/default/WarehouseOrder/{$type}.html");
$replacements = [
'{{ basedir }}' => BASEDIR,
'{{ externalReference }}' => !empty($data['order']['extReference']) ?
"<strong>Ext. Ref.:</strong> ".$data['order']['extReference'] : "",
'{{ addressLine_header }}' => $data['isGermanSpeaking'] ? 'Lieferant' : 'Supplier',
'{{ addressLine_1 }}' => $data['distributor']->name,
'{{ addressLine_2 }}' => $data['distributor']->address,
'{{ addressLine_3 }}' => $data['distributor']->plz . ' ' . $data['distributor']->city,
'{{ addressLine_4 }}' => $data['countryText'],
'{{ billingAddressLine_header }}' => $this->getBillingHeader($data),
'{{ billingAddressLine_1 }}' => 'Xinon GmbH',
'{{ billingAddressLine_2 }}' => 'Fladnitz im Raabtal 150',
'{{ billingAddressLine_3 }}' => 'A-8322 Studenzen',
'{{ billingAddressLine_4 }}' => 'UID: ATU68711968',
'{{ billingAddressLine_5 }}' => 'EORI-Nr.: ATEOS1000085074',
'{{ billingAddressLine_6 }}' => "<strong>Referenz: ".$data['order']['orderNumber']."</strong>",
'{{ shippingAddressLine_header }}' => $data['isDefaultAddress'] ? '' :
($data['isGermanSpeaking'] ? 'Lieferadresse' : 'Shipping Address'),
'{{ shippingAddressLine_1 }}' => $data['isDefaultAddress'] ? '' : $data['order']['delAddrName'],
'{{ shippingAddressLine_2 }}' => $data['isDefaultAddress'] ? '' : $data['order']['delAddrLine'],
'{{ shippingAddressLine_3 }}' => $data['isDefaultAddress'] ? '' :
$data['order']['delAddrPLZ'] . ' ' . $data['order']['delAddrCity'],
'{{ shippingAddressLine_4 }}' => $data['isDefaultAddress'] ? '' : $data['order']['delAddrEMail'],
'{{ bank_iban }}' => TT_INVOICE_BANK_IBAN_FORMATTED,
'{{ bank_bic }}' => TT_INVOICE_BANK_BIC,
'{{ bank_bank }}' => TT_INVOICE_BANK_BANK,
'{{ bank_owner }}' => TT_INVOICE_BANK_OWNER
];
return strtr($template, $replacements);
}
private function getBillingHeader(array $data): string {
$base = $data['isGermanSpeaking'] ? 'Rechnungsadresse' : 'Billing Address';
return $data['isDefaultAddress'] ? $base . ' / ' .
($data['isGermanSpeaking'] ? 'Lieferadresse' : 'Shipping Address'):$base;
}
protected function getLogAction() {
$orderId = $this->request->orderId;
if (empty($orderId)) {
@@ -198,7 +323,7 @@ class WarehouseOrderController extends TTCrud {
];
try {
$order = WarehouseOrderModel::get($log['orderId']);
$order = WarehouseOrderModel::get($log['rowId']);
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'];

View File

@@ -33,6 +33,7 @@ class WarehouseOrderModel extends TTCrudBaseModel {
public int $editor;
public ?string $note;
public string $positions;
public ?int $sendShippingNote;
public int $create;
public int $createBy;
}

View File

@@ -8,6 +8,7 @@ class WarehouseOrderRequestController extends TTCrud {
//@formatter:off
protected array $columns = [
['key' => 'id', 'text' => 'Bestellnummer', 'table' => ['filter' => false], 'modal' => false],
['key' => 'addressId', 'text' => 'Kunde', 'required' => false, 'type' => 'autocomplete', 'table' => ['class' => 'text-nowrap', 'filter' => 'autocomplete'], 'modal' => ['apiUrl' => 'Address/api?do=findAddress', 'items' => '/Address/Api?do=findAddress', 'type' => 'autocomplete']],
['key' => 'purpose', 'text' => 'Verwendungszweck', 'required' => true],
['key' => 'positions', 'text' => 'Positionen', 'required' => true, 'modal' => ['type' => 'positions-manager', 'config' => [
'header' => 'Positionen',
@@ -35,11 +36,16 @@ class WarehouseOrderRequestController extends TTCrud {
['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' => 'done', 'text' => 'Erledigt', 'modal' => ['visible' => false, 'type' => 'icon-select', 'items' => [
['value' => 0, 'text' => 'Bestellwunsch nicht erledigt', 'icon' => 'fa-regular fa-circle-check text-success'],
['value' => 1, 'text' => 'Bestellwunsch erledigt', '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 $defaultOrder = ['key' => 'create', 'order' => 'DESC'];
protected array $additionalActions = [
['key' => 'openHistory', 'title' => 'Historie', 'class' => 'fas fa-history text-primary'],
@@ -59,37 +65,6 @@ class WarehouseOrderRequestController extends TTCrud {
return true;
}
protected function afterCreate($postData): void {
return;
//TODO: this should be working again
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")
->setBody(<<<BODY
Hallo,
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 = filter_var($this->request->id, FILTER_VALIDATE_INT);
$cancel = filter_var($this->request->cancel, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 1]]);
@@ -101,6 +76,17 @@ BODY
self::returnJson(['success' => true]);
}
protected function doneAction() {
$id = filter_var($this->request->id, FILTER_VALIDATE_INT);
$done = filter_var($this->request->done, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 1]]);
if (!$id || $done === false) self::returnJson(['error' => 'Ungültige Anfrage']);
if (!(WarehouseOrderRequestModel::get($id))) self::returnJson(['error' => 'Bestellwunsch nicht gefunden']);
WarehouseOrderRequestModel::update(['id' => $id, 'done' => $done]);
self::returnJson(['success' => true]);
}
protected function createNewLogAction() {
$postData = json_decode(file_get_contents('php://input'), true);

View File

@@ -2,11 +2,13 @@
class WarehouseOrderRequestModel extends TTCrudBaseModel {
public int $id;
public ?int $addressId;
public string $purpose;
public string $positions;
public ?string $note;
public ?string $linkedOrderIds;
public ?int $cancelled;
public ?int $done;
public int $create;
public int $createBy;
}

View File

@@ -223,8 +223,8 @@ class WarehouseShippingNoteController extends TTCrud {
}
}
protected function createPDFAction() {
$id = $this->request->id;
public function createPDFAction($returnFilename = false, $idOverride = null) {
$id = $idOverride ?? $this->request->id;
if (strlen($id) < 1) {
http_response_code(500);
self::returnJson(['success' => false, 'message' => 'Lieferschein wurde nicht gefunden']);
@@ -376,10 +376,13 @@ class WarehouseShippingNoteController extends TTCrud {
$wkhtmltopdfArgs = "--header-html $headerFile --footer-html $footerFile";
$filename = $pdf->render($wkhtmltopdfArgs);
if ($returnFilename === true) return $filename;
// return the pdf and die so the client sees the pdf not the filename
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
readfile($filename);
die();
}
// TODO: either move this to UserController or make it better

View File

@@ -5,6 +5,7 @@
"textalk/websocket": "^1.6",
"chillerlan/php-qrcode": "^4",
"phpseclib/phpseclib": "^3.0",
"stomp-php/stomp-php": "^5"
"stomp-php/stomp-php": "^5",
"phpmailer/phpmailer": "^6.9"
}
}

View File

@@ -0,0 +1,37 @@
<?php declare(strict_types = 1);
use Phinx\Migration\AbstractMigration;
final class WarehouseModify14 extends AbstractMigration {
public function up(): void {
if ($this->getEnvironment() == "thetool") {
// Modify WarehouseOrderRequest
$orderRequestTable = $this->table("WarehouseOrderRequest");
$orderRequestTable
->addColumn("done", "integer", ['default' => 0])
->addColumn("addressId", "integer", ['default' => null, 'null' => true])
->save();
// Modify WarehouseOrder
$orderTable = $this->table("WarehouseOrder");
$orderTable
->addColumn("sendShippingNote", "integer", ['default' => 0])
->save();
}
}
public function down(): void {
if ($this->getEnvironment() == "thetool") {
$orderRequestTable = $this->table("WarehouseOrderRequest");
$orderRequestTable
->removeColumn("done")
->removeColumn("addressId")
->save();
$orderTable = $this->table("WarehouseOrder");
$orderTable
->removeColumn("sendShippingNote")
->save();
}
}
}

View File

@@ -127,3 +127,56 @@
margin-bottom: 0.5rem;
}
.ios-switch-wrapper {
position: relative;
display: inline-block;
width: 50px;
height: 28px;
}
.ios-switch-wrapper.disabled {
opacity: 0.6;
}
.ios-switch-wrapper input {
opacity: 0;
width: 0;
height: 0;
}
.ios-switch-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 34px;
}
.ios-switch-slider:before {
position: absolute;
content: "";
height: 20px;
width: 20px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .ios-switch-slider {
background-color: #4cd964;
}
input:checked + .ios-switch-slider:before {
transform: translateX(22px);
}
input:disabled + .ios-switch-slider {
cursor: not-allowed;
}

View File

@@ -5,11 +5,15 @@ Vue.component('change-status-modal', {
},
data() {
return {
order: null,
newStatus: 'noChanges',
note: '',
file: null,
uploadedFiles: []
window: window,
order: null,
newStatus: 'noChanges',
note: '',
file: null,
uploadedFiles: [],
sendEmail: false,
sendEmailViewedPDF: false,
sendEmailMail: '',
};
},
async mounted() {
@@ -101,12 +105,25 @@ Vue.component('change-status-modal', {
},
removeFile: index => this.uploadedFiles.splice(index, 1),
async submit() {
if (this.newStatus === 'accepted' && this.sendEmail && !this.sendEmailMail) {
window.notify('error', 'Bitte geben Sie eine E-Mail-Adresse ein');
return;
} else if (this.newStatus === 'accepted' && this.sendEmail && !this.sendEmailViewedPDF) {
window.notify('error', 'Bitte öffnen Sie das PDF bevor Sie die E-Mail senden');
return;
} else if (this.newStatus === 'accepted' && this.sendEmail && this.sendEmailMail) {
if (this.order.sendShippingNote > 0) {
const shippingNoteId = await this.generateShippingNote();
await this.submitEmail(shippingNoteId);
} else await this.submitEmail();
}
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)
fileIds: JSON.stringify(fileIds),
});
if (response.data.success) {
@@ -116,6 +133,39 @@ Vue.component('change-status-modal', {
window.notify('error',
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten');
}
},
async generateShippingNote() {
const positions = this.order.positions.map(position => ({
article: position.article,
amount: position.amount,
price: position.buyPrice
}));
const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseShippingNote/create`,
{
"billingAddressId": this.order.sendShippingNote,
"deliveryAddressName": this.order.delAddrName,
"deliveryAddressLine": this.order.delAddrLine,
"deliveryAddressPLZ": this.order.delAddrPLZ,
"deliveryAddressCity": this.order.delAddrCity,
"deliveryAddressEMail": this.order.delAddrEMail,
"textElements": [],
"hoursEntries": [],
"positions": positions,
"note": "Bestellung #" + this.order.orderNumber,
"status": "new"
});
return response.data.id;
},
async submitEmail(shippingNote = null) {
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/sendEmail?id=${this.order.id}&email=${this.sendEmailMail}${shippingNote ? '&shippingNote=' + shippingNote : ''}`);
if (response.data.success) {
window.notify('success', response.data.message);
} else {
window.notify('error', response.data.message || 'Ein Fehler ist aufgetreten');
}
}
},
template: `
@@ -163,6 +213,32 @@ Vue.component('change-status-modal', {
</div>
</div>
<div v-if="newStatus === 'accepted'">
<h4>E-Mail verschicken?</h4>
<tt-button
additional-class="btn-outline-primary"
icon="fa fa-file-pdf text-danger"
text="PDF anzeigen"
@click="sendEmailViewedPDF = true;window.open(window.TT_CONFIG.BASE_PATH + '/WarehouseOrder/createPDF?id=' + order.id)"
></tt-button>
<div class="mt-2 d-flex align-items-center">
<div class="mr-2">E-Mail senden:</div>
<label class="ios-switch-wrapper" :class="{'disabled': !sendEmailViewedPDF}">
<input
type="checkbox"
v-model="sendEmail"
:disabled="!sendEmailViewedPDF"
>
<span class="ios-switch-slider"></span>
</label>
<div v-if="!sendEmailViewedPDF" class="text-muted ml-2 text-danger">
Bitte erst PDF ansehen
</div>
</div>
<tt-input v-if="sendEmail" label="E-Mail-Adresse" v-model="sendEmailMail" sm row/>
</div>
</template>
</tt-modal>
@@ -210,6 +286,24 @@ Vue.component('warehouse-order-modal', {
<tt-input label="E-Mail" v-model="order.delAddrEMail" sm/>
</div>
<template v-if="id === 'create' && order.delAddrLine !== 'Fladnitz im Raabtal 150'">
<div class="mt-2 d-flex align-items-center">
<div class="mr-2">Lieferschein erstellen und anhängen?</div>
<label class="ios-switch-wrapper">
<input type="checkbox" v-model="showSendShippingNote">
<span class="ios-switch-slider"></span>
</label>
</div>
<template v-if="showSendShippingNote">
<hr>
<tt-autocomplete v-model="order.sendShippingNote"
:api-url="window.TT_CONFIG['BASE_PATH'] + '/Address/Api?do=findAddress&fibu_primary_account=1'"
label="Rechnungsadresse"
sm
row/>
</template>
</template>
<hr>
<tt-textarea label="Notiz" v-model="order.note" sm row/>
</div>
@@ -218,8 +312,9 @@ Vue.component('warehouse-order-modal', {
data() {
return {
window: window,
positionsConfig: {
window: window,
showSendShippingNote: false,
positionsConfig: {
customOrdering: 'distributorId',
fields: {
article: {
@@ -252,16 +347,17 @@ Vue.component('warehouse-order-modal', {
return true;
},
},
order: {
extReference: '',
delAddrName: 'XINON GmbH',
delAddrLine: 'Fladnitz im Raabtal 150',
delAddrPLZ: '8322',
delAddrCity: 'Studenzen',
delAddrEMail: 'einkauf@xinon.at',
note: '',
editor: window.TT_CONFIG['USER_ID'],
positions: [],
order: {
extReference: '',
delAddrName: 'XINON GmbH',
delAddrLine: 'Fladnitz im Raabtal 150',
delAddrPLZ: '8322',
delAddrCity: 'Studenzen',
delAddrEMail: 'einkauf@xinon.at',
note: '',
editor: window.TT_CONFIG['USER_ID'],
sendShippingNote: false,
positions: [],
}
}
},
@@ -365,20 +461,20 @@ Vue.component('warehouse-order-modal', {
});
Vue.component('tt-file', {
props: ['id'],
data: () => ({file: null}),
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>
<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>
`
`
})

View File

@@ -19,7 +19,19 @@ window.TT_CONFIG["CRUD_CONFIG"]["additionalActions"] = [
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,
}
},
{
key: "doneOrder",
title: "Bestellwunsch erledigt",
class: "fas fa-check text-success",
condition: (row) => window.TT_CONFIG['WAREHOUSE_ADMIN'] === '1' && row.done === 0,
},
{
key: "undoneOrder",
title: "Bestellwunsch wieder offen",
class: "fas fa-times text-danger",
condition: (row) => window.TT_CONFIG['WAREHOUSE_ADMIN'] === '1' && row.done === 1,
},
]
Vue.component('add-log-modal', {
@@ -188,6 +200,8 @@ Vue.component('warehouse-order-request', {
<tt-table-crud @openHistory="openHistory"
@cancelRequest="cancelRequest"
@uncancelRequest="uncancelRequest"
@doneOrder="doneOrder"
@undoneOrder="undoneOrder"
@createLog="createLog"
@createOrder="createOrder"
ref="crud">
@@ -232,6 +246,19 @@ Vue.component('warehouse-order-request', {
res.data.message || (res.data.success ? 'Erfolgreich aktualisiert' : 'Fehler beim aktualisieren'));
if (res.data.success) this.$refs.crud.$refs.table.refreshTable();
},
async doneRequest(row, done) {
if (!confirm('Bestellwunsch wirklich als erledigt markieren?')) return;
const res = await axios.get(`${window.TT_CONFIG.BASE_PATH}/WarehouseOrderRequest/done?id=${row.id}&done=${done}`);
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 doneOrder(row) {
this.doneRequest(row, '1');
},
async undoneOrder(row) {
this.doneRequest(row, '0');
},
async createLog(row) {
this.addLogModal = true;
this.addLogModalId = row.id;