From 6d5aaae11782c2acc8c10a4aee974d9368aec523 Mon Sep 17 00:00:00 2001 From: Frank Schubert Date: Tue, 16 Jul 2024 22:50:49 +0200 Subject: [PATCH] WIP Productchange 2024-07-16 --- Layout/default/Contract/CancelForm.php | 12 +- Layout/default/Contract/Form.php | 2 +- Layout/default/Contract/ProductchangeForm.php | 741 ++++++----- Layout/default/Contract/View.php | 15 +- application/Contract/Contract.php | 1166 +++++++++-------- application/Contract/ContractController.php | 248 ++-- application/Contract/ContractModel.php | 102 ++ application/Contract/trigger/Finished.php | 186 +++ .../Contractqueue/ContractqueueModel.php | 2 +- ...ct_link_change_changeaction_to_varchar.php | 31 + 10 files changed, 1523 insertions(+), 982 deletions(-) create mode 100644 application/Contract/trigger/Finished.php create mode 100644 db/migrations/20240716164608_contract_link_change_changeaction_to_varchar.php diff --git a/Layout/default/Contract/CancelForm.php b/Layout/default/Contract/CancelForm.php index 2d2e09854..6607adb83 100644 --- a/Layout/default/Contract/CancelForm.php +++ b/Layout/default/Contract/CancelForm.php @@ -46,6 +46,11 @@ Produkt Info: product_info?> + + Verrechnungsperiode: + + billing_period, "billing_period")?> + Fertigstellungsdatum: finish_date)?> @@ -75,7 +80,7 @@
- "> + ">
@@ -84,8 +89,8 @@
- - + +
@@ -122,6 +127,7 @@ $linkcontract = $link->contract; } + if($linkcontract->isCancelled()) continue; if(!$linkcontract->billing_period) continue; ?> diff --git a/Layout/default/Contract/Form.php b/Layout/default/Contract/Form.php index d3addd0bd..ef86b0999 100644 --- a/Layout/default/Contract/Form.php +++ b/Layout/default/Contract/Form.php @@ -86,7 +86,7 @@
- diff --git a/Layout/default/Contract/ProductchangeForm.php b/Layout/default/Contract/ProductchangeForm.php index 7a1a67b1f..d1ef26abc 100644 --- a/Layout/default/Contract/ProductchangeForm.php +++ b/Layout/default/Contract/ProductchangeForm.php @@ -1,344 +1,447 @@ - +
-
-
- -

Aktives Produkt

+
+
+ +

Aktives Produkt

+
-
- +
-
- - - -
-
-

product_name?> (id?>)

- - - - - - - - - - - - - - - - - -
Matchcode:matchcode?>
Vertragsinhaber: $contract->owner->id])?>">owner->getCompanyOrName()?> (owner->customer_number?>)
Produkt:product_name?> [product_id?>]product_name != $contract->product->name) ? " (".$contract->product->name.")" : ""?>
Produkt Info:product_info?>
- -
-
- - -
-
-

Neuer Contract

- -
"> - - -
-
- -
- -
- -
-
- -
- -
- - Eindeutige Identifizierung das Produkts. Z.B. Anschlussadresse, Domainname usw. -
-
- - product->attributes) && count($contract->product->attributes)) - && (array_key_exists(TT_ATTRIB_TERMINATION_REQUIRED_NAME, $contract->product->attributes) - && $contract->product->attributes[TT_ATTRIB_TERMINATION_REQUIRED_NAME]->value == 1) - || $contract->termination_id - ): ?> -
- - - -
- -
- - Scheint statt dem echten Produktnamen auf der Rechnung auf -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
+
- - linkFrom) && count($contract->linkFrom)) || (is_array($contract->linkTo) && count($contract->linkTo))): ?> -

Verknüpfte Verträge

+ - +
+
+

product_name?> (id?>)

+ +
- - - - - - - - - + + + + + + + + + + + + + + + + + + + + - linksWithCredit as $link): ?> - - if($link->contract_id == $contract->id) { - $linkcontract = $link->origin; - } else { - $linkcontract = $link->contract; - - } - - ?> - - - - - - - - - - - - -
ÜbernehmenKündigenKundeContract IDProduktMatchcodeBestelldatumFertigstellungKündigungMatchcode:matchcode?>
Vertragsinhaber: + $contract->owner->id])?>">owner->getCompanyOrName()?> + (owner->customer_number?>) + +
Produkt:product_name?> [product_id?> + ]product_name != $contract->product->name) ? " (" . $contract->product->name . ")" : ""?>
Produkt Info:product_info?>
Preis Netto:">€ amount != 1) ? $contract->price * $contract->amount : $contract->price, 4, ",", ".")?>
Preis Brutto:">€ + price && $contract->vatrate): ?> + amount != 1): ?> + price + ($contract->price / 100) * $contract->vatrate, 4, ",", ".")?> + + price + ($contract->price / 100) * $contract->vatrate) * $contract->amount, 4, ",", ".")?> + + +
Fertigstellungsdatum:finish_date)?>
- cancel_date && $linkcontract->cancel_date < date('U')) ? "" : "checked='checked'"?> /> - type == "credit"): ?> - (Gutschrift wird neu erstellt) - - $linkcontract->owner_id])?>" target="_blank">owner->getCompanyOrName()?>"> $linkcontract->id])?>" target="_blank">id?>"> $linkcontract->id])?>" target="_blank">product_name?>"> $linkcontract->id])?>" target="_blank">matchcode?>">order_date) ? date('d.m.Y', $linkcontract->order_date) : ""?>">finish_date) ? date('d.m.Y', $linkcontract->finish_date) : ""?>">cancel_date) ? date('d.m.Y', $linkcontract->cancel_date) : ""?>
- - - - - -
- -
- -
-
-
-
- - +
+ + +
+
+

Neuer Contract

+ + "> + + +
+
+ +
+ +
+ +
+
+ +
+ +
+ + Eindeutige Identifizierung das Produkts. Z.B. + Anschlussadresse, Domainname usw. +
+
+ + product->attributes) && count($contract->product->attributes)) + && (array_key_exists(TT_ATTRIB_TERMINATION_REQUIRED_NAME, $contract->product->attributes) + && $contract->product->attributes[TT_ATTRIB_TERMINATION_REQUIRED_NAME]->value == 1) + || $contract->termination_id + ): ?> +
+ + + +
+ +
+ + Scheint statt dem echten Produktnamen auf der + Rechnung auf +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ + + linkFrom) && count($contract->linkFrom)) || (is_array($contract->linkTo) && count($contract->linkTo))): ?> +

Verknüpfte Verträge

+ + + + + + + + + + + + + + + linksWithCredit as $link): ?> + contract_id == $contract->id) { + $linkcontract = $link->origin; + } else { + $linkcontract = $link->contract; + } + + if($linkcontract->isCancelled()) continue; + + ?> + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ +
+
+ + +
+
+ +
+
+ + - -
-
-
-
- - - -
+
+ + + +
- - - - - -
- + - - \ No newline at end of file + + \ No newline at end of file diff --git a/Layout/default/Contract/View.php b/Layout/default/Contract/View.php index 9f104222f..357065e4f 100644 --- a/Layout/default/Contract/View.php +++ b/Layout/default/Contract/View.php @@ -19,12 +19,11 @@
- + @@ -64,7 +63,7 @@ Upgrade auf: upgradeTo as $link): ?> - $link->contract_id])?>" class="contract-link contract->cancel_date && $link->contract->cancel_date <= date('U')) ? "canceled" : ""?>">contract->product_name?> [contract->matchcode?>] (contract_id?>)
+ $link->contract_id])?>" class="contract-link contract->cancel_date && $link->contract->cancel_date <= date('U')) ? "canceled" : ""?> contract->isFinished()) ? "not-finished" : "" ?>">contract->product_name?> [contract->matchcode?>] (contract_id?>)
@@ -233,7 +232,7 @@ $contract->id])?>"> $contract->id])?>"> - $contract->id])?>" onclick="if(!confirm('Jetzt fertigstellen und in Verrechnung geben?')) return false"> + $contract->id])?>" onclick="if(!confirm('Jetzt fertigstellen und in Verrechnung geben?')) return false"> @@ -290,11 +289,13 @@ value == "manual"): ?> - Vertrag manuell angelegt + Vertrag manuell erstellt value == "import"): ?> Vertrag importiert: text))?> value == "order"): ?> Vertrag aus Bestellung $contract->orderproduct->order_id])?>">#orderproduct->order_id?> erstellt + value == "productchange"): ?> + Vertrag erstellt: text))?> type == "contract_finished"): ?> @@ -311,7 +312,9 @@ value); ?> Verknüpfung mit $link->id])?>">id?> - product_name?> [matchcode?>] erstellt - + type == "canceled"): ?> + + Vertag gekündigt $j->id])?>" title="Journaleintrag bearbeiten"> diff --git a/application/Contract/Contract.php b/application/Contract/Contract.php index a0623e25f..7a8fc4864 100644 --- a/application/Contract/Contract.php +++ b/application/Contract/Contract.php @@ -1,572 +1,658 @@ in_after_save) return true; + $this->in_after_save++; + if($this->billingaddress_id) { $this->getProperty("billingaddress")->generateFibuAccountNumber(); } + + $this->runTriggers(); + + $this->in_after_save--; } + public function runTriggers() { + $dir = opendir(__DIR__."/trigger"); + while($filename = readdir($dir)) { + if(strpos($filename, ".") === 0 ) continue; + $m = []; + if(!preg_match('/^([a-z0-9_]+)\.php$/i', $filename, $m)) continue; + $trigger_name = $m[1]; + $classname = "ContractTrigger_$trigger_name"; + $filepath = __DIR__."/trigger/$trigger_name.php"; - private function getLinks() { - $this->linkFrom = []; - $this->linkTo = []; - $this->upgradeFrom = []; - $this->upgradeTo = []; - $this->downgradeFrom = []; - $this->downgradeTo = []; - $this->productchangeFrom = []; - $this->productchangeTo = []; - $this->relocationFrom = []; - $this->relocationTo = []; - - // Links targeting this contract (to) - foreach(ContractLinkModel::search(['origin_contract_id' => $this->id]) as $link) { - switch($link->type) { - case "link": - $this->linkTo[] = $link; - break; - case "credit": - $this->linkTo[] = $link; - break; - case "upgrade": - $this->upgradeTo[] = $link; - break; - case "downgrade": - $this->downgradeTo[] = $link; - break; - case "productchange": - $this->productchangeTo[] = $link; - break; - case "relocation": - $this->relocationTo[] = $link; - break; - default: - $this->log->warn("ContractLink with invalid type: ".$link->id." ".$link->type); - break; - } - } - - foreach(ContractLinkModel::search(['contract_id' => $this->id]) as $link) { - switch($link->type) { - case "link": - $this->linkFrom[] = $link; - break; - case "credit": - $this->linkFrom[] = $link; - break; - case "upgrade": - $this->upgradeFrom[] = $link; - break; - case "downgrade": - $this->downgradeFrom[] = $link; - break; - case "productchange": - $this->productchangeFrom[] = $link; - break; - case "relocation": - $this->relocationFrom[] = $link; - break; - default: - $this->log->warn("ContractLink with invalid type: ".$link->id." ".$link->type); - break; - } - } - - } - - - public function isCancelled() { - if(!$this->id) { - return false; - } + $this->log->debug(__METHOD__.": Looking for $classname in $filepath"); - if(!$this->cancel_date) { - return false; - } - - $now = date('U'); - if($this->cancel_date <= $now) { - return true; - } - - return false; - } - - public function isFinished() { - if(!$this->id) { - return false; - } - - $now = date('U'); - - if($this->finish_date && $this->finish_date <= $now) { - return true; - } - - if($this->cancel_date && $this->cancel_date <= $now) { - return true; - } - - return false; - } - - public function generateMatchcode() { - $owner_address = $this->getProperty("owner")->street.", ".$this->getProperty("owner")->zip." ".$this->getProperty("owner")->city; - - if($this->termination_id) { - $termination = new Termination($this->termination_id); - $termination_address = $termination->building->street.", ".$termination->building->zip." ".$termination->building->city; - return $termination_address; - } - - $product = $this->getProperty("product"); - /* - * exceptions for certain products - */ - foreach(["dsl", "standortgeber", "funkinternet", "glasfaser", "brettljausn"] as $qname) { - $qname = strtolower($qname); - $product_name = strtolower($product->name); - if(stripos($product_name, $qname) !== false) { - return $owner_address; - } - } - - return false; - } - - - public function addFilesFromOrder() { - if(!$this->orderproduct_id) { - return true; - } - - $op = $this->getProperty("orderproduct"); - $order = $op->order; - if(!$order || !$order->id) return true; - - foreach($order->files as $file) { - $cfile = ContractFileModel::create([ - "contract_id" => $this->id, - "file_id" => $file->file_id, - "name" => $file->name, - "description" => $file->description, - "create_by" => $file->create_by, - "edit_by" => $file->edit_by - ]); - $cfile->save(); - - $journal = ContractjournalModel::create([ - "contract_id" => $this->id, - "type" => "file", - "value" => $cfile->id, - "text" => (array_key_exists($cfile->name, TT_ORDER_FILE_TYPES)) ? TT_ORDER_FILE_TYPES[$cfile->name] : $cfile->name - ]); - $journal->save(); - } - - - } - - public function getVoicenumbers() { - $voicenumber = $this->getConfigValue("voicenumberblock_voicenumber"); - $numbers = json_decode($voicenumber->json); - return $numbers; - } - - public function getConfigValue($itemname) { - $configvalues = $this->getProperty("configvalues"); - if(!$configvalues) return null; - if(!array_key_exists($itemname, $configvalues)) return null; - - $configitem = $configvalues[$itemname]; - return $configitem->value; - } - - public function setConfigValue($itemname, $value) { - $configvalues = $this->getProperty("configvalues"); - if(!$configvalues) return null; - if(!array_key_exists($itemname, $configvalues)) return null; - - $configitem = $configvalues[$itemname]; - - $configitem->value->set($value); - $configitem->value->save(); - return true; - } - - public function sendCancelNotification($linked_contracts = []) { - $pdf_vars = [ - "contract" => $this, - "linked_contracts" => $linked_contracts, - "owner" => $this->getProperty("owner") - ]; - $pdf = new PdfForm("Emailtemplates/attachments/cancel_notification.pdf", $pdf_vars); - //$pdf->download(); - //exit; - $pdfpath = $pdf->render(); - $tvalue = $pdf->getReturnedValues(); - $pdfname = $tvalue["filename"]; - - // send email to customer - $tpl = new Layout(); - $tpl->setTemplate("Emailtemplates/customer/cancel_notification"); - - foreach($pdf_vars as $name => $val) { - $tpl->set($name, $val); - } - - $body = $tpl->render(); - $values = $tpl->getReturnedValue(); - - $subject = $values['subject']; - $from = $values['from_email']; - $from_name = $values['from_email_name']; - $to = $this->owner->email; - - if(!$subject || !$from || !$from_name || !$to) { - $this->log->warn("Service PIN Email not sent. (subject: '$subject', from: '$from_name', from_email: '$from', to: '$to')"); - } else { - $email = new Emailnotification(); - $email->setSubject($subject); - $email->setBody($body); - $email->setFrom($from, $from_name); - $email->setTo($to); - $email->setHeader("X-".MFAPPNAME."-Cid", $this->id); - $email->addAttachment($pdfpath, null, $pdfname, "application/pdf"); - $email->send(); - - $oemail = new Emailnotification(); - $oemail->setSubject("Kündigungbestätigung ".$this->owner->customer_number." ".str_replace(["\n", "\r"], "", $this->owner->getCompanyOrName())); - $oemail->setBody($body); - $oemail->setFrom($from, $from_name); - $oemail->setTo("office@xinon.at"); - $oemail->setHeader("X-".MFAPPNAME."-Cid", $this->id); - $oemail->addAttachment($pdfpath, null, $pdfname, "application/pdf"); - - $oemail->send(); - - $this->log->info(__METHOD__.": Sending Cancel Notication for ".$this->owner_id." to $to"); - } - - $tk_tpl = new Layout(); - $tk_tpl->setTemplate("Emailtemplates/customer/cancel_ticket"); - - foreach($pdf_vars as $name => $val) { - $tk_tpl->set($name, $val); - } - - $tk_body = $tk_tpl->render(); - $tk_values = $tk_tpl->getReturnedValue(); - - $tk_email = new Emailnotification(); - $tk_email->setSubject($tk_values["subject"]); - $tk_email->setBody($tk_body); - $tk_email->setFrom($tk_values["from_email"], $tk_values["from_email_name"]); - $tk_email->setTo("workspace@xinon.at"); - $tk_email->setHeader("X-".MFAPPNAME."-Cid", $this->id); - $tk_email->send(); - - } - - public function getProperty($name) { - if($this->$name == null) { - - if($name == "billingaddress" && $this->billingaddress_id) { - $this->billingaddress = mfValuecache::singleton()->get("mfObjectmodel-Address-".$this->billingaddress_id); - if($this->billingaddress === null) { - $this->billingaddress = new Address($this->billingaddress_id); - if($this->billingaddress->id) { - mfValuecache::singleton()->set("mfObjectmodel-Address-".$this->billingaddress_id, $this->billingaddress); - } - } - return $this->billingaddress; - } - - if($name == "owner" && $this->owner_id) { - $this->owner = mfValuecache::singleton()->get("mfObjectmodel-Address-".$this->owner_id); - if($this->owner === null) { - $this->owner = new Address($this->owner_id); - if($this->owner->id) { - mfValuecache::singleton()->set("mfObjectmodel-Address-".$this->owner_id, $this->owner); - } - } - return $this->owner; - } - - if($name == "product") { - $this->product = mfValuecache::singleton()->get("mfObjectmodel-Product-".$this->product_id); - if($this->product === null) { - $this->product = new Product($this->product_id); - if($this->product->id) { - mfValuecache::singleton()->set("mfObjectmodel-Product-".$this->product_id, $this->product); - } - } - return $this->product; - } - - if($name == "orderproduct") { - $this->orderproduct = mfValuecache::singleton()->get("mfObjectmodel-OrderProduct-".$this->orderproduct_id); - if($this->orderproduct === null) { - $this->orderproduct = new OrderProduct($this->orderproduct_id); - if($this->orderproduct->id) { - mfValuecache::singleton()->set("mfObjectmodel-OrderProduct-".$this->orderproduct_id, $this->orderproduct); - } - } - return $this->orderproduct; - } - - /*if($name == "contractConfigGroups") { - $product = $this->getProperty("product"); - $this->contractConfigGroups = ContractconfigGroupModel::search(['producttech_id' => $product->producttech_id]); - return $this->contractConfigGroups; - }*/ - - if($name == "configgroups") { - $product = $this->getProperty("product"); - $this->configgroups = []; - foreach(ContractconfiggroupProductgroupModel::search(['productgroup_id' => $product->productgroup_id]) as $ccpg) { - $ccpg->contractconfiggroup->setContractId($this->id); - $this->configgroups[] = $ccpg->contractconfiggroup; + if(!file_exists($filepath)) { + $this->log->debug(__METHOD__.": $filepath not found"); + continue; } - return $this->configgroups; - } + require_once $filepath; - if($name == "configvalues") { - $this->configvalues = []; - foreach($this->getProperty("configgroups") as $group) { - foreach($group->items as $item) { - $this->configvalues[$item->name] = $item; + if(!class_exists($classname)) { + $this->log->debug(__METHOD__.": $classname not found"); + continue; + } + + $me = new User(); + $me->loadMe(); + + $injection = [ + "db" => $this->db, + "log" => $this->log, + "me" => $me, + "contract" => $this + ]; + + try { + $trigger = new $classname($injection); + if($trigger->precheck()) { + $this->log->debug(__METHOD__.": precheck successful: running"); + $trigger->run(); } + } catch(Exception $e) { + throw $e; } - return $this->configvalues; } - if(!$this->id) { - return null; - } - - - - + } - + public function getRegularCanceldate($format = "d.m.Y") { + if(!$this->finish_date) return ""; - + $today = new DateTime(); + $tomorrow = clone($today); + $tomorrow->modify("+1 day"); + + $finish_date = new DateTime("@".$this->finish_date); + $finish_date->setTimezone(new DateTimeZone("Europe/Vienna")); + + $period_end_date = clone($finish_date); + $period_end_date->modify("+".$this->contract_term." months"); + + while($period_end_date->format("Y-m-d") <= $today->format("Y-m-d")) { + $period_end_date = $period_end_date->modify("+".$this->billing_period." months"); + + } + $period_end_date->modify("-1 day"); + return $period_end_date->format($format); + } + + public function getNextBillingPeriodEnd($format = "d.m.Y") { + if(!$this->finish_date) return ""; + + $today = new DateTime(); + + $finish_date = new DateTime("@".$this->finish_date); + $finish_date->setTimezone(new DateTimeZone("Europe/Vienna")); + + $next_billing_period = clone($finish_date); + $next_billing_period->modify("+".$this->billing_period." months"); + while($next_billing_period->format("Y-m-d") <= $today->format("Y-m-d")) { + $next_billing_period->modify("+".$this->billing_period." months"); + } + $next_billing_period->modify("-1 day"); + + return $next_billing_period->format($format); + } + + private function getLinks() { + $this->linkFrom = []; + $this->linkTo = []; + $this->upgradeFrom = []; + $this->upgradeTo = []; + $this->downgradeFrom = []; + $this->downgradeTo = []; + $this->productchangeFrom = []; + $this->productchangeTo = []; + $this->relocationFrom = []; + $this->relocationTo = []; + + // Links targeting this contract (to) + foreach(ContractLinkModel::search(['origin_contract_id' => $this->id]) as $link) { + switch($link->type) { + case "link": + $this->linkTo[] = $link; + break; + case "credit": + $this->linkTo[] = $link; + break; + case "upgrade": + $this->upgradeTo[] = $link; + break; + case "downgrade": + $this->downgradeTo[] = $link; + break; + case "productchange": + $this->productchangeTo[] = $link; + break; + case "relocation": + $this->relocationTo[] = $link; + break; + default: + $this->log->warn("ContractLink with invalid type: " . $link->id . " " . $link->type); + break; + } + } + + foreach(ContractLinkModel::search(['contract_id' => $this->id]) as $link) { + switch($link->type) { + case "link": + $this->linkFrom[] = $link; + break; + case "credit": + $this->linkFrom[] = $link; + break; + case "upgrade": + $this->upgradeFrom[] = $link; + break; + case "downgrade": + $this->downgradeFrom[] = $link; + break; + case "productchange": + $this->productchangeFrom[] = $link; + break; + case "relocation": + $this->relocationFrom[] = $link; + break; + default: + $this->log->warn("ContractLink with invalid type: " . $link->id . " " . $link->type); + break; + } + } + + } + + + public function isCancelled() { + if(!$this->id) { + return false; + } + + if(!$this->cancel_date) { + return false; + } + + $now = date('U'); + if($this->cancel_date <= $now) { + return true; + } + + return false; + } + + public function isFinished() { + if(!$this->id) { + return false; + } + + $now = date('U'); + + if($this->finish_date && $this->finish_date <= $now) { + return true; + } + + if($this->cancel_date && $this->cancel_date <= $now) { + return true; + } + + return false; + } + + public function generateMatchcode() { + $owner_address = $this->getProperty("owner")->street . ", " . $this->getProperty("owner")->zip . " " . $this->getProperty("owner")->city; + + if($this->termination_id) { + $termination = new Termination($this->termination_id); + $termination_address = $termination->building->street . ", " . $termination->building->zip . " " . $termination->building->city; + return $termination_address; + } - - /*if($name == "contractConfigItems") { $product = $this->getProperty("product"); - - $this->contractConfigItems = []; - foreach(ProducttechAttributeModel::search(['producttech_id' => $product->producttech_id, "forcontract" => 1]) as $item) { - $item->setContractId($this->id); - $this->contractConfigItems [] = $item; - } - - return $this->contractConfigItems; - }*/ - - if($name == "vatrate") { - $vatgroup = $this->getProperty("vatgroup"); - $country = $this->getProperty("country"); - $vatrate = $vatgroup->rates["domestic"]->rate; - if($this->country_id && $country->is_eu) { - $vatrate = $vatgroup->rates["eu"]->rate; - } if($this->country_id && !$country->is_eu) { - $vatrate = $vatgroup->rates["other"]->rate; + /* + * exceptions for certain products + */ + foreach(["dsl", "standortgeber", "funkinternet", "glasfaser", "brettljausn"] as $qname) { + $qname = strtolower($qname); + $product_name = strtolower($product->name); + if(stripos($product_name, $qname) !== false) { + return $owner_address; } - $this->vatrate = $vatrate; - return $this->vatrate; } - if($name == "voicenumbers") { - $numbers = $this->getVoicenumbers(); - $this->voicenumbers = $numbers; - return $this->voicenumbers; - } - - if($name == "journals") { - $this->journals = array_reverse(ContractjournalModel::search(["contract_id" => $this->id])); - return $this->journals; - } - - if($name == "orderjournals") { - if(!$this->orderproduct_id) return null; - - $journals = []; - - $order = $this->getProperty("orderproduct")->order; - foreach($order->journal as $j) { - $oj = new Contractjournal(); - $oj->type = "order_comment"; - $oj->value = ""; - $oj->text = $j->text; - $journals[] = $oj; - } - - $this->orderjournals = $journals; - return $this->orderjournals; - } - - if($name == "links") { - $this->links = ContractLinkModel::includesContractId($this->id, ["type" => "link"]); - //var_dump($this->links);exit; - return $this->links; - } - - if($name == "linksWithCredit") { - $this->linksWithCredit = ContractLinkModel::includesContractId($this->id, ["type" => ["link", "credit"]]); - //var_dump($this->links);exit; - return $this->linksWithCredit; - } - - if(in_array($name, ['linkFrom','linkTo','upgradeFrom','upgradeTo','downgradeFrom','downgradeTo','productchangeFrom','productchangeTo','relocationFrom','relocationTo'])) { - if($this->$name === null) { - $this->getLinks(); - } - return $this->$name; - } - - if($name == "finisher") { - $this->finisher = mfValuecache::singleton()->get("Worker-id-".$this->finish_date_by); - if($this->finisher === null) { - $this->finisher = new User($this->finish_date_by); - if($this->finisher->id) { - mfValuecache::singleton()->set("Worker-id-".$this->finish_date_by, $this->finisher); - } - } - return $this->finisher; - } - - if($name == "canceler") { - $this->canceler = mfValuecache::singleton()->get("Worker-id-".$this->cancel_date_by); - if($this->canceler === null) { - $this->canceler = new User($this->cancel_date_by); - if($this->canceler->id) { - mfValuecache::singleton()->set("Worker-id-".$this->cancel_date_by, $this->canceler); - } - } - return $this->canceler; - } - - if($name == "creator") { - $this->creator = mfValuecache::singleton()->get("Worker-id-".$this->create_by); - if($this->creator === null) { - $this->creator = new User($this->create_by); - if($this->creator->id) { - mfValuecache::singleton()->set("Worker-id-".$this->create_by, $this->creator); - } - } - return $this->creator; - } - - if($name == "editor") { - $this->editor = mfValuecache::singleton()->get("Worker-id-".$this->edit_by); - if($this->editor === null) { - $this->editor = new User($this->edit_by); - if($this->editor->id) { - mfValuecache::singleton()->set("Worker-id-".$this->edit_by, $this->editor); - } - } - return $this->editor; - } - - $classname = ucfirst($name); - $idfield = $name."_id"; - $this->$name = mfValuecache::singleton()->get("mfObjectmodel-$name-".$this->$idfield); - if(!$this->$name) { - $this->$name = new $classname($this->$idfield); - } - - if($this->$name->id) { - mfValuecache::singleton()->set("mfObjectmodel-$name-".$this->$name->id, $this->$name); - return $this->$name; - } else { - return null; - } - + return false; } - - return $this->$name; - } - - - - /* - * When object is cloned, new object will call this funtion to let us clean up the new Contract. - * Throws Exception if cloning fails. - */ - public function __clone() { - $me = new User; - $me->loadMe(); - - $old_id = $this->id; - $this->id = null; - - - // cleanup Contract daten - $this->orderproduct_id = null; - $this->order_date = null; - $this->finish_date = null; - $this->finish_date_by = null; - $this->cancel_date = null; - $this->cancel_date_by = null; - $this->create_by = $me->id; - $this->edit_by = $me->id; - - $this->create = null; - $this->edit = null; - $this->saved = 0; - $this->mode = "new"; - $this->_old_data = new StdClass(); - - //$this->save(); - - if($old_id == $this->id) { - $this->log->error("save() of cloned Contract $old_id failed!"); - throw new Exception("Saving clone failed."); + + + public function addFilesFromOrder() { + if(!$this->orderproduct_id) { + return true; + } + + $op = $this->getProperty("orderproduct"); + $order = $op->order; + if(!$order || !$order->id) return true; + + foreach($order->files as $file) { + $cfile = ContractFileModel::create([ + "contract_id" => $this->id, + "file_id" => $file->file_id, + "name" => $file->name, + "description" => $file->description, + "create_by" => $file->create_by, + "edit_by" => $file->edit_by + ]); + $cfile->save(); + + $journal = ContractjournalModel::create([ + "contract_id" => $this->id, + "type" => "file", + "value" => $cfile->id, + "text" => (array_key_exists($cfile->name, TT_ORDER_FILE_TYPES)) ? TT_ORDER_FILE_TYPES[$cfile->name] : $cfile->name + ]); + $journal->save(); + } + + + } + + public function getVoicenumbers() { + $voicenumber = $this->getConfigValue("voicenumberblock_voicenumber"); + $numbers = json_decode($voicenumber->json); + return $numbers; + } + + public function getConfigValue($itemname) { + $configvalues = $this->getProperty("configvalues"); + if(!$configvalues) return null; + if(!array_key_exists($itemname, $configvalues)) return null; + + $configitem = $configvalues[$itemname]; + return $configitem->value; + } + + public function setConfigValue($itemname, $value) { + $configvalues = $this->getProperty("configvalues"); + if(!$configvalues) return null; + if(!array_key_exists($itemname, $configvalues)) return null; + + $configitem = $configvalues[$itemname]; + + $configitem->value->set($value); + $configitem->value->save(); + return true; + } + + public function sendCancelNotification($linked_contracts = []) { + $pdf_vars = [ + "contract" => $this, + "linked_contracts" => $linked_contracts, + "owner" => $this->getProperty("owner") + ]; + $pdf = new PdfForm("Emailtemplates/attachments/cancel_notification.pdf", $pdf_vars); + //$pdf->download(); + //exit; + $pdfpath = $pdf->render(); + $tvalue = $pdf->getReturnedValues(); + $pdfname = $tvalue["filename"]; + + // send email to customer + $tpl = new Layout(); + $tpl->setTemplate("Emailtemplates/customer/cancel_notification"); + + foreach($pdf_vars as $name => $val) { + $tpl->set($name, $val); + } + + $body = $tpl->render(); + $values = $tpl->getReturnedValue(); + + $subject = $values['subject']; + $from = $values['from_email']; + $from_name = $values['from_email_name']; + $to = $this->owner->email; + + if(!$subject || !$from || !$from_name || !$to) { + $this->log->warn("Service PIN Email not sent. (subject: '$subject', from: '$from_name', from_email: '$from', to: '$to')"); + } else { + $email = new Emailnotification(); + $email->setSubject($subject); + $email->setBody($body); + $email->setFrom($from, $from_name); + $email->setTo($to); + $email->setHeader("X-" . MFAPPNAME . "-Cid", $this->id); + $email->addAttachment($pdfpath, null, $pdfname, "application/pdf"); + $email->send(); + + $oemail = new Emailnotification(); + $oemail->setSubject("Kündigungbestätigung " . $this->owner->customer_number . " " . str_replace(["\n", "\r"], "", $this->owner->getCompanyOrName())); + $oemail->setBody($body); + $oemail->setFrom($from, $from_name); + $oemail->setTo("office@xinon.at"); + $oemail->setHeader("X-" . MFAPPNAME . "-Cid", $this->id); + $oemail->addAttachment($pdfpath, null, $pdfname, "application/pdf"); + + $oemail->send(); + + $this->log->info(__METHOD__ . ": Sending Cancel Notication for " . $this->owner_id . " to $to"); + } + + $tk_tpl = new Layout(); + $tk_tpl->setTemplate("Emailtemplates/customer/cancel_ticket"); + + foreach($pdf_vars as $name => $val) { + $tk_tpl->set($name, $val); + } + + $tk_body = $tk_tpl->render(); + $tk_values = $tk_tpl->getReturnedValue(); + + $tk_email = new Emailnotification(); + $tk_email->setSubject($tk_values["subject"]); + $tk_email->setBody($tk_body); + $tk_email->setFrom($tk_values["from_email"], $tk_values["from_email_name"]); + $tk_email->setTo("workspace@xinon.at"); + $tk_email->setHeader("X-" . MFAPPNAME . "-Cid", $this->id); + $tk_email->send(); + + } + + public function getProperty($name) { + if($this->$name == null) { + + if($name == "billingaddress" && $this->billingaddress_id) { + $this->billingaddress = mfValuecache::singleton()->get("mfObjectmodel-Address-" . $this->billingaddress_id); + if($this->billingaddress === null) { + $this->billingaddress = new Address($this->billingaddress_id); + if($this->billingaddress->id) { + mfValuecache::singleton()->set("mfObjectmodel-Address-" . $this->billingaddress_id, $this->billingaddress); + } + } + return $this->billingaddress; + } + + if($name == "owner" && $this->owner_id) { + $this->owner = mfValuecache::singleton()->get("mfObjectmodel-Address-" . $this->owner_id); + if($this->owner === null) { + $this->owner = new Address($this->owner_id); + if($this->owner->id) { + mfValuecache::singleton()->set("mfObjectmodel-Address-" . $this->owner_id, $this->owner); + } + } + return $this->owner; + } + + if($name == "product") { + $this->product = mfValuecache::singleton()->get("mfObjectmodel-Product-" . $this->product_id); + if($this->product === null) { + $this->product = new Product($this->product_id); + if($this->product->id) { + mfValuecache::singleton()->set("mfObjectmodel-Product-" . $this->product_id, $this->product); + } + } + return $this->product; + } + + if($name == "orderproduct") { + $this->orderproduct = mfValuecache::singleton()->get("mfObjectmodel-OrderProduct-" . $this->orderproduct_id); + if($this->orderproduct === null) { + $this->orderproduct = new OrderProduct($this->orderproduct_id); + if($this->orderproduct->id) { + mfValuecache::singleton()->set("mfObjectmodel-OrderProduct-" . $this->orderproduct_id, $this->orderproduct); + } + } + return $this->orderproduct; + } + + /*if($name == "contractConfigGroups") { + $product = $this->getProperty("product"); + $this->contractConfigGroups = ContractconfigGroupModel::search(['producttech_id' => $product->producttech_id]); + return $this->contractConfigGroups; + }*/ + + if($name == "configgroups") { + $product = $this->getProperty("product"); + $this->configgroups = []; + foreach(ContractconfiggroupProductgroupModel::search(['productgroup_id' => $product->productgroup_id]) as $ccpg) { + $ccpg->contractconfiggroup->setContractId($this->id); + $this->configgroups[] = $ccpg->contractconfiggroup; + } + + return $this->configgroups; + } + + if($name == "configvalues") { + $this->configvalues = []; + foreach($this->getProperty("configgroups") as $group) { + foreach($group->items as $item) { + $this->configvalues[$item->name] = $item; + } + } + + return $this->configvalues; + } + + if(!$this->id) { + return null; + } + + + /*if($name == "contractConfigItems") { + $product = $this->getProperty("product"); + + $this->contractConfigItems = []; + foreach(ProducttechAttributeModel::search(['producttech_id' => $product->producttech_id, "forcontract" => 1]) as $item) { + $item->setContractId($this->id); + $this->contractConfigItems [] = $item; + } + + return $this->contractConfigItems; + }*/ + + if($name == "vatrate") { + $vatgroup = $this->getProperty("vatgroup"); + $country = $this->getProperty("country"); + $vatrate = $vatgroup->rates["domestic"]->rate; + if($this->country_id && $country->is_eu) { + $vatrate = $vatgroup->rates["eu"]->rate; + } + if($this->country_id && !$country->is_eu) { + $vatrate = $vatgroup->rates["other"]->rate; + } + $this->vatrate = $vatrate; + return $this->vatrate; + } + + if($name == "voicenumbers") { + $numbers = $this->getVoicenumbers(); + $this->voicenumbers = $numbers; + return $this->voicenumbers; + } + + if($name == "journals") { + $this->journals = array_reverse(ContractjournalModel::search(["contract_id" => $this->id])); + return $this->journals; + } + + if($name == "orderjournals") { + if(!$this->orderproduct_id) return null; + + $journals = []; + + $order = $this->getProperty("orderproduct")->order; + foreach($order->journal as $j) { + $oj = new Contractjournal(); + $oj->type = "order_comment"; + $oj->value = ""; + $oj->text = $j->text; + $journals[] = $oj; + } + + $this->orderjournals = $journals; + return $this->orderjournals; + } + + if($name == "links") { + $this->links = ContractLinkModel::includesContractId($this->id, ["type" => "link"]); + //var_dump($this->links);exit; + return $this->links; + } + + if($name == "linksWithCredit") { + $this->linksWithCredit = ContractLinkModel::includesContractId($this->id, ["type" => ["link", "credit"]]); + //var_dump($this->links);exit; + return $this->linksWithCredit; + } + + if(in_array($name, ['linkFrom', 'linkTo', 'upgradeFrom', 'upgradeTo', 'downgradeFrom', 'downgradeTo', 'productchangeFrom', 'productchangeTo', 'relocationFrom', 'relocationTo'])) { + if($this->$name === null) { + $this->getLinks(); + } + return $this->$name; + } + + if($name == "finisher") { + $this->finisher = mfValuecache::singleton()->get("Worker-id-" . $this->finish_date_by); + if($this->finisher === null) { + $this->finisher = new User($this->finish_date_by); + if($this->finisher->id) { + mfValuecache::singleton()->set("Worker-id-" . $this->finish_date_by, $this->finisher); + } + } + return $this->finisher; + } + + if($name == "canceler") { + $this->canceler = mfValuecache::singleton()->get("Worker-id-" . $this->cancel_date_by); + if($this->canceler === null) { + $this->canceler = new User($this->cancel_date_by); + if($this->canceler->id) { + mfValuecache::singleton()->set("Worker-id-" . $this->cancel_date_by, $this->canceler); + } + } + return $this->canceler; + } + + if($name == "creator") { + $this->creator = mfValuecache::singleton()->get("Worker-id-" . $this->create_by); + if($this->creator === null) { + $this->creator = new User($this->create_by); + if($this->creator->id) { + mfValuecache::singleton()->set("Worker-id-" . $this->create_by, $this->creator); + } + } + return $this->creator; + } + + if($name == "editor") { + $this->editor = mfValuecache::singleton()->get("Worker-id-" . $this->edit_by); + if($this->editor === null) { + $this->editor = new User($this->edit_by); + if($this->editor->id) { + mfValuecache::singleton()->set("Worker-id-" . $this->edit_by, $this->editor); + } + } + return $this->editor; + } + + $classname = ucfirst($name); + $idfield = $name . "_id"; + $this->$name = mfValuecache::singleton()->get("mfObjectmodel-$name-" . $this->$idfield); + if(!$this->$name) { + $this->$name = new $classname($this->$idfield); + } + + if($this->$name->id) { + mfValuecache::singleton()->set("mfObjectmodel-$name-" . $this->$name->id, $this->$name); + return $this->$name; + } else { + return null; + } + + } + + return $this->$name; + } + + + /* + * When object is cloned, new object will call this funtion to let us clean up the new Contract. + * Throws Exception if cloning fails. + */ + public function __clone() { + $me = new User; + $me->loadMe(); + + $old_id = $this->id; + $this->id = null; + + + // cleanup Contract daten + $this->orderproduct_id = null; + $this->order_date = null; + $this->finish_date = null; + $this->finish_date_by = null; + $this->cancel_date = null; + $this->cancel_date_by = null; + $this->create_by = $me->id; + $this->edit_by = $me->id; + + $this->create = null; + $this->edit = null; + $this->saved = 0; + $this->mode = "new"; + $this->_old_data = new StdClass(); + + //$this->save(); + + if($old_id == $this->id) { + $this->log->error("save() of cloned Contract $old_id failed!"); + throw new Exception("Saving clone failed."); + } + + $this->log->debug("Cloned Contract $old_id"); } - - $this->log->debug("Cloned Contract $old_id"); - } } \ No newline at end of file diff --git a/application/Contract/ContractController.php b/application/Contract/ContractController.php index 1f0c05050..ec6cf2ebb 100644 --- a/application/Contract/ContractController.php +++ b/application/Contract/ContractController.php @@ -140,34 +140,13 @@ class ContractController extends mfBaseController $this->redirect("Contract", "view", ["contract_id" => $contract->id]); } - if($contract->finish_date) { - $today = new DateTime(); - $tomorrow = clone($today); - $tomorrow->modify("+1 day"); + $today = new DateTime(); + $tomorrow = clone($today); + $tomorrow->modify("+1 day"); - $finish_date = new DateTime("@".$contract->finish_date); - $finish_date->setTimezone(new DateTimeZone("Europe/Vienna")); - - $period_end_date = clone($finish_date); - $period_end_date->modify("+".$contract->contract_term." months"); - - while($period_end_date->format("Y-m-d") <= $today->format("Y-m-d")) { - $period_end_date = $finish_date->modify("+".$contract->billing_period." months"); - - } - $period_end_date->modify("-1 day"); - - $next_billing_period = clone($finish_date); - $next_billing_period->modify("+".$contract->billing_period." months"); - while($next_billing_period->format("Y-m-d") <= $today->format("Y-m-d")) { - $next_billing_period->modify("+".$contract->billing_period." months"); - } - $next_billing_period->modify("-1 day"); - - $this->layout()->set("tomorrow", $tomorrow); - $this->layout()->set("term_end_date", $period_end_date); - $this->layout()->set("period_end_date", $next_billing_period); - } + $this->layout()->set("tomorrow", $tomorrow); + $this->layout()->set("term_end_date", $contract->getRegularCanceldate()); + $this->layout()->set("period_end_date", $contract->getNextBillingPeriodEnd()); $this->layout()->set("contract", $contract); @@ -191,7 +170,7 @@ class ContractController extends mfBaseController try { $cancel_date = DateTime::createFromFormat("d.m.Y", trim($r->cancel_date), new DateTimeZone("Europe/Vienna")); - $cancel_date->setTime(2,0,0); + $cancel_date->setTime(23,59,59); } catch(Exception $e) { $this->layout()->setFlash("Ungültiges Datumsformat"); $this->redirect("Contract", "cancel", ["contract_id" => $contract->id]); @@ -288,6 +267,7 @@ class ContractController extends mfBaseController protected function saveProductchangeAction() { $r = $this->request; + //var_dump($r->links);exit; $id = $r->contract_id; if (!is_numeric($id) || !$id) { @@ -330,6 +310,22 @@ class ContractController extends mfBaseController $contract_data['product_external_id'] = $product->external_id; $contract_data['sla_id'] = $product->sla_id; + if($r->finish_date) { + try { + $finish_date = DateTime::createFromFormat("d.m.Y", $r->finish_date, new DateTimeZone("Europe/Vienna")); + } catch (Exception $e) { + $this->layout()->setFlash("Ungültiges Kündigungsdateum", "error"); + $this->redirect("Contract", "productchange", ["contract_id" => $id]); + } + + $finish_date->setTime(0,0,0); + $contract_data["finish_date"] = $finish_date->getTimestamp(); + + $contract_cancel_date = clone($finish_date); + $contract_cancel_date->modify("-1 day"); + $contract_cancel_date->setTime(23,59,59); + } + $require_term = false; if (array_key_exists(TT_ATTRIB_TERMINATION_REQUIRED_NAME, $product->attributes) && $product->attributes[TT_ATTRIB_TERMINATION_REQUIRED_NAME]->value == 1) { //var_dump($prod->attributes); @@ -353,41 +349,124 @@ class ContractController extends mfBaseController $this->redirect("Contract", "productchange", ["contract_id" => $id]); } + if($contract_cancel_date) { + $contract->cancel_date = $contract_cancel_date->getTimestamp(); + $contract->save(); + } + + $journal = ContractjournalModel::create([ + 'contract_id' => $new_contract->id, + 'type' => "created_from", + 'value' => "productchange", + 'text' => "Produkt-/Standortwechsel von Contract ID ".$contract->id + ]); + $journal->save(); if (is_array($r->links) && count($r->links)) { - foreach ($r->links as $link_id => $action) { + foreach ($r->links as $link_id => $link_data) { + $action = $link_data["action"]; + $cancel_date = false; + if($link_data["cancel_date"]) { + try { + $cancel_date = DateTime::createFromFormat("d.m.Y", $link_data["cancel_date"], new DateTimeZone("Europe/Vienna")); + } catch (Exception $e) { + $this->layout()->setFlash("Ungültiges Kündigungsdateum", "error"); + $this->redirect("Contract", "productchange", ["contract_id" => $id]); + } + } + + $old_link = new ContractLink($link_id); if (!$old_link->id) continue; // check if link contains this contract if ($old_link->contract_id == $contract->id) { + $origin_id = $old_link->origin_contract_id; + $link_contract_id = $old_link->contract_id; + $new_link_contract_id = $new_contract->id; $new_link_origin_id = $old_link->origin_contract_id; } elseif ($old_link->origin_contract_id == $contract->id) { + $origin_id = $old_link->contract_id; + $link_contract_id = $old_link->origin_contract_id; + $new_link_contract_id = $old_link->contract_id; $new_link_origin_id = $new_contract->id; } else { continue; } - $new_link = ContractLinkModel::create([ - 'contract_id' => $new_link_contract_id, - 'origin_contract_id' => $new_link_origin_id, - 'type' => $old_link->type, - ]); - if (!$new_link->save()) { - $this->layout()->setFlash("Konnte neuen Link nicht speichern", "warn"); - } - - if ($action == "cancel") { - $old_link->change_action = "cancel"; - if (!$old_link->save()) { - $this->layout()->setFlash("Konnte alten Link nicht speichern", "warn"); + if($action != "cancel" && $old_link->type != "credit") { + $new_link = ContractLinkModel::create([ + 'contract_id' => $new_link_contract_id, + 'origin_contract_id' => $new_link_origin_id, + 'type' => $old_link->type, + ]); + if (!$new_link->save()) { + $this->layout()->setFlash("Konnte neuen Link nicht speichern", "warn"); } } + if ($action == "cancel") { + if($cancel_date) { + // insert cancel_date in old contract + $lc = new Contract($origin_id); + $lc->cancel_date = $cancel_date->getTimestamp(); + $lc->save(); + } else { + // leave cancellation for later (when finishing upgrade) + $old_link->change_action = "cancel"; + if (!$old_link->save()) { + $this->layout()->setFlash("Konnte alten Link nicht speichern", "warn"); + } + } + + } + if ($old_link->type == "credit" && $action == "keep") { - $old_link->change_action = "recreate"; + // XXX - if we have finish date then recreate credit contract right now + if($contract_cancel_date) { + $new_credit = ContractModel::createCreditForContract($new_contract); + $new_credit->save(); + + // create journal for credit + $journal = ContractjournalModel::create([ + 'contract_id' => $new_credit->id, + 'type' => "created_from", + 'value' => "productchange", + 'text' => "Produkt-/Standortwechsel von Contract ID ".$new_link_origin_id + ]); + $journal->save(); + + + $this->log->debug(print_r($new_credit, true)); + + // set cancel date for old credit + $old_credit = new Contract($origin_id); + $old_credit->cancel_date = $contract_cancel_date->getTimestamp(); + $old_credit->save(); + + // create link to new credit contract + $link = ContractLinkModel::create([ + "contract_id" => $new_credit->id, + "origin_contract_id" => $new_contract->id, + "type" => "credit" + ]); + $link->save(); + + // create upgrade link from old to new credit contract + $link = ContractLinkModel::create([ + "contract_id" => $new_credit->id, + "origin_contract_id" => $origin_id, + "type" => "upgrade" + ]); + $link->save(); + + } else { + $old_link->change_action = "recreate"; + $old_link->save(); + } + } //var_dump($new_link);exit; @@ -439,82 +518,23 @@ class ContractController extends mfBaseController $this->redirect("Contract"); } - $now = date('U'); - - /* - * Vorgänger Contracts kündigen - */ - foreach (ContractLinkModel::search(['contract_id' => $id]) as $link) { - if (!in_array($link->type, ["upgrade", "downgrade", "relocation", "productchange"])) { - continue; - } - - $origin = $link->origin; - $origin->cancel_date = $now; - $origin->cancel_date_by = $this->me->id; - $origin->edit_by = $this->me->id; - if (!$origin->save()) { - $this->layout()->setFlash("Achtung: Konnte nicht alle Vorgängercontracts kündigen!", "warn"); - } - - /* - * alte Links übernehmen / kündigen - */ - foreach (ContractLinkModel::search(['type' => "link", 'contract_id' => $origin->id]) as $old_link) { - // verlinkten Contract kündigen (wenn nicht schon gekündigt) - if ($old_link->change_action == "cancel" && !$old_link->contract->cancel_date) { - $old_link->origin->update([ - 'cancel_date' => $now, - 'cancel_date_by' => $this->me->id, - 'edit_by' => $this->me->id - ]); - $old_link->origin->save(); - - $old_link->change_action = null; - $old_link->save(); - } - } - - foreach (ContractLinkModel::search(['type' => "link", 'origin_contract_id' => $origin->id]) as $old_link) { - // verlinkten Contract kündigen (wenn nicht schon gekündigt) - if ($old_link->change_action == "cancel" && !$old_link->contract->cancel_date) { - $old_link->contract->update([ - 'cancel_date' => $now, - 'cancel_date_by' => $this->me->id, - 'edit_by' => $this->me->id - ]); - $old_link->contract->save(); - - $old_link->change_action = null; - $old_link->save(); - } - } - - foreach (ContractLinkModel::search(['type' => "credit", 'contract_id' => $origin->id]) as $old_credit) { - // verlinkten Contract kündigen (wenn nicht schon gekündigt) - if ($old_credit->change_action == "recreate" && !$old_credit->contract->cancel_date) { - $old_credit->origin->update([ - 'cancel_date' => $now, - 'cancel_date_by' => $this->me->id, - 'edit_by' => $this->me->id - ]); - $old_credit->origin->save(); - - $old_credit->change_action = null; - $old_credit->save(); - } - } + $now = new DateTime("now"); + $now->setTime(0,0,0); + $contract->finish_date = $now->getTimestamp(); + $contract->finish_date_by = $this->me->id; + try { + $saved = $contract->save(); + } catch(Exception $e) { + $saved = false; } - - $contract->finish_date = $now; - $contract->finish_date_by = $this->me->id; - if (!$contract->save()) { + if(!$saved) { $this->layout()->setFlash("Contract konnte nicht gespeichert werden", "error"); $this->redirect("Contract", "view", ['contract_id' => $id]); } + // create Journal $journal = ContractjournalModel::create([ 'contract_id' => $contract->id, @@ -620,6 +640,10 @@ class ContractController extends mfBaseController $contract_data['billing_delay'] = (int)$r->billing_delay; $contract_data['note'] = $r->note; + if($r->termination_id) { + $contract_data["termination_id"] = $r->termination_id; + } + if($r->order_date) { $order_date = new DateTime("@" . $this->dateToTimestamp($r->order_date)); $order_date->setTimezone(new DateTimeZone("Europe/Vienna")); diff --git a/application/Contract/ContractModel.php b/application/Contract/ContractModel.php index 7df9d3d30..f1ea89cf0 100644 --- a/application/Contract/ContractModel.php +++ b/application/Contract/ContractModel.php @@ -184,6 +184,108 @@ class ContractModel { return $contract; } + + public static function createCreditForContract($contract) { + $log = mfLoghandler::singleton(); + $me = new User(); + $me->loadMe(); + + if(!$contract->id) { + $log->warning(__METHOD__."(): Invalid Contractqueue object"); + return false; + } + + $product = $contract->product; + $owner = $contract->owner; + + if(!$product->id) { + $log->warning(__METHOD__."(): Invalid Product"); + return false; + } + + /* + * Get credit price from NNE or percentage of sales price + */ + $crediting_partner_id = false; + $crediting_partner_rate = false; + + $product_attribs = $product->attributes; + if(is_array($product_attribs) && array_key_exists("crediting_partner", $product_attribs) && $product_attribs["crediting_partner"] && is_object($product_attribs["crediting_partner"])) { + if($product_attribs["crediting_partner"]->value) { + $crediting_partner_id = $product_attribs["crediting_partner"]->value; + } + if($product_attribs["crediting_rate"]->value) { + $crediting_partner_rate = str_replace(",", ".",$product_attribs["crediting_rate"]->value); + } + } + + // or from netowner if anschluss product + if(!$crediting_partner_id && $contract->termination_id) { + $crediting_partner_id = $contract->termination->building->network->owner_id; + } + + $crediting_partner = new Address($crediting_partner_id); + if(!$crediting_partner->id) { + $log->error(__METHOD__.": Kein Crediting Partner gefunden für Credit für Contract ID ".$contract->id); + return false; + } + + // determine price: + // either NNE or percentage based, depends on product + $crediting_price = $product->price_nne; + if($crediting_partner_rate) { + $crediting_price = round($contract->price / 100 * $crediting_partner_rate, 4); + } + + if(!$crediting_price) return true; + $crediting_price *= -1; + + $data = []; + $data["orderproduct_id"] = null; + $data["owner_id"] = $crediting_partner_id; + $data["billingaddress_id"] = null; + $data["termination_id"] = null; + $data["product_id"] = $contract->product_id; + $data["product_name"] = $contract->product_name; + $data["product_info"] = $contract->product_info; + $data["amount"] = $contract->amount; + $data["sla_id"] = $contract->sla_id; + $data["product_external"] = $contract->product_external; + $data["product_external_id"] = $contract->product_external_id; + $data["billing_delay"] = $contract->billing_delay; + $data["billing_period"] = $contract->billing_period; + $data["contract_term"] = $contract->contract_term; + $data["order_date"] = $contract->order_date; + + $data["finish_date"] = $contract->finish_date; + $data["finish_date_by"] = $me->id; + $data["note"] = null; + + + // matchcode + $owner_address = $owner->street.", ".$owner->zip." ".$owner->city; + if($contract->termination_id) { + $termination = new Termination($contract->termination_id); + $termination_address = $termination->building->street.", ".$termination->building->zip." ".$termination->building->city; + $matchcode = $termination_address; + } else { + $matchcode = $owner_address; + } + + $matchcode = $contract->owner->getCompanyOrName()."; $matchcode"; + + $data["matchcode"] = $matchcode; + $data["price"] = $crediting_price; + $data["price_setup"] = 0; + $data["price_nne"] = 0; + $data["price_nbe"] = 0; + $data["vatgroup_id"] = $contract->vatgroup_id; + + + $credit = ContractModel::create($data); + + return $credit; + } public static function getAll() { $items = []; diff --git a/application/Contract/trigger/Finished.php b/application/Contract/trigger/Finished.php new file mode 100644 index 000000000..bc81bc6ce --- /dev/null +++ b/application/Contract/trigger/Finished.php @@ -0,0 +1,186 @@ + $value) { + if(in_array($name, ["log", "db", "contract", "me"])) { + $this->$name = $value; + } + } + } + + /** + * Checks Contract if can/must run + * return true if it wants to run + * @return bool + */ + public function precheck() { + $contract = $this->contract; + if($contract->finish_date && !$contract->_old_data->finish_date) { + // contract was just finished, so we need to run + return true; + } + } + + public function run() { + $contract = $this->contract; + if($contract->_old_data->finish_date) { + var_dump($contract->finish_date, $contract->_old_data->finish_date); + return true; + } + // contract was just finished, so we need to run + + /* + * Vorgänger Contracts kündigen + */ + foreach (ContractLinkModel::search(['contract_id' => $contract->id]) as $link) { + if (!in_array($link->type, ["upgrade", "downgrade", "relocation", "productchange", "ownerchange"])) { + continue; + } + + $now = new DateTime("now"); + $now->setTime(2,0,0); + + $cancel_date = clone($now); + $cancel_date->setTime(23,59,59); + + $origin = $link->origin; + $origin->cancel_date = $cancel_date->getTimestamp(); + $origin->cancel_date_by = $this->me->id; + $origin->edit_by = $this->me->id; + if (!$origin->save()) { + throw new Exception("Fehler beim Speichern der Kündigung des Vorgängercontracts!"); + } + // cancel journal + $journal = ContractjournalModel::create([ + 'contract_id' => $origin->id, + 'type' => "canceled", + 'value' => "", + 'text' => "" + ]); + $journal->save(); + + + /* + * alte Links übernehmen / kündigen + */ + foreach (ContractLinkModel::search(['type' => "link", 'contract_id' => $origin->id]) as $old_link) { + // verlinkten Contract kündigen (wenn nicht schon gekündigt) + if ($old_link->change_action == "cancel" && !$old_link->origin->cancel_date) { + $old_link->origin->update([ + 'cancel_date' => $now->getTimestamp(), + 'cancel_date_by' => $this->me->id, + 'edit_by' => $this->me->id + ]); + if(!$old_link->origin->save()) { + throw new Exception("Fehler beim Speichern der Kündigung des Vorgängercontracts!"); + } + + $old_link->change_action = null; + $old_link->save(); + + // cancel journal + $journal = ContractjournalModel::create([ + 'contract_id' => $old_link->origin->id, + 'type' => "canceled", + 'value' => "", + 'text' => "" + ]); + $journal->save(); + } + } + + foreach (ContractLinkModel::search(['type' => "link", 'origin_contract_id' => $origin->id]) as $old_link) { + // verlinkten Contract kündigen (wenn nicht schon gekündigt) + if ($old_link->change_action == "cancel" && !$old_link->contract->cancel_date) { + $old_link->contract->update([ + 'cancel_date' => $now->getTimestamp(), + 'cancel_date_by' => $this->me->id, + 'edit_by' => $this->me->id + ]); + //$this->log->debug(print_r($old_link->contract, true)); + $old_link->contract->save(); + + $old_link->change_action = null; + $old_link->save(); + + // cancel journal + $journal = ContractjournalModel::create([ + 'contract_id' => $old_link->contract->id, + 'type' => "canceled", + 'value' => "", + 'text' => "" + ]); + $journal->save(); + } + } + + foreach (ContractLinkModel::search(['type' => "credit", 'origin_contract_id' => $origin->id]) as $old_credit) { + if ($old_credit->change_action == "recreate" && !$old_credit->contract->cancel_date) { + // Alte Gutschrift kündigen und neue anlegen + //var_dump($old_credit->contract); + $old_credit->contract->update([ + 'cancel_date' => $now->getTimestamp(), + 'cancel_date_by' => $this->me->id, + 'edit_by' => $this->me->id + ]); + $old_credit->contract->save(); + //var_dump($old_credit->contract); + //exit; + + $old_credit->change_action = null; + $old_credit->save(); + + $new_credit = ContractModel::createCreditForContract($contract); + if(!$new_credit->save()) { + $this->log->debug(print_r($new_credit, true)); + throw new Exception("Fehler beim Speichern des neuen Gutschrift Contracts"); + } + + // create journal for credit + $journal = ContractjournalModel::create([ + 'contract_id' => $new_credit->id, + 'type' => "created_from", + 'value' => "productchange", + 'text' => "Produkt-/Standortwechsel von Contract ID ".$old_credit->contract_id + ]); + $journal->save(); + + + $this->log->debug(print_r($new_credit, true)); + + // create link to new credit contract + $link = ContractLinkModel::create([ + "contract_id" => $new_credit->id, + "origin_contract_id" => $contract->id, + "type" => "credit" + ]); + $link->save(); + + // create link from old to new credit contract + $link = ContractLinkModel::create([ + "contract_id" => $new_credit->id, + "origin_contract_id" => $old_credit->contract_id, + "type" => "upgrade" + ]); + $link->save(); + + $journal = ContractjournalModel::create([ + 'contract_id' => $old_credit->contract_id, + 'type' => "canceled", + 'value' => "", + 'text' => "" + ]); + $journal->save(); + } + } + + } + } +} \ No newline at end of file diff --git a/application/Contractqueue/ContractqueueModel.php b/application/Contractqueue/ContractqueueModel.php index 6f195a21a..ee066fa14 100644 --- a/application/Contractqueue/ContractqueueModel.php +++ b/application/Contractqueue/ContractqueueModel.php @@ -103,7 +103,7 @@ class ContractqueueModel { $data["billing_delay"] = $op->billing_delay; $data["billing_period"] = $op->billing_period; $data["order_date"] = $order->order_date; - + $data["finish_date"] = $order->finish_date; $data["finish_date_by"] = 1; $data["note"] = $order->note; diff --git a/db/migrations/20240716164608_contract_link_change_changeaction_to_varchar.php b/db/migrations/20240716164608_contract_link_change_changeaction_to_varchar.php new file mode 100644 index 000000000..730f6ec2c --- /dev/null +++ b/db/migrations/20240716164608_contract_link_change_changeaction_to_varchar.php @@ -0,0 +1,31 @@ +getEnvironment() == "thetool") { + $table = $this->table("ContractLink"); + $table->changeColumn("change_action", "string", ["null" => true, "default" => null, "limit" => 64]); + $table->update(); + } + + if($this->getEnvironment() == "addressdb") { + + } + } + + public function down(): void + { + if($this->getEnvironment() == "thetool") { + + } + + if($this->getEnvironment() == "addressdb") { + + } + } +}