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";
+
+
=($is_credit) ? "Gutschrift" : "Rechnung"?> =$invoice->invoice_number?>
+
+
+
+ | Pos |
+ Artikel |
+ Leistung / Produkt |
+ Preis |
+ Menge |
+ Einheit |
+ Preis € |
+ Gesamt € |
+
+ 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, ",",".");
+
+ ?>
+
+ ">
+ | =$i+1?> |
+ =$p->article_number?> |
+ =$p->article_name?> |
+ =$price?> € |
+ =$amount?> |
+ =$p->unit?> |
+ =$price?> € |
+ =$price_total?> € |
+
+
+
+
+ | Gesamt Netto: |
+ =number_format($net_total, 2, ",","."). " €"?> |
+
+
+ $vat_total): ?>
+
+ 0): ?>
+
+ | + USt. =number_format($rate, 0, ",", ".")?>%: |
+ =number_format($vat_total, 2, ",","."). " €"?> |
+
+
+
+
+
+
+
+ | Gesamt Brutto: |
+ =number_format($gross_total, 2, ",","."). " €"?> |
+
+
+
+
+
+ tax_text): ?>
+
=$invoice->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") {
+
+ }
+ }
+}