'id', 'text' => 'ID', 'modal' => false, 'table' => false],
['key' => 'orderNumber', 'text' => 'Bestellnummer', 'required' => true, 'modal' => false],
['key' => 'distributorId', 'text' => 'Lieferant', 'required' => false, 'modal' => ['type' => 'select', 'items' => []], 'table' => ['filter' => 'select']],
['key' => 'delAddrCity', 'text' => 'Stadt', 'required' => true, 'modal' => false, 'table' => false],
['key' => 'delAddrEMail', 'text' => 'E-Mail', 'required' => true, 'modal' => false, 'table' => false],
['key' => 'delAddrLine', 'text' => 'Adresse', 'required' => true, 'modal' => false, 'table' => false],
['key' => 'delAddrName', 'text' => 'Name', 'required' => true, 'modal' => false, 'table' => false],
['key' => 'delAddrPLZ', 'text' => 'PLZ', 'required' => true, 'modal' => false, 'table' => false],
['key' => 'editor', 'text' => 'Bearbeiter', 'required' => true, 'modal' => ['type' => 'select'], 'table' => ['filter' => 'select']],
['key' => 'note', 'text' => 'Interne 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' => [
['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']],
['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
protected array $infoMessages = ['create' => 'Bestellung wurde erfolgreich erstellt.',
'update' => 'Bestellung wurde aktualisiert.',
'delete' => 'Bestellung wurde gelöscht',
'noChanges' => 'Keine Änderungen',];
protected array $additionalActions = [['key' => 'openpdf', 'title' => 'PDF öffnen', 'class' => 'fas fa-file-pdf', 'color' => 'primary']];
protected function prepareCrudConfig(): void {
$editorColumnIndex = array_search('editor', array_column($this->columns, 'key'));
$this->columns[$editorColumnIndex]['modal']['items'] = array_map(function ($user) {
return ['value' => intval($user->id), 'text' => $user->name];
}, UserModel::search(['employee' => true]));
$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 {
$this->postData['orderNumber'] = 'PO' . date('Y') . '-' . str_pad(WarehouseOrderModel::count(['create' => ['from' => strtotime(date('Y-01-01'))]]) + 1, 4, '0', STR_PAD_LEFT);
return true;
}
protected function getArticleDistributorDataAction() {
$articleId = $this->request->articleId;
if ($this->request->allDistributor === 'true') self::returnJson(array_map(fn($d) => ['id' => $d->id,
'name' => $d->name], WarehouseDistributorModel::getAll()));
else if (!empty($articleId)) self::returnJson(array_map(fn($d) => ['id' => $d->distributorId,
'name' => WarehouseDistributorModel::get($d->distributorId)->name,
'purchasePrice' => $d->purchasePrice,
'externalArticleNumber' => $d->externalArticleNumber], WarehouseArticleDistributorModel::getAll(['articleId' => $articleId])));
else self::returnJson([]);
}
protected function getByIdParse(array $order): array {
$order['positions'] = json_decode($order['positions'], true);
foreach ($order['positions'] as &$position) {
$position['distributorName'] = WarehouseDistributorModel::get($position['distributorId'])->name;
$position['articleName'] = $position['article_text'] ?? WarehouseArticleModel::get($position['article'])->title;
}
return $order;
}
protected function createPDFAction($returnFilename = false) {
$order = (array) WarehouseOrderModel::get($this->request->id);
$order['positions'] = json_decode($order['positions'], true);
// 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.']);
}
// 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";
// Enhance positions
foreach ($order['positions'] as &$position) {
$article = WarehouseArticleModel::get($position['article']);
$position += [
'distributorName' => $distributor->name,
'articleName' => $article->title,
'articleDescription' => $article->description
];
}
// 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
]
];
// Generate HTML templates
$replacements = [
'basedir' => BASEDIR,
'externalReference' => !empty($order['extReference']) ?
"Ext. Ref.: ".$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, $this->generateTemplate('PDF_HEADER', $replacements));
$footerFile = BASEDIR . "/var/temp/order_footer-" . date("U") . "-" . rand(1000, 9999) . ".html";
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");
if ($returnFilename === true) return $filename;
// Output PDF
header('Content-Type: application/pdf');
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;
$texts = [
'EN' => [
'taxFree' => 'Please provide tax-free delivery according to § 6a UStG (Austrian Sales tax law).
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.',
],
'DE' => [
'taxFree' => 'Bitte um steuerfreie Lieferung gemäß § 6a UStG.
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.',
]];
$countryText = (new Country(WarehouseDistributorModel::get($order['distributorId'])->countryId))->name;
$isGermanSpeaking = in_array($countryText, ['Österreich', 'Deutschland', 'Schweiz']);
$appendToBody = '';
if ($isGermanSpeaking && $countryText !== "Österreich") {
$appendToBody .= $texts['DE']['taxFree'] . '
';
} else {
$appendToBody .= $texts['EN']['taxFree'] . '
';
}
$appendToBody .= $texts[$isGermanSpeaking ? 'DE' : 'EN']['orderConfirmation'];
if ($order['sendShippingNote'] > 0) {
$appendToBody .= '
' . $texts[$isGermanSpeaking ? 'DE' : 'EN']['sendShippingNote'];
}
$appendToBody .= $isGermanSpeaking ? '
Die Bestellung liegt als PDF-Anhang bei.' : '
The order is attached as a PDF file.';
// 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 = "
Bestellnummer: $orderNumber
Datum: " . date('d.m.Y H:i', $order['create']) . "
Lieferant: $distributorName