From 217b19026ebe82dd5042a02fb6f6e83f9a6ee672 Mon Sep 17 00:00:00 2001 From: Frank Schubert Date: Sat, 22 Mar 2025 14:50:08 +0100 Subject: [PATCH] WIP PreorderBilling 2025-03-22 --- Layout/default/PreorderBilling/Index.php | 17 +- .../default/PreorderBillingInvoice/Index.php | 179 +++++++ .../PreorderBillingInvoice/PDF_FOOTER.html | 43 ++ .../PreorderBillingInvoice/PDF_HEADER.html | 143 ++++++ .../PreorderBillingInvoice/PDF_MAIN.php | 158 +++++++ application/Api/v1/AddressdbApicontroller.php | 2 +- .../PreorderBilling/PreorderBilling.php | 41 +- .../PreorderBillingController.php | 44 +- .../PreorderBillingCustomer.php | 2 +- .../PreorderBillingInvoice.php | 436 ++++++++++++++++++ .../PreorderBillingInvoiceController.php | 419 +++++++++++++++++ .../PreorderBillingInvoiceFile.php | 233 ++++++++++ .../PreorderBillingInvoiceposition.php | 221 +++++++++ .../PreorderProduct/PreorderProduct.php | 8 +- ..._billing_change_prices_to_two_decimals.php | 54 +++ ...123230_create_preorder_billing_invoice.php | 101 ++++ ...8_create_preorder_billing_invoice_file.php | 40 ++ 17 files changed, 2113 insertions(+), 28 deletions(-) create mode 100644 Layout/default/PreorderBillingInvoice/Index.php create mode 100644 Layout/default/PreorderBillingInvoice/PDF_FOOTER.html create mode 100644 Layout/default/PreorderBillingInvoice/PDF_HEADER.html create mode 100644 Layout/default/PreorderBillingInvoice/PDF_MAIN.php create mode 100644 application/PreorderBillingInvoice/PreorderBillingInvoice.php create mode 100644 application/PreorderBillingInvoice/PreorderBillingInvoiceController.php create mode 100644 application/PreorderBillingInvoiceFile/PreorderBillingInvoiceFile.php create mode 100644 application/PreorderBillingInvoiceposition/PreorderBillingInvoiceposition.php create mode 100644 db/migrations/20250321115602_preorder_billing_change_prices_to_two_decimals.php create mode 100644 db/migrations/20250321123230_create_preorder_billing_invoice.php create mode 100644 db/migrations/20250322112328_create_preorder_billing_invoice_file.php diff --git a/Layout/default/PreorderBilling/Index.php b/Layout/default/PreorderBilling/Index.php index 4167dd1f1..0d50fa095 100644 --- a/Layout/default/PreorderBilling/Index.php +++ b/Layout/default/PreorderBilling/Index.php @@ -108,7 +108,10 @@ $pagination_entity_name = "Billingrecords";
"> - Rechnungsdatensätze erstellen + Rechnungsdatensätze erstellen + + "> + Rechnungen erstellen
@@ -140,7 +143,13 @@ $pagination_entity_name = "Billingrecords"; preorder->adb_hausnummer->netzgebiet->name?> ["oaid" => $billing->oaid]])?>" target="_blank">oaid?> order_date?> - start_date?> -
end_date?> + + start_date == $billing->end_date): ?> + start_date?> + + start_date?> -
end_date?> + + company) ? $billing->company."
" : ""?> firstname || $billing->lastname) ? $billing->firstname." ".$billing->lastname."
" : ""?> @@ -154,8 +163,8 @@ $pagination_entity_name = "Billingrecords"; product_name?> amount / (int)$billing->amount > 1) ? number_format($billing->amount,3,",",".") : (int)$billing->amount?> - ">€ price,4,",",".")?> - ">€ price_setup,4,",",".")?> + ">€ price,2,",",".")?> + ">€ price_setup,2,",",".")?> billing_delivery == "email") ? "Email" : "Papier"?> diff --git a/Layout/default/PreorderBillingInvoice/Index.php b/Layout/default/PreorderBillingInvoice/Index.php new file mode 100644 index 000000000..6a38d4d8e --- /dev/null +++ b/Layout/default/PreorderBillingInvoice/Index.php @@ -0,0 +1,179 @@ +getUrl($Mod, "Index"); +$pagination_baseurl_params = ["filter" => $filter]; +$pagination_entity_name = "Rechnungen"; +?> + + + +
+
+
+
+ +
+

Rechnungen

+
+
+
+ + +
+
+ +
+
+

Filter

+ +
"> +
+ +
+ + "/> +
+
+ + +
+
+ + "/> +
+ +
+ + +
+
+ + "/> +
+
+ + "/> +
+ +
+ + +
+ +
+
+
+ + ?resetFilter=1">Filter zurücksetzen +
+
+
+ +
+
+ +
+
+ +
+
+

Rechnungen

+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
RechnungsnummerRechnungsdatumKundennummerRechnungsadresseNettoUst.Brutto
+ $invoice->id])?>"> invoice_number?> + pdf): ?> + + + date_delivered): ?> + date_delivered)?>"> + + invoice_date) ? (new DateTime($invoice->invoice_date))->format("d.m.Y") : ""?>fibu_account_number?> + company) ? $invoice->company."
" : ""?> + firstname || $invoice->lastname) ? $invoice->firstname." ".$invoice->lastname."
" : ""?> + street?>
+ zip?> city?> + country != "Österreich") ? "
".$invoice->country : ""?> +
total, 2, ",", ".")?>total_gross - $invoice->total, 2, ",", ".")?>total_gross, 2, ",", ".")?>
+ + + + + +
+
+ + +
+ +
+ +
+ +
+ + + \ No newline at end of file diff --git a/Layout/default/PreorderBillingInvoice/PDF_FOOTER.html b/Layout/default/PreorderBillingInvoice/PDF_FOOTER.html new file mode 100644 index 000000000..0a984b7d6 --- /dev/null +++ b/Layout/default/PreorderBillingInvoice/PDF_FOOTER.html @@ -0,0 +1,43 @@ + + + + Xinon Rechnung + + + + + + +
+
+ XINON GmbH | Fladnitz 150 | 8322 Studenzen
+ Tel.: +43 3115 40800 | E-Mail: office@xinon.at
+ UID: ATU68711968 | FN: 416556h | LG: Feldbach
+ IBAN: {{ bank_iban }} | BIC: {{ bank_bic }}
+
+ +
Seite von
+ +
+ + diff --git a/Layout/default/PreorderBillingInvoice/PDF_HEADER.html b/Layout/default/PreorderBillingInvoice/PDF_HEADER.html new file mode 100644 index 000000000..03b67d568 --- /dev/null +++ b/Layout/default/PreorderBillingInvoice/PDF_HEADER.html @@ -0,0 +1,143 @@ + + + + Invoice Header + + + + + +
+ + + + + + +
+ RML Infrastruktur GmbH
+ Wirtschaftspark A
+ 8940 Liezen
+ Österreich +
+ RML Logo +
+ + + + + + + + +
+
{{ addressLine_1 }}
+
{{ addressLine_2 }}
+
{{ addressLine_3 }}
+
{{ addressLine_4 }}
+
{{ addressLine_5 }}
+
+ + + + +
+ + + + + + + + + + {{ vatHtml }} + + + + + + + + + + + + + + + + + + + + +
Rechnungsdatum:{{ invoiceDate }}
Kundennummer:{{ customerNumber }}
 
Sachbearbeiter:{{ caseWorker }}
Telefon:{{ caseWorkerPhone }}
Email:{{ caseWorkerEmail }}
Unsere Auftragsnummer:{{ orderNumber }}
+
+
+ + +
+ + + diff --git a/Layout/default/PreorderBillingInvoice/PDF_MAIN.php b/Layout/default/PreorderBillingInvoice/PDF_MAIN.php new file mode 100644 index 000000000..4be41af7f --- /dev/null +++ b/Layout/default/PreorderBillingInvoice/PDF_MAIN.php @@ -0,0 +1,158 @@ +total; +$gross_total = $invoice->total_gross; +$is_credit = $net_total < 0; + +$invoice_date = new DateTime($invoice->invoice_date); + +$this->setReturnValue(['filename' => $invoice->invoice_number . ".pdf"]); + +//die(json_encode($invoice->positions)); + +?> + + + + + Rechnung + + + + + + + + + +
+ +

invoice_number?>

+ + + + + + + + + + + + + positions as $p): + $amount = (float) number_format($p->amount, 3, ",", "."); + $price = number_format($p->price, 2, ",","."); + $price_total = number_format($p->price_total, 2, ",","."); + $price_gross = number_format($p->price_gross, 2, ",","."); + $vatrate = number_format($p->vatrate, 0, ",","."); + + ?> + + "> + + + + + + + + + + + + + + + + + $vat_total): ?> + + 0): ?> + + + + + + + + + + + + + +
PosArtikelLeistung / ProduktPreisMengeEinheitPreis €Gesamt €
article_number?>article_name?>unit?>
Gesamt Netto:
+ USt. %:
Gesamt Brutto:
+ + +
+ tax_text): ?> +

tax_text?>

+ +
+ + + + \ No newline at end of file diff --git a/application/Api/v1/AddressdbApicontroller.php b/application/Api/v1/AddressdbApicontroller.php index 5d1b34f65..89f1ece78 100644 --- a/application/Api/v1/AddressdbApicontroller.php +++ b/application/Api/v1/AddressdbApicontroller.php @@ -939,7 +939,7 @@ class AddressdbApicontroller extends mfBaseApicontroller { ]; //$paid = $unit->enduser_setup_paid; - if(PreorderBilling::getFirst(["adb_wohneinheit_id" => $unit->id, "product_id" => $enduser_setup_product_id, "invoice_id" => false])) { + if(PreorderBillingInvoice::getFirst(["adb_wohneinheit_id" => $unit->id, "product_id" => $enduser_setup_product_id, "invoice_id" => false])) { $prices_return["enduser_setup_price_net"] = 0; $prices_return["enduser_setup_price_gross"] = 0; $prices_return["enduser_setup_info"] = "paid"; diff --git a/application/PreorderBilling/PreorderBilling.php b/application/PreorderBilling/PreorderBilling.php index 91fdd64af..a4d14e004 100644 --- a/application/PreorderBilling/PreorderBilling.php +++ b/application/PreorderBilling/PreorderBilling.php @@ -77,7 +77,7 @@ class PreorderBilling extends mfBaseModel { $model = new PreorderBilling(); $table_fields = [ - "invoice_id", "preorder_id", "oaid", "adb_wohneinheit_id", "order_date", "start_date", "end_date", "preorderbillingcustomer_id", "owner_id", + "netowner_id","invoice_id", "preorder_id", "oaid", "adb_wohneinheit_id", "order_date", "start_date", "end_date", "preorderbillingcustomer_id", "owner_id", "billingaddress_id", "fibu_account_number", "company", "firstname", "lastname", "street", "zip", "city", "country", "email", "uid", "billing_delivery", "product_id", "product_name", "product_info", "article_number", "amount", "unit", "price", "price_setup", "vatrate", "billing_period", "create_by","edit_by","create","edit" @@ -102,6 +102,31 @@ class PreorderBilling extends mfBaseModel { return $model; } + public static function getInvoiceBaseData($filter) { + $db = FronkDB::singleton(); + + $items = []; + + $where = self::getSqlFilter($filter); + $sql = "SELECT preorderbillingcustomer_id, owner_id, billingaddress_id, billing_delivery FROM PreorderBilling + WHERE $where + GROUP BY preorderbillingcustomer_id, owner_id, billingaddress_id, billing_delivery + ORDER BY preorderbillingcustomer_id, owner_id, billingaddress_id, billing_delivery"; + //var_dump($sql);exit; + $res = $db->query($sql); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[] = [ + "preorderbillingcustomer_id" => $data->preorderbillingcustomer_id, + "owner_id" => $data->owner_id, + "billingaddress_id" => $data->billingaddress_id, + "billing_delivery" => $data->billing_delivery, + ]; + } + } + return $items; + } + public static function getAll() { $items = []; @@ -197,6 +222,13 @@ class PreorderBilling extends mfBaseModel { private static function getSqlFilter($filter) { $where = "1=1 "; + if(array_key_exists("netowner_id", $filter)) { + $netowner_id = $filter['netowner_id']; + if(is_numeric($netowner_id)) { + $where .= " AND PreorderBilling.netowner_id=$netowner_id"; + } + } + if(array_key_exists("invoice_id", $filter)) { $invoice_id = $filter['invoice_id']; if($invoice_id === null || $invoice_id === false) { @@ -208,6 +240,13 @@ class PreorderBilling extends mfBaseModel { } } + if(array_key_exists("preorderbillingcustomer_id", $filter)) { + $preorderbillingcustomer_id = $filter['preorderbillingcustomer_id']; + if(is_numeric($preorderbillingcustomer_id)) { + $where .= " AND PreorderBilling.preorderbillingcustomer_id=$preorderbillingcustomer_id"; + } + } + if(array_key_exists("preorder_id", $filter)) { $preorder_id = $filter['preorder_id']; if(is_numeric($preorder_id)) { diff --git a/application/PreorderBilling/PreorderBillingController.php b/application/PreorderBilling/PreorderBillingController.php index d12b34ecb..07a4a4349 100644 --- a/application/PreorderBilling/PreorderBillingController.php +++ b/application/PreorderBilling/PreorderBillingController.php @@ -166,7 +166,7 @@ class PreorderBillingController extends mfBaseController { $preorder_search = [ "preordercampaign_id" => $campaign_ids, - ">=status_code" => 241, + ">=status_code" => 244, //"oaid" => "AT-8943-a1116acf.001", ]; @@ -193,7 +193,7 @@ class PreorderBillingController extends mfBaseController { $order_date->setTime(4,0,0); $order_date->setTimezone(new DateTimeZone("Europe/Vienna")); - if($order_date->format("Ymd") < "2025-01-01") { + if($order_date->format("Ymd") < $earliest_bill_date->format("Ymd")) { // start billing from 2025-01-01 $this->log->debug(__METHOD__.": Preorder ".$preorder->id." ordered before 2025-01-01, so billing no enduser setup"); $bill_enduser_setup = false; @@ -255,7 +255,7 @@ class PreorderBillingController extends mfBaseController { if($preorder->status->code >= 899) { // TODO is canceled, need to determine if setup still needs to be billed - $this->log->debug(__METHOD__.": already cancelled"); + $this->log->debug(__METHOD__.": Preorder ".$preorder->id." / ".$preorder->oaid." already cancelled"); return true; } @@ -288,20 +288,20 @@ class PreorderBillingController extends mfBaseController { if(!$status_change) { $status_change = PreorderHistoryModel::getFirstStatusChangeToOrHigher($preorder->id, 241); } - if(!$status_change) { + + if($status_change) { + $status_change_date = new DateTime("@".$status_change->changed); + } else { $this->log->debug(__METHOD__.": No status change found for preorder ".$preorder->id." so using creation date"); - $status_change = $preorder; + $status_change_date = new DateTime("@".$preorder->create); } - - - $status_change_date = new DateTime("@".$status_change->create); - if($preorder->oaid == 'AT-8943-8392e815.001') { //var_dump($status_change, $status_change_date); } $billing_data = [ + "netowner_id" => $netowner->id, "preorder_id" => $preorder->id, "oaid" => $preorder->oaid, "adb_wohneinheit_id" => $preorder->adb_wohneinheit_id, @@ -315,7 +315,7 @@ class PreorderBillingController extends mfBaseController { "amount" => 1, "unit" => "Stk.", "price" => "0", - "price_setup" => $price->price_setup, + "price_setup" => round($price->price_setup, 2), "vatrate" => 20, "billing_period" => 0, ]; @@ -323,6 +323,10 @@ class PreorderBillingController extends mfBaseController { if($type == "enduser_setup") { // Endkunde Setup Gebühr + if($order_date->format("Ymd") < $earliest_bill_date->format("Ymd")) { + return true; + } + if(PreorderBilling::getFirst(["adb_wohneinheit_id" => $preorder->adb_wohneinheit_id, "product_id" => $product->id])) { $this->log->debug(__METHOD__.": billing record for enduser setup for wohneinheit ".$preorder->adb_wohneinheit_id." exists"); return true; // already billed @@ -358,13 +362,13 @@ class PreorderBillingController extends mfBaseController { $billing_data["preorderbillingcustomer_id"] = $customer->id; $billing_data["fibu_account_number"] = $customer->fibu_account_number; - $billing_data["product_name"] = "Bereitstellungsentgelt Glasfaseranschluss"; + $billing_data["product_name"] = "Herstellungsentgelt Glasfaser-Internetanschluss"; $billing_data["product_info"] = "Bestellung vom ".$order_date->format("d.m.Y"); } elseif($type == "operator_setup") { $change_to_active = PreorderHistoryModel::getFirstStatusChangeTo($preorder->id, 500); if($change_to_active) { - $status_change_date = new DateTime("@".$change_to_active->create); + $status_change_date = new DateTime("@".$change_to_active->changed); $billing_data["start_date"] = $status_change_date->format("Y-m-d"); $billing_data["end_date"] = $status_change_date->format("Y-m-d"); } @@ -374,7 +378,7 @@ class PreorderBillingController extends mfBaseController { } // Netzbetreiber Setup Gebühr - $billing_data["product_name"] = "Brereitstellungsentgelt ".$bill_date->format("m/Y"); + $billing_data["product_name"] = "Brereitstellungsentgelt ".$status_change_date->format("m/Y"); $billing_data["owner_id"] = $netoperator->id; $billing_data["billingaddress_id"] = $netoperator->id; $billing_data["fibu_account_number"] = $netoperator->attributes['rml-fibu-account']->value; @@ -391,7 +395,6 @@ class PreorderBillingController extends mfBaseController { die("Unknown billing type $type"); } - $billing = PreorderBilling::create($billing_data); if(!$billing->save()) { die("Billing record could not be saved!"); @@ -412,7 +415,7 @@ class PreorderBillingController extends mfBaseController { $latest_bill_date = $options['latest_bill_date']; if($preorder->status->code >= 899) { - $this->log->debug(__METHOD__.": Preorder is cancelled"); + $this->log->debug(__METHOD__.": Preorder ".$preorder->id." / ".$preorder->oaid." is cancelled"); // TODO is cancelled, so determine if refund is necessary return true; } @@ -433,12 +436,14 @@ class PreorderBillingController extends mfBaseController { //var_dump($product, $price);exit; $status_change = PreorderHistoryModel::getLastStatusChangeTo($preorder->id, 500); - if(!$status_change) { + if($status_change) { + $status_change_date = new DateTime("@".$status_change->changed); + } else { $this->log->debug(__METHOD__.": No status change to 500 found for preorder ".$preorder->id." so using creation date"); - $status_change = $preorder; + $status_change_date = new DateTime("@".$preorder->create); + } - $status_change_date = new DateTime("@".$status_change->create); if($status_change_date->format("Ymd") > $earliest_bill_date->format("Ymd")) { $earliest_bill_date = $status_change_date; } @@ -522,7 +527,7 @@ class PreorderBillingController extends mfBaseController { if ($period_days < 0) return true; // don't bill for negative time range $pc = $period_days / $total_days * 100; - $bill_price = round($price->price_inet / 100 * $pc, 4); + $bill_price = round($price->price_inet / 100 * $pc, 2); } $article_number = $product->article_number; @@ -531,6 +536,7 @@ class PreorderBillingController extends mfBaseController { } $billing_data = [ + "netowner_id" => $netowner->id, "preorder_id" => $preorder->id, "oaid" => $preorder->oaid, "adb_wohneinheit_id" => $preorder->adb_wohneinheit_id, diff --git a/application/PreorderBillingCustomer/PreorderBillingCustomer.php b/application/PreorderBillingCustomer/PreorderBillingCustomer.php index b05aca065..3bc68d68f 100644 --- a/application/PreorderBillingCustomer/PreorderBillingCustomer.php +++ b/application/PreorderBillingCustomer/PreorderBillingCustomer.php @@ -22,7 +22,7 @@ class PreorderBillingCustomer extends mfBaseModel if($last_fibu_account_number_row) { $new_num = $last_fibu_account_number_row->fibu_account_number + 1; } else { - $new_num = 250001; + $new_num = TT_PREORDER_BILLING[$netowner_id]["first-cust-num"]; } $this->fibu_account_number = $new_num; diff --git a/application/PreorderBillingInvoice/PreorderBillingInvoice.php b/application/PreorderBillingInvoice/PreorderBillingInvoice.php new file mode 100644 index 000000000..9556324da --- /dev/null +++ b/application/PreorderBillingInvoice/PreorderBillingInvoice.php @@ -0,0 +1,436 @@ + true, "netowner_id" => $netowner_id]); + if(!$last_invoice || !$last_invoice->invoice_number) { + return false; + } + return $last_invoice->invoice_number; + } + + public function createPdf() { + if($this->id) { + $invoice_number = $this->invoice_number; + $invoice_date = new DateTime($this->invoice_date); + } else { + $invoice_number = "PROFORMA"; + $invoice_date = new DateTime("@1"); + } + + $filename = ""; + $positions = $this->getProperty("positions"); + + $vat = []; + foreach ($positions as $p) { + if (!array_key_exists($p->vatrate, $vat)) { + $vat[$p->vatrate] = 0; + } + $vat[$p->vatrate] += $p->price_gross - ($p->price * $p->amount); + } + + $pdf_vars = [ + "invoice" => $this, + "vat" => $vat, + ]; + + $me = new User(); + $me->loadMe(); + + // Replace placeholders in header + $headerHtml = file_get_contents(BASEDIR . "/Layout/default/PreorderBillingInvoice/PDF_HEADER.html"); + $headerHtml = str_replace("{{ basedir }}", BASEDIR, $headerHtml); + $headerHtml = str_replace("{{ addressLine_1 }}", $this->company ? $this->company : "", $headerHtml); + $headerHtml = str_replace("{{ addressLine_2 }}", $this->firstname . " " . $this->lastname, $headerHtml); + $headerHtml = str_replace("{{ addressLine_3 }}", nl2br($this->street), $headerHtml); + $headerHtml = str_replace("{{ addressLine_4 }}", $this->zip . " " . $this->city, $headerHtml); + $headerHtml = str_replace("{{ addressLine_5 }}", $this->country != "Österreich" ? $this->country : "", $headerHtml); + $headerHtml = str_replace("{{ customerNumber }}", $this->fibu_account_number, $headerHtml); + $headerHtml = str_replace("{{ invoiceNumber }}", $invoice_number, $headerHtml); + $headerHtml = str_replace("{{ invoiceDate }}", $invoice_date->format("d.m.Y"), $headerHtml); + $headerHtml = str_replace("{{ caseWorker }}", $me->name, $headerHtml); + $headerHtml = str_replace("{{ caseWorkerPhone }}", $me->mobile, $headerHtml); + $headerHtml = str_replace("{{ caseWorkerEmail }}", $me->email, $headerHtml); + $headerHtml = str_replace("{{ orderNumber }}", "WVR / 52", $headerHtml); + $headerHtml = str_replace("{{ vatHtml }}", $this->uid ? "Ihre UID:" . $this->uid . "" : "", $headerHtml); + + $headerFile = BASEDIR . "/var/temp/invoice_header-" . date("U") . "-" . rand(1000, 9999) . ".html"; + file_put_contents($headerFile, $headerHtml); + + + // Replace placeholders in header + $footerHtml = file_get_contents(BASEDIR . "/Layout/default/PreorderBillingInvoice/PDF_FOOTER.html"); + + $footerFile = BASEDIR . "/var/temp/preorderbillinginvoice_footer-" . date("U") . "-" . rand(1000, 9999) . ".html"; + file_put_contents($footerFile, $footerHtml); + + + $pdf = new PdfForm("PreorderBillingInvoice/PDF_MAIN", $pdf_vars); + $wkhtmltopdfArgs = "--header-html $headerFile --footer-html $footerFile"; + + $filename = $pdf->render($wkhtmltopdfArgs); + + return $filename; + + } + + + + public function getProperty($name) { + if($this->$name == null) { + + if($name == "positions") { + $positions = PreorderBillingInvoiceposition::search(["invoice_id" => $this->id]); + $this->positions = $positions; + return $this->positions; + } + + if($name == "pdf") { + $ifile = PreorderBillingInvoiceFile::getFirst(["invoice_id" => $this->id]); + if(!$ifile) return null; + + $file = $ifile->file; + if(!$file) return null; + + $this->pdf = $file; + return $this->pdf; + } + + if($name == "creator") { + $creator = mfValuecache::singleton()->get("Worker-id-".$this->create_by); + if($creator) { + $this->creator = $creator; + return $this->creator; + } + $this->creator = new User($this->create_by); + + if(!$this->creator->id) { + return null; + } + mfValuecache::singleton()->set("Worker-id-".$this->create_by, $this->creator); + return $this->creator; + } + + if($name == "editor") { + $editor = mfValuecache::singleton()->get("Worker-id-".$this->edit_by); + if($editor) { + $this->editor = $editor; + return $this->editor; + } + $this->editor = new User($this->edit_by); + if(!$this->editor->id) { + return null; + } + 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; + + } + + /******************************** + * Begin static Model functions + */ + + public static function create(Array $data) { + $model = new PreorderBillingInvoice(); + + $table_fields = [ + "netowner_id","invoice_number","invoice_date","preorderbillingcustomer_id","owner_id","billingaddress_id","fibu_account_number","fibu_cost_area", + "fibu_cost_account", "fibu_revenue_account","fibu_taxcode","tax_text","company","firstname","lastname","street","zip","city","country","email","uid", + "billing_type", "billing_delivery","bank_account_bank","bank_account_owner","bank_account_iban","bank_account_bic","total","total_gross","vatgroup_id", + "vatrate", "bmd_export_date","date_delivered", + "create_by","edit_by","create","edit" + ]; + + foreach($data as $field => $value) { + if(in_array($field, $table_fields)) { + $model->$field = $value; + } + } + + $me = new User(); + $me->loadMe(); + + if($model->create_by === null) { + $model->create_by = $me->id; + } + if($model->edit_by === null) { + $model->edit_by = $me->id; + } + + return $model; + } + + public static function getAll() { + $items = []; + + $db = FronkDB::singleton(); + + $res = $db->select("PreorderBillingInvoice", "*", "1 = 1 ORDER BY id"); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[] = new PreorderBillingInvoice($data); + } + } + return $items; + + } + + public static function getFirst($filter) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT PreorderBillingInvoice.* FROM PreorderBillingInvoice + WHERE $where + ORDER BY id LIMIT 1"; + + //mfLoghandler::singleton()->debug($sql); + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + $item = new PreorderBillingInvoice($data); + if($item->id) { + return $item; + } else { + return null; + } + } + return null; + } + + public static function getLast($filter) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + + $sql = "SELECT PreorderBillingInvoice.* FROM PreorderBillingInvoice + WHERE $where + ORDER BY id DESC LIMIT 1"; + + mfLoghandler::singleton()->debug($sql); + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + $item = new PreorderBillingInvoice($data); + if($item->id) { + return $item; + } else { + return null; + } + } + return null; + } + + public static function count($filter) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT COUNT(*) as cnt FROM PreorderBillingInvoice + WHERE $where"; + + //mfLoghandler::singleton()->debug($sql); + + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + return $data->cnt; + } + return 0; + } + + public static function search($filter, $limit = false, $order = false) { + //var_dump($filter);exit; + $items = []; + + if(!$order) { + $order = "id ASC"; + } + + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT PreorderBillingInvoice.* FROM PreorderBillingInvoice + WHERE $where + ORDER BY $order"; + + if(is_array($limit) && count($limit)) { + if(is_numeric($limit['start']) && is_numeric($limit['count'])) { + $sql .= " LIMIT ".$limit['start'].", ".$limit['count']; + } elseif(is_numeric($limit['count'])) { + $sql .= " LIMIT ".$limit['count']; + } + } + + mfLoghandler::singleton()->debug($sql); + + $res = $db->query($sql); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[$data->id] = new PreorderBillingInvoice($data); + } + } + + return $items; + } + + private static function getSqlFilter($filter) { + $where = "1=1 "; + + if(array_key_exists("invoice_number", $filter)) { + $invoice_number = $filter['invoice_number']; + if($invoice_number === null || $invoice_number === false) { + $where .= " AND (PreorderBillingInvoice.invoice_number IS NULL OR PreorderBillingInvoice.invoice_number=0)"; + } elseif($invoice_number === true) { + $where .= " AND (PreorderBillingInvoice.invoice_number IS NOT NULL AND PreorderBillingInvoice.invoice_number > 0)"; + } elseif(is_numeric($invoice_number)) { + $where .= " AND PreorderBillingInvoice.invoice_number=$invoice_number"; + } + } + + if(array_key_exists("invoice_date", $filter)) { + $invoice_date = FronkDB::singleton()->escape($filter['invoice_date']); + if($invoice_date) { + $where .= " AND PreorderBillingInvoice.invoice_date='$invoice_date'"; + } + } + + if(array_key_exists("preorderbillingcustomer_id", $filter)) { + $preorderbillingcustomer_id = $filter['preorderbillingcustomer_id']; + if(is_numeric($preorderbillingcustomer_id)) { + $where .= " AND PreorderBillingInvoice.preorderbillingcustomer_id=$preorderbillingcustomer_id"; + } + } + + if(array_key_exists("netowner_id", $filter)) { + $netowner_id = $filter['netowner_id']; + if(is_numeric($netowner_id)) { + $where .= " AND PreorderBillingInvoice.netowner_id=$netowner_id"; + } + } + + if(array_key_exists("owner_id", $filter)) { + $owner_id = $filter['owner_id']; + if(is_numeric($owner_id)) { + $where .= " AND PreorderBillingInvoice.owner_id=$owner_id"; + } + } + + if(array_key_exists("billingaddress_id", $filter)) { + $billingaddress_id = $filter['billingaddress_id']; + if(is_numeric($billingaddress_id)) { + $where .= " AND PreorderBillingInvoice.billingaddress_id=$billingaddress_id"; + } + } + + if (array_key_exists("company", $filter)) { + $company = FronkDB::singleton()->escape($filter["company"]); + if ($company) { + $where .= " AND company like '%$company%'"; + } + } + + if (array_key_exists("firstname", $filter)) { + $firstname = FronkDB::singleton()->escape($filter["firstname"]); + if ($firstname) { + $where .= " AND firstname like '%$firstname%'"; + } + } + + if (array_key_exists("lastname", $filter)) { + $lastname = FronkDB::singleton()->escape($filter["lastname"]); + if ($lastname) { + $where .= " AND lastname like '%$lastname%'"; + } + } + + if (array_key_exists("mergedName", $filter)) { + $name = FronkDB::singleton()->escape($filter["mergedName"]); + if ($name) { + $where .= " AND (CONCAT(firstname, ' ', lastname) like '%$name%' OR CONCAT(lastname, ' ', firstname) like '%$name%' )"; + } + } + + if (array_key_exists("street", $filter)) { + $street = FronkDB::singleton()->escape($filter["street"]); + if ($street) { + $where .= " AND street like '%$street%'"; + } + } + + if (array_key_exists("zip", $filter)) { + $zip = FronkDB::singleton()->escape($filter["zip"]); + if ($zip) { + $where .= " AND zip like '%$zip%'"; + } + } + + if (array_key_exists("city", $filter)) { + $city = FronkDB::singleton()->escape($filter["city"]); + if ($city) { + $where .= " AND city like '%$city%'"; + } + } + + if (array_key_exists("country", $filter)) { + $country = FronkDB::singleton()->escape($filter["country"]); + if ($country) { + $where .= " AND country like '%$country%'"; + } + } + + if (array_key_exists("email", $filter)) { + $email = FronkDB::singleton()->escape($filter["email"]); + if ($email) { + $where .= " AND email like '%$email%'"; + } + } + + if(array_key_exists("billing_delivery", $filter)) { + $billing_delivery = FronkDB::singleton()->escape($filter['billing_delivery']); + if($billing_delivery) { + $where .= " AND PreorderBillingInvoice.billing_delivery = '$billing_delivery'"; + } + } + + + if(array_key_exists("add-where", $filter)) { + $where .= " ".$filter['add-where']; + } + + //var_dump($filter, $where);exit; + return $where; + } + +} \ No newline at end of file diff --git a/application/PreorderBillingInvoice/PreorderBillingInvoiceController.php b/application/PreorderBillingInvoice/PreorderBillingInvoiceController.php new file mode 100644 index 000000000..78c9b075d --- /dev/null +++ b/application/PreorderBillingInvoice/PreorderBillingInvoiceController.php @@ -0,0 +1,419 @@ +needlogin = true; + $me = new User(); + $me->loadMe(); + $this->me = $me; + $this->layout()->set("me", $me); + + if(!$me->can(["preorderbilling", "preorderbillingReadonly"])) { + $this->redirect("Dashboard"); + } + } + + protected function indexAction() : void { + $this->layout()->setTemplate("PreorderBillingInvoice/Index"); + + if ($this->request->resetFilter) { + unset($_SESSION[MFAPPNAME . '-PreorderBillingInvoice-filter']); + } + + $filter = []; + if (is_array($this->request->filter)) { + $filter = $this->request->filter; + $_SESSION[MFAPPNAME . '-PreorderBillingInvoice-filter'] = $filter; + } else { + if (array_key_exists(MFAPPNAME . '-PreorderBillingInvoice-filter', $_SESSION) && count($_SESSION[MFAPPNAME . '-PreorderBillingInvoice-filter'])) { + $filter = $_SESSION[MFAPPNAME . '-PreorderBillingInvoice-filter']; + } + } + + $this->layout->set("filter", $filter); + $filter = $this->getPreparedFilter($filter); + + // pagination defaults + $pagination = []; + $pagination['start'] = 0; + $pagination['count'] = 25; + $pagination['maxItems'] = 0; + + if (is_numeric($this->request->s)) { + $pagination['start'] = intval($this->request->s); + } + + $filter["netowner_id"] = $this->me->address_id; + $pagination['maxItems'] = PreorderBillingInvoice::count($filter); + + $invoices = PreorderBillingInvoice::search($filter, $pagination); + $this->layout()->set("invoices", $invoices); + $this->layout()->set("pagination", $pagination); + + } + + private function getPreparedFilter($filter) { + $new_filter = []; + + if(array_key_exists("start_date_from", $filter)) { + if($filter["start_date_from"]) { + try { + $from = DateTime::createFromFormat("d.m.Y", $filter["start_date_from"]); + } catch (Exception $e) {} + $new_filter["start_date>="] = $from->format("Y-m-d"); + } + unset($filter["start_date_from"]); + } + + if(array_key_exists("start_date_to", $filter)) { + if($filter["start_date_to"]) { + try { + $to = DateTime::createFromFormat("d.m.Y", $filter["start_date_to"]); + } catch (Exception $e) {} + $new_filter["start_date<="] = $to->format("Y-m-d"); + } + unset($filter["start_date_to"]); + } + + if (is_array($filter) && count($filter)) { + foreach ($filter as $name => $value) { + $new_filter[$name] = $value; + } + } + + return $new_filter; + } + + protected function downloadInvoice() { + $id = $this->request->id; + if (!is_numeric($id) || !$id) { + $this->layout()->setFlash("Rechnung nicht gefunden", "error"); + $this->redirect("Invoice"); + } + + $invoice = new PreorderBillingInvoice($id); + if (!$invoice->id) { + $this->layout()->setFlash("Rechnung nicht gefunden", "error"); + $this->redirect("Invoice"); + } + + $pdf = $invoice->pdf; + $pdf = false; + //var_dump($pdf, !$pdf);exit; + //var_dump($pdf->name);exit; + if(!$pdf || !$pdf->name) { + $ifile = PreorderBillingInvoiceFile::createFromInvoice($invoice); + if(!$ifile) { + $this->layout()->setFlash("Fehler beim PDF erstellen"); + $this->redirect("PreorderBillingInvoice"); + } + $pdf = $ifile->file; + } + + $pdf_path = $pdf->getFullPath(); + $filename = $pdf->filename; + + if(!file_exists($pdf_path)) { + $this->layout()->setFlash("PDF-Datei nicht gefunden"); + $this->redirect("Invoice"); + } + + header('Content-Type: application/octet-stream'); + header('Content-disposition: attachment; filename="'.$filename.'"'); + header('Content-Transfer-Encoding: binary'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Content-Type: ' . mime_content_type($pdf_path)); + header("Content-Length: " . filesize($pdf_path)); + + readfile($pdf_path); + exit; + } + + protected function createAction() { + $netowner_id = $this->me->address_id; + $today = new DateTime(); + + $billing_row_count = 0; + $positions_count = 0; + $bill_id_count = 0; + //$products = PreorderProduct::getAll(true); + + $base_rows = PreorderBilling::getInvoiceBaseData(['invoice_id' => null, "netowner_id" => $netowner_id]); + //var_dump($base_rows);exit; + $invoice_count = 0; + $invoice_detail_data = []; + foreach($base_rows as $base) { + $preorderbillingcustomer_id = $base["preorderbillingcustomer_id"]; + $owner_id = $base["owner_id"]; + $billingaddress_id = $base["billingaddress_id"]; + $billing_delivery = $base["billing_delivery"]; + + $bill_positions = []; + $credit_positions = []; + $invoice_voicenumbers = []; + + $bill_filter = [ + "netowner_id" => $netowner_id, + "preorderbillingcustomer_id" => $preorderbillingcustomer_id, + "owner_id" => $owner_id, + "billingaddress_id" => $billingaddress_id, + "billing_delivery" => $billing_delivery, + "invoice_id" => null, + ]; + + + + $billing_rows = PreorderBilling::search($bill_filter); + if(!count($billing_rows)) { + var_dump($bill_filter); + die("Keine Billing rows"); + continue; + } + + $positions_to_sort = []; + $sorted_positions = []; + $invoice_positions = []; + $invoice_vatrate = 20; + $price_total_sum = 0; + + foreach($billing_rows as $bill) { + $billing_row_count++; + $netoperator_id = false; + if ($bill->owner_id) { + $netoperator_id = $bill->owner_id; + } + + $price = 0; + $price_gross = 0; + if ($bill->price > 0.000) { + $price = $bill->price; + } + if ($bill->price_setup > 0.000) { + $price = $bill->price_setup; + } + + if ($bill->vatrate > 0 && $price) { + $price_gross = $price + ($price / 100 * $bill->vatrate); + } + + $start_date = new DateTime($bill->start_date); + $end_date = new DateTime($bill->end_date); + $year_month = $start_date->format("Ym"); + + if ($netoperator_id) { + if (!array_key_exists($netoperator_id, $invoice_detail_data)) $invoice_detail_data[$netoperator_id] = []; + $detail_data = [ + "owner_id" => $bill->owner_id, + "company" => ($bill->company) ?: $bill->firstname . " " . $bill->lastname, + "oaid" => $bill->oaid, + "order_date" => $bill->order_date, + "start_date" => $start_date->format("Y-m-d"), + "end_date" => $end_date->format("Y-m-d"), + "product_name" => $bill->product_name, + "amount" => $bill->amount, + "billing_periodic" => $bill->billing_period, + "price" => $price, + "price_gross" => $price_gross, + "vatrate" => $bill->vatrate, + ]; + $invoice_detail_data[$netoperator_id][] = $detail_data; + } + + $position_data = []; + + $position_data["product_id"] = $bill->product_id; + $position_data["article_number"] = $bill->article_number; + $position_data["product_name"] = $bill->product_name; + $position_data["product_info"] = $bill->product_info; + $position_data["total"] = round($price, 2); + $position_data["total_gross"] = round($price_gross, 2); + $position_data["vatrate"] = $bill->vatrate; + $position_data["billing_id"] = $bill->id; + + if (!array_key_exists($bill->product_id, $invoice_positions)) { + $invoice_positions[$bill->product_id] = []; + } + if (!array_key_exists($year_month, $invoice_positions[$bill->product_id])) { + $invoice_positions[$bill->product_id][$year_month] = []; + } + $invoice_positions[$bill->product_id][$year_month][] = $position_data; + + } + + //var_dump($invoice_positions);exit; + foreach($invoice_positions as $product_id => $year_months) { + foreach($year_months as $year_month => $positions) { + $new_pos_data = [ + "count" => 0, + "amount" => 0, + "unit" => "Stk", + "price" => 0, + "price_total" => 0, + "price_gross" => 0, + "vatrate" => 20, + "preorder_billings" => [], + ]; + foreach($positions as $position) { + //var_dump($position);exit; + $new_pos_data["count"]++; + $new_pos_data["amount"]++; + $new_pos_data["price"] = $position["total"]; + $new_pos_data["price_total"] += $position["total"]; + $new_pos_data["price_gross"] += $position["total_gross"]; + $new_pos_data["vatrate"] = $position_data["vatrate"]; + $new_pos_data["product_id"] = $position["product_id"]; + $new_pos_data["article_number"] = $position["article_number"]; + $new_pos_data["article_name"] = $position["product_name"]; + $new_pos_data["preorder_billings"][] = $position["billing_id"]; + + $positions_count++; + } + if($product_id == 3) { + $new_pos_data["amount"] = 1; + $new_pos_data["unit"] = "Pau"; + $new_pos_data["price"] = $new_pos_data["price_total"]; + } + + $new_position = PreorderBillingInvoiceposition::create($new_pos_data); + $new_position->preorder_billings = $new_pos_data["preorder_billings"]; + $new_position->count = $new_pos_data["count"]; + + $sort_key = round(3 / $product_id)."-".$year_month; + $positions_to_sort[$sort_key] = $new_position; + + } + //var_dump($year_months, $new_pos_data); + //var_dump($year_month, $product_id, $positions);exit; + + + + + } + + ksort($positions_to_sort); + $positions_to_sort = array_reverse($positions_to_sort); + foreach($positions_to_sort as $key => $sort_pos) { + $sorted_positions[] = $sort_pos; + } + + + //exit; + //var_dump($sorted_positions);exit; + //exit; + + + $invoice_data = []; + $invoice_data["netowner_id"] = $netowner_id; + $invoice_data["preorderbillingcustomer_id"] = $bill->preorderbillingcustomer_id; + $invoice_data["owner_id"] = $bill->owner_id; + $invoice_data["billingaddress_id"] = $bill->billingaddress_id; + $invoice_data["fibu_account_number"] = $bill->fibu_account_number; + $invoice_data["fibu_cost_account"] = TT_PREORDER_BILLING[$netowner_id]["fibu-cost-code"]; + $invoice_data["fibu_revenue_account"] = TT_PREORDER_BILLING[$netowner_id]["fibu-revenue-code"]; + $invoice_data["fibu_tax_code"] = 1; + $invoice_data["tax_text"] = null; + $invoice_data["uid"] = $bill->uid; + $invoice_data["company"] = $bill->company; + $invoice_data["firstname"] = $bill->firstname; + $invoice_data["lastname"] = $bill->lastname; + $invoice_data["street"] = $bill->street; + $invoice_data["zip"] = $bill->zip; + $invoice_data["city"] = $bill->city; + $invoice_data["country"] = $bill->country; + $invoice_data["email"] = $bill->email; + $invoice_data["billing_type"] = "invoice"; + $invoice_data["billing_delivery"] = "email"; + $invoice_data["total"] = 0; + $invoice_data["total_gross"] = 0; + + + $invoice = PreorderBillingInvoice::create($invoice_data); + $invoice->startTransaction(); + + try { + if (!$invoice->save()) { + var_dump($invoice); + $invoice->rollbackTransaction(); + die("Error saving Invoice"); + } + + $total_net = 0; + $total_gross = 0; + + foreach($sorted_positions as $position) { + //var_dump($position); + $this->log->debug(__METHOD__.": count: ".$position->count); + $billing_ids = $position->preorder_billings; + $bill_id_count += count($billing_ids); + unset($position->preorder_billings); + unset($position->count); + + $total_net += $position->price_total; + $total_gross += $position->price_gross; + + if (!$position->save()) { + $invoice->rollbackTransaction(); + die("Error saving Invoiceposition"); + } + + $position->invoice_id = $invoice->id; + if (!$position->save()) { + $invoice->rollbackTransaction(); + die("Error saving Invoiceposition"); + } + + // add Invoice::id to Bill + foreach($billing_ids as $bill_id) { + $pbill = new PreorderBilling($bill_id); + if (!$pbill->id) { + $invoice->rollbackTransaction(); + die("Bill for Invoiceposition not found"); + } + $pbill->invoice_id = $invoice->id; + + if (!$pbill->save()) { + $invoice->rollbackTransaction(); + die("error saving invoice_id to bill"); + } + } + } + + // generate Invoice number + $new_num = PreorderBillingInvoice::getNextInvoiceNumber($netowner_id); + $invoice->total = $total_net; + $invoice->total_gross = $total_gross; + $invoice->invoice_number = $new_num; + $invoice->invoice_date = $today->format("Y-m-d"); + + // voicenumbers + //var_dump($invoice_voicenumbers);exit; + + if (!$invoice->save()) { + $invoice->rollbackTransaction(); + die("Error saving Invoice number and date"); + } + + + } catch(Exception $e) { + if($invoice) { + $invoice->rollbackTransaction(); + } + + die("Error saving Invoice!\n"); + } + + // commit transaction + $invoice->commitTransaction(); + $invoice_count++; + } + + $this->log->debug(__METHOD__.": $billing_row_count billing rows | $positions_count positions count | $bill_id_count bill_id count"); + + $this->layout()->setFlash("$invoice_count Rechnungen erstellt", "success"); + $this->redirect("PreorderBillingInvoice"); + } +} \ No newline at end of file diff --git a/application/PreorderBillingInvoiceFile/PreorderBillingInvoiceFile.php b/application/PreorderBillingInvoiceFile/PreorderBillingInvoiceFile.php new file mode 100644 index 000000000..b85f6f9ea --- /dev/null +++ b/application/PreorderBillingInvoiceFile/PreorderBillingInvoiceFile.php @@ -0,0 +1,233 @@ +$name == null) { + + if(!$this->id) { + return null; + } + + if($name == "creator") { + $this->creator = new User($this->create_by); + return $this->creator; + } + + if($name == "editor") { + $this->editor = new User($this->edit_by); + return $this->editor; + } + + $classname = ucfirst($name); + $idfield = $name."_id"; + $this->$name = new $classname($this->$idfield); + + if($this->$name->id) { + return $this->$name; + } else { + return null; + } + } + + return $this->$name; + } + + /******************************** + * Begin static Model functions + */ + + public static function create(Array $data) { + $model = new PreorderBillingInvoiceFile(); + + $table_fields = [ + "invoice_id", "file_id", "name", "description", + "create_by","edit_by","create","edit" + ]; + + foreach($data as $field => $value) { + if(in_array($field, $table_fields)) { + $model->$field = $value; + } + } + + $me = new User(); + $me->loadMe(); + + if($model->create_by === null) { + $model->create_by = $me->id; + } + if($model->edit_by === null) { + $model->edit_by = $me->id; + } + + return $model; + } + + public static function createFromInvoice(PreorderBillingInvoice $invoice) { + $log = mfLoghandler::singleton(); + $invoice_path_base = MFUPLOAD_FILE_SAVE_PATH."/".TT_PREORDER_BILLING[$invoice->netowner_id]["subfolder"]; + + $invoice_date = new DateTime($invoice->invoice_date); + $year = $invoice_date->format("Y"); + $invoice_subfolder = TT_PREORDER_BILLING[$invoice->netowner_id]["subfolder"]."/$year"; + $invoice_path = "$invoice_path_base/$year"; + + + if(!file_exists($invoice_path)) { + mkdir($invoice_path, 0777, true); + } + + // create PDF + $tmp_filename = $invoice->createPdf(); + if(!$tmp_filename) { + $log->debug("Error creating PDF file"); + return false; + } + + $new_filename = $invoice->invoice_number.".pdf"; + + // move pdf to correct folder + $log->debug(__METHOD__.": invoice path: $invoice_path"); + $log->debug(__METHOD__.": new filename: $invoice_path/$new_filename"); + if(!rename($tmp_filename, "$invoice_path/$new_filename")) { + $log->debug(__METHOD__.": Error moving created PDF file"); + return false; + } + + try { + // create File + $file = FileModel::create([ + "name" => $invoice->invoice_number, + "filename" => $new_filename, + "subfolder" => $invoice_subfolder, + "store_filename" => $new_filename, + "orig_filename" => $new_filename, + ]); + + if (!$file->save()) { + $log->error(__METHOD__ . ": Error saving PDF file"); + return false; + } + } catch(Exception $e) { + $log->error(__METHOD__ . ": Exception while creating File object"); + throw($e); + } + + // create PreorderBillingInvoiceFile + $ifile = PreorderBillingInvoiceFile::create([ + "invoice_id" => $invoice->id, + "file_id" => $file->id, + "name" => $new_filename, + "description" => "" + ]); + + if(!$ifile->save()) { + $log->error(__METHOD__.": Error saving PDF Invoice file"); + return false; + } + + return $ifile; + } + + public static function getAll() { + $items = []; + + $db = FronkDB::singleton(); + + $res = $db->select("PreorderBillingInvoiceFile", "*", "1=1 ORDER BY name"); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[] = new PreorderBillingInvoiceFile($data); + } + } + return $items; + + } + + public static function getFirst($filter = []) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $res = $db->select("PreorderBillingInvoiceFile", "*", "$where ORDER BY name"); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + $item = new PreorderBillingInvoiceFile($data); + if($item->id) { + return $item; + } else { + return null; + } + } + return null; + } + + public static function search($filter) { + $items = []; + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + + $sql = "SELECT PreorderBillingInvoiceFile.* FROM PreorderBillingInvoiceFile + LEFT JOIN File ON (PreorderBillingInvoiceFile.file_id = File.id) + WHERE $where + ORDER BY invoice_id, name"; + + $res = $db->query($sql); + + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[] = new PreorderBillingInvoiceFile($data); + } + } + return $items; + } + + private static function getSqlFilter($filter) { + $where = "1=1 "; + + + if(array_key_exists("file_id", $filter)) { + $file_id = $filter['file_id']; + if(is_numeric($file_id)) { + $where .= " AND file_id=$file_id"; + } + } + + if(array_key_exists("invoice_id", $filter)) { + $invoice_id = $filter['invoice_id']; + if(is_numeric($invoice_id)) { + $where .= " AND invoice_id=$invoice_id"; + } + } + + //var_dump($filter);exit; + if(array_key_exists("name", $filter)) { + $name = FronkDB::singleton()->escape($filter['name']); + if($name) { + $where .= " AND name='$name'"; + } + } + + if(array_key_exists("filename", $filter)) { + $filename = FronkDB::singleton()->escape($filter['filename']); + if($filename) { + $where .= " AND File.filename='$filename'"; + } + } + + if(array_key_exists("subfolder", $filter)) { + $subfolder = FronkDB::singleton()->escape($filter['subfolder']); + if($subfolder) { + $where .= " AND File.subfolder='$subfolder'"; + } + } + + //var_dump($filter, $where);exit; + return $where; + } +} + diff --git a/application/PreorderBillingInvoiceposition/PreorderBillingInvoiceposition.php b/application/PreorderBillingInvoiceposition/PreorderBillingInvoiceposition.php new file mode 100644 index 000000000..a07755670 --- /dev/null +++ b/application/PreorderBillingInvoiceposition/PreorderBillingInvoiceposition.php @@ -0,0 +1,221 @@ +$name == null) { + + if($name == "invoice") { + $invoice = PreorderBillingInvoice::getFirst(["invoice_id" => $this->invoice_id]); + if($invoice) { + $this->invoice = $invoice; + } + return $this->invoice; + } + + if($name == "creator") { + $creator = mfValuecache::singleton()->get("Worker-id-".$this->create_by); + if($creator) { + $this->creator = $creator; + return $this->creator; + } + $this->creator = new User($this->create_by); + + if(!$this->creator->id) { + return null; + } + mfValuecache::singleton()->set("Worker-id-".$this->create_by, $this->creator); + return $this->creator; + } + + if($name == "editor") { + $editor = mfValuecache::singleton()->get("Worker-id-".$this->edit_by); + if($editor) { + $this->editor = $editor; + return $this->editor; + } + $this->editor = new User($this->edit_by); + if(!$this->editor->id) { + return null; + } + 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; + + } + + /******************************** + * Begin static Model functions + */ + + public static function create(Array $data) { + $model = new PreorderBillingInvoiceposition(); + + $table_fields = [ + "invoice_id", "billing_id", "start_date", "end_date", "product_id", "article_number", "article_name", "article_info", + "amount", "unit", "price", "price_total", "price_gross", "vatrate", "billing_period", + "create_by","edit_by","create","edit" + ]; + + foreach($data as $field => $value) { + if(in_array($field, $table_fields)) { + $model->$field = $value; + } + } + + $me = new User(); + $me->loadMe(); + + if($model->create_by === null) { + $model->create_by = $me->id; + } + if($model->edit_by === null) { + $model->edit_by = $me->id; + } + + return $model; + } + + public static function getAll() { + $items = []; + + $db = FronkDB::singleton(); + + $res = $db->select("PreorderBillingInvoiceposition", "*", "1 = 1 ORDER BY id"); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[] = new PreorderBillingInvoiceposition($data); + } + } + return $items; + + } + + public static function getFirst($filter) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT PreorderBillingInvoiceposition.* FROM PreorderBillingInvoiceposition + WHERE $where + ORDER BY id LIMIT 1"; + + //mfLoghandler::singleton()->debug($sql); + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + $item = new PreorderBillingInvoiceposition($data); + if($item->id) { + return $item; + } else { + return null; + } + } + return null; + } + + public static function count($filter) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT COUNT(*) as cnt FROM PreorderBillingInvoiceposition + WHERE $where"; + + //mfLoghandler::singleton()->debug($sql); + + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + return $data->cnt; + } + return 0; + } + + public static function search($filter, $limit = false, $order = false) { + //var_dump($filter);exit; + $items = []; + + if(!$order) { + $order = "id ASC"; + } + + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT PreorderBillingInvoiceposition.* FROM PreorderBillingInvoiceposition + WHERE $where + ORDER BY $order"; + + if(is_array($limit) && count($limit)) { + if(is_numeric($limit['start']) && is_numeric($limit['count'])) { + $sql .= " LIMIT ".$limit['start'].", ".$limit['count']; + } elseif(is_numeric($limit['count'])) { + $sql .= " LIMIT ".$limit['count']; + } + } + + mfLoghandler::singleton()->debug($sql); + + $res = $db->query($sql); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[$data->id] = new PreorderBillingInvoiceposition($data); + } + } + + return $items; + } + + private static function getSqlFilter($filter) { + $where = "1=1 "; + + if(array_key_exists("invoice_id", $filter)) { + $invoice_id = $filter['invoice_id']; + if(is_numeric($invoice_id)) { + $where .= " AND PreorderBillingInvoiceposition.invoice_id=$invoice_id"; + } + } + + if(array_key_exists("billing_id", $filter)) { + $billing_id = $filter['billing_id']; + if(is_numeric($billing_id)) { + $where .= " AND PreorderBillingInvoiceposition.billing_id=$billing_id"; + } + } + + if(array_key_exists("product_id", $filter)) { + $product_id = $filter['product_id']; + if(is_numeric($product_id)) { + $where .= " AND PreorderBillingInvoiceposition.product_id=$product_id"; + } + } + + + if(array_key_exists("add-where", $filter)) { + $where .= " ".$filter['add-where']; + } + + //var_dump($filter, $where);exit; + return $where; + } + +} \ No newline at end of file diff --git a/application/PreorderProduct/PreorderProduct.php b/application/PreorderProduct/PreorderProduct.php index a235b7ddf..c9330be1e 100644 --- a/application/PreorderProduct/PreorderProduct.php +++ b/application/PreorderProduct/PreorderProduct.php @@ -555,7 +555,7 @@ class PreorderProduct extends mfBaseModel { return $types; } - public static function getAll() { + public static function getAll($withKeyIsId = false) { $items = []; $db = FronkDB::singleton(); @@ -563,7 +563,11 @@ class PreorderProduct extends mfBaseModel { $res = $db->select("PreorderProduct", "*", "1 = 1 ORDER BY type"); if($db->num_rows($res)) { while($data = $db->fetch_object($res)) { - $items[] = new PreorderProduct($data); + if($withKeyIsId) { + $items[$data->id] = new PreorderProduct($data); + } else { + $items[] = new PreorderProduct($data); + } } } return $items; diff --git a/db/migrations/20250321115602_preorder_billing_change_prices_to_two_decimals.php b/db/migrations/20250321115602_preorder_billing_change_prices_to_two_decimals.php new file mode 100644 index 000000000..03a4e1e15 --- /dev/null +++ b/db/migrations/20250321115602_preorder_billing_change_prices_to_two_decimals.php @@ -0,0 +1,54 @@ +getEnvironment() == "thetool") { + $table = $this->table('PreorderBilling'); + $table->changeColumn("price", "decimal", ["null" => false, "precision" => 14, "scale" => 2]); + $table->changeColumn("price_setup", "decimal", ["null" => false, "precision" => 14, "scale" => 2]); + $table->update(); + + $pp = $this->table("PreorderProduct"); + $pp->changeColumn("price_inet", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $pp->changeColumn("price_inet_tv", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $pp->changeColumn("price_catv", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $pp->changeColumn("price_passive", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $pp->update(); + + $ppp = $this->table("PreorderProductPrice"); + $ppp->changeColumn("price_inet", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $ppp->changeColumn("price_inet_tv", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $ppp->changeColumn("price_catv", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $ppp->changeColumn("price_passive", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $ppp->update(); + + $ppmd = $this->table("PreorderProductMarketshareDiscount"); + $ppmd->changeColumn("price_inet", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $ppmd->changeColumn("price_inet_tv", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $ppmd->changeColumn("price_catv", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $ppmd->changeColumn("price_passive", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 2]); + $ppmd->update(); + + } + + if($this->getEnvironment() == "addressdb") { + + } + } + + public function down(): void + { + if($this->getEnvironment() == "thetool") { + + } + + if($this->getEnvironment() == "addressdb") { + + } + } +} diff --git a/db/migrations/20250321123230_create_preorder_billing_invoice.php b/db/migrations/20250321123230_create_preorder_billing_invoice.php new file mode 100644 index 000000000..fbf3bb3fb --- /dev/null +++ b/db/migrations/20250321123230_create_preorder_billing_invoice.php @@ -0,0 +1,101 @@ +getEnvironment() == "thetool") { + $billing = $this->table("PreorderBilling"); + $billing->addColumn("netowner_id", "integer", ["null" => false, "after" => "id"]); + $billing->update(); + + $invoice = $this->table("PreorderBillingInvoice"); + $invoice->addColumn("netowner_id", "integer", ["null" => false]); + $invoice->addColumn("invoice_number", "string", ["null" => true, "default" => null]); + $invoice->addColumn("invoice_date", "date", ["null" => true, "default" => null]); + $invoice->addColumn("preorderbillingcustomer_id", "integer", ["default" => null, "null" => true]); + $invoice->addColumn("owner_id", "integer", ["null" => true, "default" => null]); + $invoice->addColumn("billingaddress_id", "integer", ["null" => true, "default" => null]); + $invoice->addColumn("fibu_account_number", "integer", ["null" => true, "default" => null]); + $invoice->addColumn("fibu_cost_area", "string", ["null" => true, "default" => null]); + $invoice->addColumn("fibu_cost_account", "string", ["null" => true, "default" => null]); + $invoice->addColumn("fibu_revenue_account", "string", ["null" => true, "default" => null]); + $invoice->addColumn("fibu_taxcode", "integer", ["null" => true, "default" => null]); + $invoice->addColumn("tax_text", "string", ["null" => true, "default" => null, "length" => 255]); + $invoice->addColumn("company", "string", ["null" => true, "default" => null, "length" => 1024]); + $invoice->addColumn("firstname", "string", ["null" => true, "default" => null, "length" => 1024]); + $invoice->addColumn("lastname", "string", ["null" => true, "default" => null, "length" => 1024]); + $invoice->addColumn("street", "string", ["null" => false, "length" => 1024]); + $invoice->addColumn("zip", "string", ["null" => false, "length" => 1024]); + $invoice->addColumn("city", "string", ["null" => false, "length" => 1024]); + $invoice->addColumn("country", "string", ["null" => true, "default" => null, "length" => 1024]); + $invoice->addColumn("email", "string", ["null" => true, "default" => null, "length" => 1024]); + $invoice->addColumn("uid", "string", ["null" => true, "default" => null, "length" => 1024]); + $invoice->addColumn("billing_type", "enum", ["null" => false, "values" => "invoice,sepa"]); + $invoice->addColumn("billing_delivery", "enum", ["null" => false, "values" => "email,paper"]); + $invoice->addColumn("bank_account_bank", "string", ["null" => true, "default" => null, "length" => 255]); + $invoice->addColumn("bank_account_owner", "string", ["null" => true, "default" => null, "length" => 255]); + $invoice->addColumn("bank_account_iban", "string", ["null" => true, "default" => null, "length" => 255]); + $invoice->addColumn("bank_account_bic", "string", ["null" => true, "default" => null, "length" => 255]); + $invoice->addColumn("total", "decimal", ["null" => false, "precision" => 14, "scale" => 2]); + $invoice->addColumn("total_gross", "decimal", ["null" => false, "precision" => 14, "scale" => 2]); + $invoice->addColumn("vatgroup_id", "integer", ["null" => true, "default" => null]); + $invoice->addColumn("vatrate", "decimal", ["null" => false, "default" => 0, "precision" => 6, "scale" => 2]); + $invoice->addColumn("bmd_export_date", "integer", ["null" => true, "default" => null]); + $invoice->addColumn("date_delivered", "integer", ["null" => true, "default" => null]); + $invoice->addColumn("create_by", "integer", ["null" => false]); + $invoice->addColumn("edit_by", "integer", ["null" => false]); + $invoice->addColumn("create", "integer", ["null" => false]); + $invoice->addColumn("edit", "integer", ["null" => false]); + $invoice->create(); + + $ip = $this->table("PreorderBillingInvoiceposition"); + $ip->addColumn("invoice_id", "integer", ["null" => true, "default" => null]); + $ip->addColumn("billing_id", "integer", ["null" => true, "default" => null]); + $ip->addColumn("start_date", "date", ["null" => true, "default" => null]); + $ip->addColumn("end_date", "date", ["null" => true, "default" => null]); + $ip->addColumn("product_id", "integer", ["null" => false]); + $ip->addColumn("article_number", "integer", ["null" => false]); + $ip->addColumn("article_name", "string", ["null" => false, "length" => 255]); + $ip->addColumn("article_info", "text", ["null" => true, "default" => null]); + $ip->addColumn("amount", "decimal", ["null" => false, "precision" => 9, "scale" => 6]); + $ip->addColumn("unit", "string", ["null" => false, "default" => ""]); + $ip->addColumn("price", "decimal", ["null" => false, "precision" => 14, "scale" => 2]); + $ip->addColumn("price_total", "decimal", ["null" => false, "precision" => 14, "scale" => 2]); + $ip->addColumn("price_gross", "decimal", ["null" => false, "precision" => 14, "scale" => 2]); + $ip->addColumn("vatrate", "decimal", ["null" => false, "default" => 0, "precision" => 6, "scale" => 2]); + $ip->addColumn("billing_period", "integer", ["null" => false, "default" => 0]); + $ip->addColumn("create_by", "integer", ["null" => false]); + $ip->addColumn("edit_by", "integer", ["null" => false]); + $ip->addColumn("create", "integer", ["null" => false]); + $ip->addColumn("edit", "integer", ["null" => false]); + $ip->create(); + + + + } + + if($this->getEnvironment() == "addressdb") { + + } + } + + public function down(): void + { + if($this->getEnvironment() == "thetool") { + $this->table("PreorderBillingInvoiceposition")->drop()->save(); + $this->table("PreorderBillingInvoice")->drop()->save(); + + $billing = $this->table("PreorderBilling"); + $billing->removeColumn("netowner_id"); + $billing->update(); + } + + if($this->getEnvironment() == "addressdb") { + + } + } +} diff --git a/db/migrations/20250322112328_create_preorder_billing_invoice_file.php b/db/migrations/20250322112328_create_preorder_billing_invoice_file.php new file mode 100644 index 000000000..3a034fc4b --- /dev/null +++ b/db/migrations/20250322112328_create_preorder_billing_invoice_file.php @@ -0,0 +1,40 @@ +getEnvironment() == "thetool") { + $table = $this->table("PreorderBillingInvoiceFile"); + $table->addColumn("invoice_id", "integer", ["null" => false]); + $table->addColumn("file_id", "integer", ["null" => false]); + $table->addColumn("name", "string", ["null" => false, "limit" => 1024]); + $table->addColumn("description", "string", ["null" => true, "default" => null, "limit" => 1024]); + + $table->addColumn("create_by", "integer", ["null" => false]); + $table->addColumn("edit_by", "integer", ["null" => false]); + $table->addColumn("create", "integer", ["null" => false]); + $table->addColumn("edit", "integer", ["null" => false]); + + $table->create(); + } + + if($this->getEnvironment() == "addressdb") { + + } + } + + public function down(): void + { + if($this->getEnvironment() == "thetool") { + $this->table("PreorderBillingInvoiceFile")->drop()->save(); + } + + if($this->getEnvironment() == "addressdb") { + + } + } +}