diff --git a/Layout/default/Mailtemplate/Index.php b/Layout/default/Mailtemplate/Index.php index 6e9b2cb24..01d51ff3e 100644 --- a/Layout/default/Mailtemplate/Index.php +++ b/Layout/default/Mailtemplate/Index.php @@ -41,6 +41,15 @@ +
| Typ | Code | Name | Beschreibung | @@ -77,13 +87,14 @@|||||||
|---|---|---|---|---|---|---|---|---|---|---|
| ">"> | =$template->code?> |
=$template->name?> | =$template->description?> | =$template->subject?> | =count($template->files)?> | -=date("d.m.Y H:i",$template->edit)?> (=$template->editor->name?>) | -=date("d.m.Y H:i",$template->create)?> (=$template->creator->name?>) | +=date("d.m.Y H:i",$template->create)?> (=$template->editor->name?>) | +=date("d.m.Y H:i",$template->edit)?> (=$template->creator->name?>) | $template->id])?>"> $template->id])?>" class="text-danger" onclick="if(!confirm('Emailtemplate wirklich löschen?')) return false;" title="Emailtemplate Löschen"> diff --git a/application/Api/v1/PreorderApicontroller.php b/application/Api/v1/PreorderApicontroller.php index 02725889e..8197f78a1 100644 --- a/application/Api/v1/PreorderApicontroller.php +++ b/application/Api/v1/PreorderApicontroller.php @@ -1047,12 +1047,13 @@ class PreorderApicontroller extends mfBaseApicontroller { } $return['status'] = $preorder->status->getApiArray(); foreach($preorder->statusflags as $sflag) { - $return['status']['flags'][$sflag->code] = [ + $return['status']['flags'][] = [ "code" => (int)$sflag->code, "text" => $sflag->name, "value" => ($sflag->value->value == 1) ]; } + //var_dump($return['status']);exit; if($addon_data) { $return["additionalData"] = $addon_data; } diff --git a/application/Emailnotification/Emailnotification.php b/application/Emailnotification/Emailnotification.php index 1fa645924..491567bf3 100644 --- a/application/Emailnotification/Emailnotification.php +++ b/application/Emailnotification/Emailnotification.php @@ -122,6 +122,18 @@ class Emailnotification { //$log->debug(print_r($headers, true)); $mail =& Mail::factory('mail', ["-f ".$this->email_from]); + /* + * PHP 8's mail() function now outputs proper line endings in headers (CRLF instead of LF). + * Mail_mail < 2.0 would detect a unix system and change the headers line ending to LF. + * On Mail submission to Exim (via sendmail), mail() adds its own headers first with CRLF line ending, + * making Exim treat every subsequent header's LF as an invalid line ending, adding a whitespace after the LF, + * making all our custom headers into one long header. + * + * See: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-message_processing.html#SECTlineendings + * + * So we force CRLF line endings for headers here. + */ + $mail->sep = "\r\n"; $mail->send($this->email_to, $headers, $body); } diff --git a/application/Invoice/InvoiceController.php b/application/Invoice/InvoiceController.php index 93db2e782..b3e44c135 100644 --- a/application/Invoice/InvoiceController.php +++ b/application/Invoice/InvoiceController.php @@ -837,7 +837,7 @@ class InvoiceController extends mfBaseController { public function createPDFs($limit = false) { $invoice_path_base = MFUPLOAD_FILE_SAVE_PATH."/".TT_INVOICE_SAVE_SUBFOLDER; $created = 0; - foreach(InvoiceModel::getAll() as $invoice) { + foreach(InvoiceModel::search(["invoice_file" => false]) as $invoice) { if($limit && $created >= $limit) { return $created; } diff --git a/application/Invoice/InvoiceModel.php b/application/Invoice/InvoiceModel.php index eddef131f..659842282 100644 --- a/application/Invoice/InvoiceModel.php +++ b/application/Invoice/InvoiceModel.php @@ -238,7 +238,11 @@ class InvoiceModel { $db = FronkDB::singleton(); - $res = $db->select("Invoice", "*", "1 = 1 ORDER BY invoice_number"); + $sql = "SELECT Invoice.* FROM Invoice + LEFT JOIN InvoiceFile ON (InvoiceFile.invoice_id = Invoice.id) + ORDER BY invoice_number"; + + $res = $db->query($sql); if($db->num_rows($res)) { while($data = $db->fetch_object($res)) { $items[] = new Invoice($data); @@ -252,7 +256,8 @@ class InvoiceModel { $db = FronkDB::singleton(); $where = self::getSqlFilter($filter); - $sql = "SELECT * FROM Invoice + $sql = "SELECT Invoice.* FROM Invoice + LEFT JOIN InvoiceFile ON (InvoiceFile.invoice_id = Invoice.id) WHERE $where ORDER BY invoice_number LIMIT 1"; //var_dump($sql);exit; @@ -273,7 +278,8 @@ class InvoiceModel { $db = FronkDB::singleton(); $where = self::getSqlFilter($filter); - $sql = "SELECT * FROM Invoice + $sql = "SELECT Invoice.* FROM Invoice + LEFT JOIN InvoiceFile ON (InvoiceFile.invoice_id = Invoice.id) WHERE $where ORDER BY invoice_number DESC LIMIT 1"; @@ -297,6 +303,7 @@ class InvoiceModel { $where = self::getSqlFilter($filter); $sql = "SELECT COUNT(*) as cnt FROM Invoice + LEFT JOIN InvoiceFile ON (InvoiceFile.invoice_id = Invoice.id) WHERE $where"; mfLoghandler::singleton()->debug($sql); @@ -320,7 +327,8 @@ class InvoiceModel { $db = FronkDB::singleton(); $where = self::getSqlFilter($filter); - $sql = "SELECT * FROM Invoice + $sql = "SELECT Invoice.* FROM Invoice + LEFT JOIN InvoiceFile ON (InvoiceFile.invoice_id = Invoice.id) WHERE $where ORDER BY $order"; @@ -358,6 +366,15 @@ class InvoiceModel { } } + if(array_key_exists("invoice_file", $filter)) { + $invoice_file = $filter['invoice_file']; + if($invoice_file === true) { + $where .= " AND InvoiceFile.id > 0'"; + } elseif($invoice_file === false || $invoice_file === null) { + $where .= " AND InvoiceFile.id IS NULL"; + } + } + if(array_key_exists("invoice_number", $filter)) { $invoice_number = $filter['invoice_number']; if($invoice_number === true) { diff --git a/application/Mailtemplate/Mailtemplate.php b/application/Mailtemplate/Mailtemplate.php index 4031b3ef8..670b5080c 100644 --- a/application/Mailtemplate/Mailtemplate.php +++ b/application/Mailtemplate/Mailtemplate.php @@ -11,19 +11,55 @@ class Mailtemplate extends mfBaseModel { } public function getVariableReplacedBody($replaceVars) { - return $this->replaceVariables($this->body, $replaceVars); + $body = $this->body_html ? $this->body_html : $this->body_text; + return $this->replaceVariables($body, $replaceVars); } private function replaceVariables($text, $replaceVars) { - if(!is_array($replaceVars)) return false; + if(!is_array($replaceVars)) return $text; foreach($replaceVars as $key => $replacement) { - $body = str_replace("{{".$key."}}", $replacement, $body); + $text = str_replace("{{".$key."}}", $replacement, $text); + } + + return $text; + } + + public function renderBody($body_replacers = null) { + if(is_array($body_replacers)) { + $body = $this->getVariableReplacedBody($body_replacers); + } else { + $body = $this->body_html ? $this->body_html : $this->body_text; + } + + // handle EMBED Tokens + $m = []; + if(preg_match_all('/{{EMBED:([^}]+)}}/i', $body, $m)) { + if(array_key_exists(1, $m)) { + foreach($m[1] as $match) { + $tpl_name = $match; + $tpl = MailtemplateModel::getFirst(["code" => $tpl_name]); + if(!$tpl) continue; + $tpl_body = $tpl->body_html ? $tpl->body_html : $tpl->body_text; + $tpl_replace = $this->replaceVariables($tpl_body, $body_replacers); + $body = $this->replaceVariables($body, ["EMBED:$tpl_name" => $tpl_replace]); + } + } } return $body; } + public function renderSubject($subject_replacers = null) { + if(is_array($subject_replacers)) { + $subject = $this->getVariableReplacedSubject($subject_replacers); + } else { + $subject = $this->subject; + } + + return $subject; + } + public function getProperty($name) { if($this->$name == null) { diff --git a/application/Mailtemplate/MailtemplateController.php b/application/Mailtemplate/MailtemplateController.php index 7b8bd3f6f..a4e571295 100644 --- a/application/Mailtemplate/MailtemplateController.php +++ b/application/Mailtemplate/MailtemplateController.php @@ -53,6 +53,10 @@ class MailtemplateController extends mfBaseController { private function getPreparedFilter($filter) { $new_filter = []; + if(array_key_exists("is_include", $filter) && !is_numeric($filter["is_include"])) { + unset($filter["is_include"]); + } + foreach($filter as $name => $value) { $new_filter[$name] = $value; } diff --git a/application/Preorder/Preorder.php b/application/Preorder/Preorder.php index 6cc7ec723..2439cd9c9 100644 --- a/application/Preorder/Preorder.php +++ b/application/Preorder/Preorder.php @@ -608,13 +608,13 @@ class Preorder extends mfBaseModel { $status = $this->getProperty("status")->getApiArray(); foreach($this->getProperty("statusflags") as $sflag) { - $status["flags"][$sflag->code] = [ + $status["flags"][] = [ "code" => (int)$sflag->code, "text" => $sflag->name, "value" => ($sflag->value->value == 1) ]; } - + //var_dump($status);exit; $a = []; $a['code'] = strtoupper($this->ucode); diff --git a/db/migrations/20240822153505_create_mailtemplate.php b/db/migrations/20240822153505_create_mailtemplate.php index 23cd78fbd..09da7a1f4 100644 --- a/db/migrations/20240822153505_create_mailtemplate.php +++ b/db/migrations/20240822153505_create_mailtemplate.php @@ -13,7 +13,7 @@ final class CreateMailtemplate extends AbstractMigration $table->addColumn("name", "string", ["null" => false, "limit" => 255]); $table->addColumn("code", "string", ["null" => false, "limit" => 64]); $table->addColumn("description", "string", ["null" => true, "default" => null, "limit" => 1024]); - $table->addColumn("subject", "string", ["null" => false, "limit" => 255]); + $table->addColumn("subject", "string", ["null" => true, "default" => null, "limit" => 255]); $table->addColumn("body_text", "text", ["null" => true, "default" => null]); $table->addColumn("body_html", "text", ["null" => true, "default" => null]); $table->addColumn("note", "text", ["null" => true, "default" => null]); diff --git a/db/migrations/20240827113849_create_invoice_job.php b/db/migrations/20240827113849_create_invoice_job.php index dd67b784a..587a73855 100644 --- a/db/migrations/20240827113849_create_invoice_job.php +++ b/db/migrations/20240827113849_create_invoice_job.php @@ -14,7 +14,7 @@ final class CreateInvoiceJob extends AbstractMigration $table->addColumn("to_date", "date", ["null" => false]); $table->addColumn("started", "datetime", ["null" => true, "default" => null]); $table->addColumn("finished", "datetime", ["null" => true, "default" => null]); - $table->addColumn("status", "string", ["null" => true, "default" => true, "limit" => 64]); + $table->addColumn("status", "string", ["null" => true, "default" => null, "limit" => 64]); $table->addColumn("result", "json", ["null" => true, "default" => null]); $table->addColumn("create_by", "integer", ["null" => false]); diff --git a/public/docs/preorder-api.yaml b/public/docs/preorder-api.yaml index 12ce71e4f..a9f5e2772 100644 --- a/public/docs/preorder-api.yaml +++ b/public/docs/preorder-api.yaml @@ -5,7 +5,7 @@ info: license: name: Apache 2.0 License url: http://www.apache.org/licenses/LICENSE-2.0.html - version: 1.2.0 + version: 1.2.1 servers: - url: https://thetool-test.xinon.at/api/v1 - url: https://thetool.xinon.at/api/v1 @@ -545,6 +545,20 @@ paths: type: string description: Statustext example: Fiber installation work assigned + flags: + type: array + items: + type: object + properties: + code: + type: integer + example: 141 + text: + type: string + example: "Conduit on property border and FCP prepaired" + value: + type: boolean + example: true additionalData: type: object description: Benutzerdefiniertes Objekt. Wird unverändert gespeichert und in `GET /preorder` wieder ausgegeben @@ -609,7 +623,7 @@ paths: description: | Successful operation - Mögliche Werte für Rückgabewert `status`: + Mögliche `status` Werte: | code | text | description | |------|------|-------------| @@ -618,15 +632,12 @@ paths: | 120 | Underground construction planning finished | Tiefbau Planung abgeschlossen | | 130 | Underground construction work assigned | Bauauftrag zugeteilt | | 140 | Conduit at property border | Rohr an Grundstücksgrenze | - | 145 | Installation kit picked up or shipped | Hausanschlusspaket an Kunden übergeben/versandt | - | 200 | Conduit in building | Rohr im Gebäude | | 210 | Fiber planning | Leitungsplan in Arbeit | | 220 | Fiber planning finished | Leitungsplan abgeschlossen | | 230 | Fiber installation work assigned | Bauauftrag zugeteilt | | 235 | Fiber on property line | Faser an Grundstücksgrenze | | 240 | Fiber in building | Faser im Gebäude | | 241 | BEP installed (multi-dwelling) | HÜP installiert (MPH) | - | 242 | Inhouse cabeling finished (multi-dwelling) | Hausverkabelung erledigt (MPH) | | 244 | BEP installed (single-dwelling) | HÜP installiert (EFH) | | 245 | OTO installed | Anschlussbox installiert | | 250 | ONT ready | ONT vorbereitet | @@ -638,6 +649,19 @@ paths: | 910 | Cancelled | Storniert: Keine Begründung | | 920 | Cancelled | Gekündigt durch Kunde | | 921 | Cancelled | Gekündigt durch ISP | + + --- + + Mögliche Status `flag`s: + + | code | text | description | + |------|------|-------------| + | 141 | Conduit on property border and FCP prepaired | | + | 145 | Installation kit picked up or shipped | Hausanschlusspaket an Kunden übergeben/versandt | + | 150 | Borderpoint connected | | + | 200 | Conduit in building | Rohr im Gebäude | + | 242 | Inhouse cabeling finished (multi-dwelling) | Hausverkabelung erledigt (MPH) | + content: application/json: schema: @@ -684,15 +708,12 @@ paths: | 120 | Underground construction planning finished | Tiefbau Planung abgeschlossen | | 130 | Underground construction work assigned | Bauauftrag zugeteilt | | 140 | Conduit at property border | Rohr an Grundstücksgrenze | - | 145 | Installation kit picked up or shipped | Hausanschlusspaket an Kunden übergeben/versandt | - | 200 | Conduit in building | Rohr im Gebäude | | 210 | Fiber planning | Leitungsplan in Arbeit | | 220 | Fiber planning finished | Leitungsplan abgeschlossen | | 230 | Fiber installation work assigned | Bauauftrag zugeteilt | | 235 | Fiber on property line | Faser an Grundstücksgrenze | | 240 | Fiber in building | Faser im Gebäude | | 241 | BEP installed (multi-dwelling) | HÜP installiert (MPH) | - | 242 | Inhouse cabeling finished (multi-dwelling) | Hausverkabelung erledigt (MPH) | | 244 | BEP installed (single-dwelling) | HÜP installiert (EFH) | | 245 | OTO installed | Anschlussbox installiert | | 250 | ONT ready | ONT vorbereitet | @@ -704,6 +725,19 @@ paths: | 910 | Cancelled | Storniert: Keine Begründung | | 920 | Cancelled | Gekündigt durch Kunde | | 921 | Cancelled | Gekündigt durch ISP | + + --- + + Mögliche Status `flag`s: + + | code | text | description | + |------|------|-------------| + | 141 | Conduit on property border and FCP prepaired | | + | 145 | Installation kit picked up or shipped | Hausanschlusspaket an Kunden übergeben/versandt | + | 150 | Borderpoint connected | | + | 200 | Conduit in building | Rohr im Gebäude | + | 242 | Inhouse cabeling finished (multi-dwelling) | Hausverkabelung erledigt (MPH) | + content: application/json: schema: @@ -1511,6 +1545,21 @@ components: type: string description: Statustext example: Fiber installation work assigned + flags: + type: array + items: + type: object + properties: + code: + type: integer + example: 141 + text: + type: string + example: "Conduit on property border and FCP prepaired" + value: + type: boolean + example: true + ciftoken: type: string description: Customer Installation Feedback Token @@ -1629,6 +1678,8 @@ components: type: string description: Wert für zusätzliches Feld example: true + + additionalData: type: object description: Benutzerdefiniertes Objekt. Wird unverändert gespeichert und in `GET /preorder` wieder ausgegeben @@ -1662,6 +1713,9 @@ components: type: object properties: ciftoken: + type: string + description: Cif Token + example: AbcdEFG123 code: type: string description: Eindeutiger Code der Vorbestellung |