diff --git a/Layout/default/Billing/Index.php b/Layout/default/Billing/Index.php index c786a0c3c..43676214c 100644 --- a/Layout/default/Billing/Index.php +++ b/Layout/default/Billing/Index.php @@ -33,8 +33,20 @@ $pagination_entity_name = "Billingrecords";
- - "/> + + "/> +
+
+ + "/> +
+
+ + "/> +
+
+ + "/>
@@ -74,7 +86,7 @@ $pagination_entity_name = "Billingrecords";
"> - Verrechenbare Contracts importieren + Aktuell verrechenbare Contracts neu importieren
@@ -92,23 +104,24 @@ $pagination_entity_name = "Billingrecords"; Vertragsinhaber Rechnungsadresse Kundennummer - Einzug - Zustellung - Bankdaten Produkt Anzahl Preis Preis Setup + Zahlung + Zustellung + Bankdaten + $billing->contract_id])?>" target="_blank">contract_id?> - start_date?> - end_date?> + start_date?> -
end_date?> contract->owner->getCompanyOrName()?>
contract->owner->street?>
- contract->owner->zip?> contract->owner->city?> + contract->owner->zip?> contract->owner->city?>
contract->owner->country->name?> @@ -119,6 +132,17 @@ $pagination_entity_name = "Billingrecords"; country?> customer_number?> + + product_name?>matchcode) ? " (".$billing->matchcode.")" : ""?> + product_info): ?> +
+ product_info?> +
+ + + amount / (int)$billing->amount > 1) ? number_format($billing->amount,3,",",".") : (int)$billing->amount?> + ">€ price,4,",",".")?> + ">€ price_setup,4,",",".")?> billing_type == "sepa") ? "SEPA" : ""?> billing_delivery == "email") ? "Email" : "Papier"?> @@ -129,17 +153,7 @@ $pagination_entity_name = "Billingrecords"; BIC: bank_account_bic?>
- - product_name?>matchcode) ? " (".$billing->matchcode.")" : ""?> - product_info): ?> -
- product_info?> -
- - - amount,3,",",".")?> - € price,4,",",".")?> - € price_setup,4,",",".")?> + diff --git a/Layout/default/Contract/Form.php b/Layout/default/Contract/Form.php index 8ce9dc516..d3addd0bd 100644 --- a/Layout/default/Contract/Form.php +++ b/Layout/default/Contract/Form.php @@ -148,8 +148,9 @@
diff --git a/Layout/default/Contract/Index.php b/Layout/default/Contract/Index.php index 8926ffc5f..ee84e0460 100644 --- a/Layout/default/Contract/Index.php +++ b/Layout/default/Contract/Index.php @@ -131,15 +131,7 @@ isFinished()) ? "not-finished" : "" ?> price < 0) ? "text-danger" : ""?>">€ price,4,",",".")?> isFinished()) ? "not-finished" : "" ?> price_setup < 0) ? "text-danger" : ""?>">€ price_setup,4,",",".")?> isFinished()) ? "not-finished" : "" ?>"> - billing_period == 1): ?> - monatlich - billing_period == 24): ?> - biennal - billing_period == 36): ?> - triennal - billing_period): ?> - billing_period)?>x Jährlich - + billing_period, "billing_period")?> ">finish_date) ? date('d.m.Y', $contract->finish_date) : ""?> cancel_date) ? date('d.m.Y', $contract->cancel_date) : ""?> diff --git a/Layout/default/Contract/View.php b/Layout/default/Contract/View.php index 53af585f4..4e83926d2 100644 --- a/Layout/default/Contract/View.php +++ b/Layout/default/Contract/View.php @@ -136,12 +136,12 @@ Vertragsinhaber: - $contract->owner_id])?>">owner->getCompanyOrName()?> [owner->customer_number?>] + $contract->owner_id])?>">owner->getCompanyOrName()?> [owner->customer_number?>] billingaddress_id): ?> Rechnungsempfänger: - $contract->billingaddress_id])?>">billingaddress->getCompanyOrName()?> [billingaddress->customer_number?>] + $contract->billingaddress_id])?>">billingaddress->getCompanyOrName()?> [billingaddress->customer_number?>] @@ -160,22 +160,42 @@ Externes Produkt: product_external) ? "Ja" : "Nein"?> - Setup Preis: - ">€ price_setup?> - - Preis Periodisch: - ">€ price?> - - Verrechnungsperiode: - - billing_period == 1) ? "Monatlich" : ""?> - billing_period == 12) ? "Jährlich" : ""?> - billing_period == 24) ? "Zweijährlich" : ""?> - billing_period == 36) ? "Dreijährlich" : ""?> - + Menge: + amount, 3, ",", ".")?> + + + Preis Periodisch Netto: + ">€ amount != 1) ? $contract->price * $contract->amount : $contract->price, 4, ",", ".")?> + + Preis Periodisch Brutto: + ">€ + price && $contract->vatrate): ?> + amount != 1): ?> + price + ($contract->price / 100) * $contract->vatrate, 4, ",", ".")?> + + price + ($contract->price / 100) * $contract->vatrate) * $contract->amount, 4, ",", ".")?> + + + + + Verrechnungsperiode: + + billing_period, "billing_period")?> + + + Herstellungskosten: + "> + price_setup > 0): ?> + Netto: € price_setup, 4, ",", ".")?>amount != 1) ? " (Gesamt: € ".number_format($contract->price_setup * $contract->amount, 4, ",", ".").")" : ""?>
+ Brutto: € price_setup && $contract->vatrate) ? number_format($contract->price_setup + ($contract->price_setup / 100) * $contract->vatrate, 4, ",", ".") : ""?>price_setup && $contract->vatrate && $contract->amount != 1) ? " (Gesamt: € ".number_format(($contract->price_setup + ($contract->price_setup / 100) * $contract->vatrate) * $contract->amount, 4, ",", ".").")" : ""?> + Verrechnungsstart Verzögerung: - billing_delay?> Monate + + billing_delay): ?> + billing_delay?> Monate + + @@ -205,14 +225,14 @@ edit)?> (editor->name?>) - $contract->id])?>"> - $contract->id])?>"> + $contract->id])?>"> + $contract->id])?>"> finish_date && $contract->finish_date < date('U')): ?> - - $contract->id])?>"> - $contract->id])?>"> + + $contract->id])?>"> + $contract->id])?>"> - $contract->id])?>" onclick="if(!confirm('Jetzt fertigstellen und in Verrechnung geben?')) return false"> + $contract->id])?>" onclick="if(!confirm('Jetzt fertigstellen und in Verrechnung geben?')) return false"> @@ -276,6 +296,11 @@ Vertrag aus Bestellung $contract->orderproduct->order_id])?>">#orderproduct->order_id?> erstellt + type == "contract_finished"): ?> + + Vertag fertiggestellt + + type == "credit_created"): ?> Gutschrift-Vertrag $j->value])?>">value?> erstellt @@ -342,6 +367,8 @@ Kunde Contract ID Produkt + Preis + Preis Setup Bestelldatum Fertigstellung Kündigung @@ -377,12 +404,14 @@ ">type, "contract")?> type != "link") ? $direction : ""?> $linkcontract->owner_id])?>">owner->getCompanyOrName()?> - "> $linkcontract->id])?>">id?> + isFinished()) ? "not-finished" : "" ?>"> $linkcontract->id])?>">id?> - "> $linkcontract->id])?>">product_name?> [matchcode?>] - ">order_date) ? date('d.m.Y', $linkcontract->order_date) : ""?> - ">finish_date) ? date('d.m.Y', $linkcontract->finish_date) : ""?> - ">cancel_date) ? date('d.m.Y', $linkcontract->cancel_date) : ""?> + isFinished()) ? "not-finished" : "" ?>"> $linkcontract->id])?>">product_name?> [matchcode?>] + isFinished()) ? "not-finished" : "" ?> price < 0) ? "text-danger" : ""?>">€ price,4,",",".")?> + isFinished()) ? "not-finished" : "" ?> price_setup < 0) ? "text-danger" : ""?>">€ price_setup,4,",",".")?> + isFinished()) ? "not-finished" : "" ?>">order_date) ? date('d.m.Y', $linkcontract->order_date) : ""?> + isFinished()) ? "not-finished" : "" ?>">finish_date) ? date('d.m.Y', $linkcontract->finish_date) : ""?> + isFinished()) ? "not-finished" : "" ?>">cancel_date) ? date('d.m.Y', $linkcontract->cancel_date) : ""?> $link->id])?>" onclick="if(!confirm('Verknüpfung wirklich entfernen?')) return false;" class="text-danger" title="Verknüpfung entfernen"> diff --git a/Layout/default/Emailtemplates/Invoice/invoice.php b/Layout/default/Emailtemplates/Invoice/invoice.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/Layout/default/Emailtemplates/Invoice/invoice.php @@ -0,0 +1 @@ +getUrl($Mod, "Index"); +$pagination_baseurl_params = ["filter" => $filter]; +$pagination_entity_name = "Billingrecords"; +?> + + + +
+
+
+
+ +
+

Rechnungen

+
+
+
+ + +
+
+ +
+
+

Filter

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

Rechnungen

+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + total + $invoice->total_setup; ?> + total_gross + $invoice->total_setup_gross; ?> + + + + + + + + + + + + + +
RechungsnummerRechnungsdatumKundennummerRechnungsadresseNettoUst.BruttoZahlungZustellung
$invoice->id])?>"> invoice_number?>invoice_date)?>customer_number?> + company) ? $invoice->company."
" : ""?> + firstname || $invoice->lastname) ? $invoice->firstname." ".$invoice->lastname."
" : ""?> + street?>
+ zip?> city?>
+ country?> +
">€ ">€ total_vat,2,",",".")?>">€ billing_type == "sepa") ? "SEPA" : "Überweisung"?>billing_delivery == "email") ? "Email" : "Papier"?> $invoice->id])?>">
+ + + + + +
+
+ + +
+ +
+ +
+ +
+ + + \ No newline at end of file diff --git a/Layout/default/Invoice/Print.pdf.php b/Layout/default/Invoice/Print.pdf.php new file mode 100644 index 000000000..7eecd7ddc --- /dev/null +++ b/Layout/default/Invoice/Print.pdf.php @@ -0,0 +1,158 @@ +total + $invoice->total_setup; +$gross_total = $invoice->total_gross + $invoice->total_setup_gross; +$is_credit = $net_total < 0; + + +?> + + + Xinon Billing - Rechnung <?=$invoice->invoice_number?> + + + + + + + + + + +
+ +
+
company?>
+ firstname && $invoice->lastname): ?>firstname?> lastname?>
+ street)?>
+ zip?> city?>
+ country != "Österreich"):?>country?>
+
+ +
+

GutschriftRechnung invoice_number?>

+
BelegdatumRechnungsdatum: invoice_date)?> + uid): ?>
Ihre UID: uid?> +
+ +
+ +
+
+
+
Xinon GmbH
+
Fladnitz im Raabtal 150
+ A-8322 Studenzen
+  
+ Tel +43 3115 40800
+ office@xinon.at - https://xinon.at/
+ UID: ATU68711968
+ FB: FN 416556 h
+  
+ Bankverbindung:
+ Steiermärkische Bank und Sparkassen AG
+ BIC: STSPAT2GXXX
+ IBAN: AT84 2081 5000 4007 9121 +
+
+
+ +
+ + +
+ +
+ + positions) && count($invoice->positions)): ?> + + + + + + + + + + + + positions as $p): ?> + start_date); + $end_date = new DateTime($p->end_date); + $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, 2, ",","."); + ?> + one-position"> + + + + + + + + + + +
BezeichnungEinzelpreisMengeGesamtpreisUst.Gesamt Brutto
+ product_name?> + format("d.m.Y") != $end_date->format("d.m.Y")): ?> +  (format("d.m.Y")?> - format("d.m.Y")?>) + + matchcode) ? "
".$p->matchcode."
" : ""?> + product_info) ? "
".nl2br($p->product_info)."
" : ""?> +
%
+ + +
+ +
+ + + + + + + + + + $vat_total): ?> + 0): ?> + + + + + + + + + + + + + + + + + +
Gesamtbetrag Netto:
+ Umsatzsteuer %:
Gesamtbetrag:Gesamtbetrag Brutto:
+ + +

Hinweis: Die Umsatzsteuerschuld geht auf den Leistungsempfänger über (Reverse Charge)

+ + +

Gutschrift! Bitte nicht überweisen.

+ + Ohne Abzug zu bezahlen bis invoice_date))->modify("+14 days")->format("d.m.Y")?>.
+ Bitte geben Sie als Verwendungszweck unbedingt die Rechnungsnummer an, nur so können wir Ihre Zahlung eindeutig zuordnen + + + + diff --git a/Layout/default/User/Form.php b/Layout/default/User/Form.php index 46ca2a9c5..3bc72f040 100644 --- a/Layout/default/User/Form.php +++ b/Layout/default/User/Form.php @@ -218,6 +218,12 @@ +
+
+ can("Billing") ? "checked='checked'" : ""?> /> + +
+

Zusatzberechtigungen

diff --git a/Layout/default/menu.php b/Layout/default/menu.php index 19efe9e0a..5fa016f8e 100644 --- a/Layout/default/menu.php +++ b/Layout/default/menu.php @@ -67,7 +67,7 @@
  • "> Geräte Hersteller
  • "> Geräte Typen
  • "> Devices
  • -
  • "> Benutzer
  • +
  • "> Benutzer
  • Grundstammdaten
  • is(["Admin"]) || ($me->is("netowner") && $me->hasGwrNetworks())): ?> @@ -86,6 +86,18 @@ + is(["Admin"])): ?> +
  • + + Verrechnung
    +
    + +
  • + is(["Admin","netowner","lineplanner","pipeplanner","pipeworker","lineworker"])): ?>
  • diff --git a/application/Admin/functions/IvtContractImport.php b/application/Admin/functions/IvtContractImport.php index c9351c0bf..0b7750b21 100644 --- a/application/Admin/functions/IvtContractImport.php +++ b/application/Admin/functions/IvtContractImport.php @@ -199,11 +199,11 @@ class Admin_IvtContractImport { switch($ip->interval) { case 0: $contract_data['billing_period'] = 1; - $finish_date->modify("+1 month"); + //$finish_date->modify("+1 month"); break; case 1: $contract_data['billing_period'] = 12; - $finish_date->modify("+1 year"); + //$finish_date->modify("+1 year"); break; } @@ -276,7 +276,7 @@ class Admin_IvtContractImport { $contract = ContractModel::create($contract_data); if(trim($ivt_contract->comment)) { $contract->matchcode = trim($ivt_contract->comment); - } else { + } elseif(!in_array($ivt_contract->id, $this->no_matchcode)) { $contract->matchcode = $contract->generateMatchcode(); } /*if($ivt_contract->id == 3035) { @@ -336,8 +336,6 @@ class Admin_IvtContractImport { foreach($new_contracts as $ivt_customer_id => $contracts) { $this->addVoipData($ivt_customer_id, $contracts); - if(in_array($ivt_contract->id, $this->no_matchcode)) break; - if(count($contracts) < 2) continue; $prev_contracts = []; $primary_matchcode = false; @@ -347,10 +345,12 @@ class Admin_IvtContractImport { foreach($contracts as $contract) { if($contract->matchcode && !$primary_matchcode && !preg_match($primary_matchcode_no_productgroups_pattern, $contract->product->productgroup->name)) { + if(in_array($ivt_contract->id, $this->no_matchcode)) continue; $primary_matchcode = $contract->matchcode; $primary_matchcode_source_product_group = $contract->product->productgroup->name; } if($primary_matchcode && !$contract->matchcode && !preg_match($primary_matchcode_no_productgroups_pattern, $primary_matchcode_source_product_group)) { + if(in_array($ivt_contract->id, $this->no_matchcode)) continue; $contract->matchcode = $primary_matchcode; $contract->save(); } diff --git a/application/Billing/BillingController.php b/application/Billing/BillingController.php index 160e0d254..15db0aec9 100644 --- a/application/Billing/BillingController.php +++ b/application/Billing/BillingController.php @@ -66,6 +66,22 @@ class BillingController extends mfBaseController { $new_filter["price>="] = 0; } + if(array_key_exists("customer", $filter)) { + if(array_key_exists("customer", $filter) && $filter["customer"]) { + $kunde = $this->db()->escape($filter['customer']); + if(!array_key_exists("add-where", $new_filter)) $new_filter["add-where"] = ""; + $new_filter['add-where'] .= " AND (company like '%$kunde%' OR firstname like '%$kunde%' OR lastname like '%$kunde%' OR concat(firstname, ' ', lastname) like '%$kunde%' OR concat(lastname, ' ', firstname) like '%$kunde%')"; + } + } + + if(array_key_exists("address", $filter)) { + if(array_key_exists("address", $filter) && $filter["address"]) { + $search = $this->db()->escape($filter['address']); + if(!array_key_exists("add-where", $new_filter)) $new_filter["add-where"] = ""; + $new_filter['add-where'] .= " AND (street like '%$search%' OR zip like '%$search%' OR city like '%$search%' OR country like '%$search%')"; + } + } + if (is_array($filter) && count($filter)) { foreach ($filter as $name => $value) { $new_filter[$name] = $value; @@ -86,19 +102,33 @@ class BillingController extends mfBaseController { $i = 0; + //$yearly_not_before = new DateTime("2023-06-01"); + $now_year = date("Y"); $now_month = date("m"); $now_day = date("d"); $now_year = 2024; $now_month = 7; + // XXX only for 1st Billing after IVT Import + //$yearly_not_before = new DateTime("$now_year-$now_month-01"); + $yearly_not_before = new DateTime("$now_year-06-01"); + + $del = 0; + // first delete all non-invoiced billing records + /*foreach(BillingModel::search(["invoice_id" => null]) as $bill) { + $bill->delete(); + $del++; + }*/ + + $this->log->notice(__METHOD__.": $del Billing records deleted"); + //$stop = false; foreach(ContractModel::search(["finish_date<" => mktime(0,0,0,$now_month, $now_day, $now_year), "cancel_date" => null]) as $contract) { + //while(!$stop) { + //$stop = true; + //$contract = new Contract(1475); + //var_dump($contract);exit; - //$contract = new Contract(1); - - - - $bill_month = $now_month; $bill_year = $now_year; //$bill_day = $now_day; @@ -108,22 +138,27 @@ class BillingController extends mfBaseController { $monthly_bill_period_to->modify("last day of this month"); $contract_finish_date = new DateTime("@".$contract->finish_date); - $finish_year = date("Y", $contract->finish_date); - $finish_month = date("m", $contract->finish_date); - $finish_day = date("d", $contract->finish_date); + $contract_finish_date->setTimezone(new DateTimeZone("Europe/Vienna")); + $contract_finish_date->setTime(0,0,0); + if($contract->billing_delay) { + // add billing delay to finish_date so the first x months won't be billed + $this->log->debug(__METHOD__.": Adding ".$contract->billing_delay." billing_delay months to finish_date"); + $contract_finish_date->modify("+".$contract->billing_delay." months"); + } - //echo "$bill_month $bill_year\n";exit; + $finish_year = $contract_finish_date->format("Y"); + $finish_month = $contract_finish_date->format("m"); + $finish_day = $contract_finish_date->format("d"); - //echo $monthly_bill_period_to->format("Y-m-d H:i:s");exit; if($contract_finish_date > $monthly_bill_period_to) { $this->log->debug(__METHOD__.": Ignoring Contract ".$contract->id." because finish_date is in $finish_month $finish_year"); continue; } - if($contract->billing_period < 1) { + /*if($contract->billing_period < 1) { $this->log->debug(__METHOD__.": Ignoring Contract ".$contract->id." because billing_period == 0"); continue; - } + }*/ if($contract->price == 0 && $contract->price_setup == 0) { $this->log->debug(__METHOD__.": Ignoring Contract ".$contract->id." because price and price_setup == 0"); continue; @@ -138,177 +173,156 @@ class BillingController extends mfBaseController { } } - $start_date = new DateTime("@".$contract->finish_date); - $start_date->setTimezone(new DateTimeZone("Europe/Vienna")); + //$start_date = clone $contract_finish_date; // ignore yearly contracts which are not billable this month - if($contract->billing_period == 12) { - if($start_date->format("m") != $bill_month) { + /*if($contract->billing_period == 12) { + if($contract_finish_date->format("m") != $bill_month) { continue; } - } + }*/ $create_bills = []; // Concurrent Billing // find not yet billed periods - $create_dates = []; - - $create_date = clone $start_date; - //$create_date->modify("+".$contract->billing_period." month"); - $create_date->modify("first day of this month"); - $last_create_date = false; - //echo "first create_date: ".$create_date->format("Y-m-d H:i:s")."
    \n"; - //echo "while ".$create_date->getTimestamp()." (".$create_date->format("Y-m-d H:i:s").") >= ".$contract->finish_date." (".date("Y-m-d H:i:s", $contract->finish_date).")
    \n"; - - while($create_date->getTimestamp() >= $contract->finish_date) { - //echo "in need date while (".$create_date->format("Y-m-d H:i:s").")
    "; - if($last_create_date) { - // must for safety / shouldn't happen - die("need-date ran out of dates"); - } - - //echo "    "; - //echo $create_date->format("Y")." == ".$finish_year." && ".$create_date->format("m")." == ".$finish_month."
    "; - if($create_date->format("Y") == $finish_year && $create_date->format("m") == $finish_month) { - $create_date->setDate($finish_year, $finish_month, $finish_day); - //echo "set last_create_date true
    "; - $last_create_date = true; - } - - $existing_bill = BillingModel::getFirst(["contract_id" => $contract->id, "start_date" => $create_date->format("Y-m-d")]); - //var_dump($need_bill);exit; - if(!$existing_bill) { - //echo "adding date to create_dates[]
    "; - $new_create_date = clone $create_date; - $create_dates[] = $new_create_date; - $create_date->modify("-".$contract->billing_period." months"); + if(!$contract->billing_period) { + // setup only + if(BillingModel::getFirst(["contract_id" => $contract->id])) { continue; } - break; - } - //var_dump($create_dates); - // find missing billings - foreach($create_dates as $start_date) { - $price_setup = 0; - if($start_date->format("Y") == $finish_year && $start_date->format("m") == $finish_month) { - $price_setup = $contract->price_setup; - } $create_bills[] = [ - "start_date" => $start_date, - "price_setup" => $price_setup // set Setup price to 0, because it was billed already - ]; - } - - /*$last_billings = BillingModel::count(["contract_id" => $contract->id]); - if(!$last_billings) { - // First billing - $start_date->setTime(0,0,0); - //echo "start_date: ".$start_date->format("Y-m-d H:i:s")."
    "; - $create_bills[] = [ - "start_date" => $start_date, + "start_date" => $contract_finish_date, + "end_date" => $contract_finish_date, "price_setup" => $contract->price_setup ]; } else { - // Concurrent Billing - // find not yet billed periods - + // contracts with billing period $create_dates = []; - $create_date = clone $start_date; - $create_date->modify("+1 month"); - $create_date->modify("first day of this month"); - $last_create_date = false; - //echo "first create_date: ".$create_date->format("Y-m-d H:i:s")."
    \n"; - //echo "while ".$create_date->getTimestamp()." (".$create_date->format("Y-m-d H:i:s").") >= ".$contract->finish_date." (".date("Y-m-d H:i:s", $contract->finish_date).")
    \n"; + //echo "initial bill_date: ".$bill_date->format("Y-m-d")."
    "; - while($create_date->getTimestamp() >= $contract->finish_date) { - //echo "in need date while (".$create_date->format("Y-m-d H:i:s").")
    "; + // if more than 1 month period, adjust initial billing_date to contracts finish_date in current period + // otherwise i.e. yearly contracts older than one year would never be billed ever, or in the best case + // they would be billed a few months too late + if($contract->billing_period > 1 && $bill_date > $contract_finish_date) { + $new_bill_date = clone $contract_finish_date; + $new_bill_date->modify("+".$contract->billing_period." months"); + + while($new_bill_date->format("Ymd") < $bill_date->format("Ymd")) { + $new_bill_date->modify("+".$contract->billing_period." months"); + } + + if($new_bill_date->format("Ymd") > $bill_date->format("Ymd")) { + $new_bill_date->modify("-".$contract->billing_period." months"); + } + + $bill_date = $new_bill_date; + //echo "new bill_date: ".$bill_date->format("Y-m-d"); + } + $create_date = clone $bill_date; + $create_date->modify("first day of this month"); + $create_date->setTime(0,0,0); + $last_create_date = false; + + $earliest_bill_date = clone $contract_finish_date; + $earliest_bill_date->modify("first day of this month"); + $earliest_bill_date->setTime(0,0,0); + + while($create_date->format("Y-m-d") >= $earliest_bill_date->format("Y-m-d")) { if($last_create_date) { - // must for safety / shouldn't happen + // just for safety / shouldn't happen die("need-date ran out of dates"); } - //echo "    "; - //echo $create_date->format("Y")." == ".$finish_year." && ".$create_date->format("m")." == ".$finish_month."
    "; if($create_date->format("Y") == $finish_year && $create_date->format("m") == $finish_month) { + // this is the finish month, so set day back to day of finish_date $create_date->setDate($finish_year, $finish_month, $finish_day); - //echo "set last_create_date true
    "; $last_create_date = true; } $existing_bill = BillingModel::getFirst(["contract_id" => $contract->id, "start_date" => $create_date->format("Y-m-d")]); - //var_dump($need_bill);exit; if(!$existing_bill) { - //echo "adding date to create_dates[]
    "; $new_create_date = clone $create_date; $create_dates[] = $new_create_date; - $create_date->modify("-".$contract->billing_period." months"); + $create_date->modify("-" . $contract->billing_period . " months"); continue; } break; } - //var_dump($create_dates); // find missing billings foreach($create_dates as $start_date) { - // ignore if last billing row is from this month - /*if($start_date->getTimestamp() < $earliest_next_billing_date->getTimestamp()) { - $this->log->debug(__METHOD__.": last billing row is current billing row. Skip creating new billing row"); - continue; - }*//* + $price_setup = 0; + if($start_date->format("Y") == $finish_year && $start_date->format("m") == $finish_month) { + $price_setup = $contract->price_setup; + } $create_bills[] = [ "start_date" => $start_date, - "price_setup" => 0 // set Setup price to 0, because it was billed already + "price_setup" => $price_setup // set Setup price to 0, because it was billed already ]; } - }*/ - + } $create_bills = array_reverse($create_bills); //var_dump($create_bills);exit; foreach($create_bills as $bill_data) { $start_date = $bill_data["start_date"]; + $end_date = (array_key_exists("end_date", $bill_data)) ? $bill_data["end_date"] : null; $price_setup = $bill_data["price_setup"]; + if($contract->billing_period > 1 && $start_date < $yearly_not_before) { + // XXX only for 1st Billing after IVT Import + $this->log->debug(__METHOD__.": Ignoring Contract ".$contract->id." with start_date ".$start_date->format("Y-m-d")." because is yearly and before ".$yearly_not_before->format("Y-m-d")); + continue; + } + // if contract has cancel date this month // use cancel date as end_date if ($cancel_date) { $end_date = clone $cancel_date; - } else { + } elseif(!$end_date) { // else calculate last of month $end_date = clone $start_date; $end_date->modify("first day of this month"); $end_date->modify("+" . $contract->billing_period . " months"); - //$end_date->modify("first day of this month"); $end_date->modify("-1 day"); } + + $sday = $start_date->format("d"); $eday = $end_date->format("d"); - if ($sday > 1 || $cancel_date) { + if ($contract->price && ($sday > 1 || $cancel_date)) { // aliquoter preis $days = ($eday - $sday) + 1; - //echo "days: $days
    "; - $pc = $days / $eday * 100; - //echo "pc: $pc
    "; + + $first_of_period = clone $start_date; + $first_of_period->modify("first day of this month"); + + $total_days = $end_date->diff($first_of_period)->format("%a") + 1; + $period_days = ($end_date->diff($start_date)->format("%a")) + 1; + + $pc = $period_days / $total_days * 100; $price = round($contract->price / 100 * $pc, 4); + + /*if($contract->id == 8766) { + echo "start date: ".$start_date->format("Y-m-d H:i:s") . "
    "; + echo "end date: ".$end_date->format("Y-m-d H:i:s") . "

    "; + echo "first_of_period: " . $first_of_period->format("Y-m-d H:i:s") . "
    "; + echo "total days: $total_days
    "; + echo "period days: $period_days
    "; + echo "price: $price

    "; + echo "classic days: $days
    "; + exit; + }*/ } else { $price = $contract->price; } - /* - echo "contact ID: ".$contract->id."
    "; - echo "contract price: ". $contract->price."
    "; - echo "price: ". $price."
    "; - echo "start_date: ".$start_date->format("Y-m-d H:i:s")."
    "; - echo "sday: $sday
    "; - exit; - */ - $owner = $contract->owner; $billingaddress = $contract->billingaddress; @@ -334,6 +348,7 @@ class BillingController extends mfBaseController { $data["contract_id"] = $contract->id; $data["start_date"] = $start_date->format("Y-m-d"); $data["end_date"] = $end_date->format("Y-m-d"); + $data["owner_id"] = $contract->owner_id; $data["billingaddress_id"] = ($contract->billingaddress_id) ? $contract->billingaddress_id : $contract->owner_id; $data["customer_number"] = $contract->owner->customer_number; $data["company"] = $billingaddress->company; @@ -351,13 +366,14 @@ class BillingController extends mfBaseController { $data["bank_account_owner"] = $billingaddress->bank_account_owner; $data["bank_account_iban"] = $billingaddress->bank_account_iban; $data["bank_account_bic"] = $billingaddress->bank_account_bic; - $data["matchcode"] = $contract->mathcode; + $data["matchcode"] = $contract->matchcode; $data["product_id"] = $contract->product_id; $data["product_name"] = $contract->product_name; $data["product_info"] = $contract->product_info; $data["amount"] = $contract->amount; $data["price"] = $price; $data["price_setup"] = $price_setup; + $data["vatrate"] = $contract->vatrate; $data["billing_period"] = $contract->billing_period; $billing = BillingModel::create($data); diff --git a/application/Billing/BillingModel.php b/application/Billing/BillingModel.php index 53f16efec..b0bbc08af 100644 --- a/application/Billing/BillingModel.php +++ b/application/Billing/BillingModel.php @@ -2,10 +2,10 @@ class BillingModel { public $invoice_id; - public $invoice_date; public $contract_id; public $start_date; public $end_date; + public $owner_id; public $billingaddress_id; public $customer_number; public $company; @@ -30,8 +30,8 @@ class BillingModel { public $amount; public $price; public $price_setup; - public $billing_period; - + public $vatrate; + public $create_by; public $edit_by; public $create; @@ -117,6 +117,31 @@ class BillingModel { return null; } + public static function getInvoiceBaseData($filter) { + $db = FronkDB::singleton(); + + $items = []; + + $where = self::getSqlFilter($filter); + $sql = "SELECT owner_id, billingaddress_id, billing_type, billing_delivery FROM Billing + WHERE $where + GROUP BY owner_id, billingaddress_id, billing_type, billing_delivery + ORDER BY owner_id, billingaddress_id, billing_type, billing_delivery"; + //var_dump($sql);exit; + $res = $db->query($sql); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[] = [ + "owner_id" => $data->owner_id, + "billingaddress_id" => $data->billingaddress_id, + "billing_type" => $data->billing_type, + "billing_delivery" => $data->billing_delivery + ]; + } + } + return $items; + } + public static function count($filter) { $db = FronkDB::singleton(); @@ -196,16 +221,11 @@ class BillingModel { $invoice_id = $filter['invoice_id']; if(is_numeric($invoice_id)) { $where .= " AND Billing.invoice_id=$invoice_id"; + } elseif($invoice_id === null || $invoice_id === false) { + $where .= " AND (Billing.invoice_id IS NULL OR Billing.invoice_id=0)"; } } - if(array_key_exists("invoice_date", $filter)) { - $invoice_date = $filter['invoice_date']; - if($invoice_date) { - $where .= " AND Billing.invoice_date='$invoice_date'"; - } - } - if(array_key_exists("contract_id", $filter)) { $contract_id = $filter['contract_id']; if(is_numeric($contract_id)) { @@ -237,7 +257,7 @@ class BillingModel { if(array_key_exists("start_date>=", $filter)) { $start_date = FronkDB::singleton()->escape($filter['start_date>=']); if($start_date) { - $where .= " AND Billing.start_date >='$start_date'"; + $where .= " AND Billing.start_date >= '$start_date'"; } } @@ -248,6 +268,13 @@ class BillingModel { } } + if(array_key_exists("owner_id", $filter)) { + $owner_id = $filter['owner_id']; + if(is_numeric($owner_id)) { + $where .= " AND Billing.owner_id=$owner_id"; + } + } + if(array_key_exists("billingaddress_id", $filter)) { $billingaddress_id = $filter['billingaddress_id']; if(is_numeric($billingaddress_id)) { @@ -384,10 +411,24 @@ class BillingModel { } - if(array_key_exists("billing_period", $filter)) { - $billing_period = $filter['billing_period']; - if(is_numeric($billing_period)) { - $where .= " AND Billing.billing_period = $billing_period"; + if(array_key_exists("billing_type", $filter)) { + $billing_type = $filter['billing_type']; + if(is_numeric($billing_type)) { + $where .= " AND Billing.billing_type = $billing_type"; + } + } + + if(array_key_exists("billing_type", $filter)) { + $billing_type = $db->escape($filter['billing_type']); + if($billing_type) { + $where .= " AND Billing.billing_type = '$billing_type'"; + } + } + + if(array_key_exists("billing_delivery", $filter)) { + $billing_delivery = $db->escape($filter['billing_delivery']); + if($billing_delivery) { + $where .= " AND Billing.billing_delivery = '$billing_delivery'"; } } diff --git a/application/Contract/Contract.php b/application/Contract/Contract.php index 575ae852b..7427bfe46 100644 --- a/application/Contract/Contract.php +++ b/application/Contract/Contract.php @@ -32,7 +32,7 @@ class Contract extends mfBaseModel { private $creator; private $editor; - + private function getLinks() { $this->linkFrom = []; $this->linkTo = []; diff --git a/application/Contract/ContractController.php b/application/Contract/ContractController.php index d77ec2a23..1c4274eb2 100644 --- a/application/Contract/ContractController.php +++ b/application/Contract/ContractController.php @@ -350,9 +350,6 @@ class ContractController extends mfBaseController } } - /* - * TODO: alten Credit Vertag kündigen und neuen anlegen - */ foreach (ContractLinkModel::search(['type' => "credit", 'contract_id' => $origin->id]) as $old_credit) { // verlinkten Contract kündigen (wenn nicht schon gekündigt) if ($old_credit->change_action == "recreate" && !$old_credit->contract->cancel_date) { @@ -368,7 +365,6 @@ class ContractController extends mfBaseController } } - } @@ -379,9 +375,12 @@ class ContractController extends mfBaseController $this->redirect("Contract", "view", ['contract_id' => $id]); } - /* - * bestehende Links übernehmen oder kündigen - */ + // create Journal + $journal = ContractjournalModel::create([ + 'contract_id' => $contract->id, + 'type' => "contract_finished" + ]); + $journal_id = $journal->save(); $this->layout()->setFlash("Contract erfolgreich fertiggestellt", "success"); $this->redirect("Contract", "view", ['contract_id' => $id]); @@ -475,6 +474,8 @@ class ContractController extends mfBaseController } //var_dump($r->get());exit; + + $contract_data = []; $contract_data["owner_id"] = (int)$r->owner_id; $contract_data["billingaddress_id"] = ($r->billingaddress_id) ? (int)$r->billingaddress_id : null; @@ -489,11 +490,34 @@ class ContractController extends mfBaseController $contract_data['price_nbe'] = (float)$r->price_nbe; $contract_data['billing_period'] = (int)$r->billing_period; $contract_data['billing_delay'] = (int)$r->billing_delay; - $contract_data['order_date'] = ($r->order_date) ? $this->dateToTimestamp($r->order_date) : null; - $contract_data['finish_date'] = ($r->finish_date) ? $this->dateToTimestamp($r->finish_date) : null; - $contract_data['cancel_date'] = ($r->cancel_date) ? $this->dateToTimestamp($r->cancel_date) : null; $contract_data['note'] = $r->note; + if($r->order_date) { + $order_date = new DateTime("@" . $this->dateToTimestamp($r->order_date)); + $order_date->setTimezone(new DateTimeZone("Europe/Vienna")); + $order_date->setTime(0, 0, 0); + $contract_data['order_date'] = $order_date->getTimestamp(); + } + + if($r->finish_date) { + $finish_date = new DateTime("@" . $this->dateToTimestamp($r->finish_date)); + $finish_date->setTimezone(new DateTimeZone("Europe/Vienna")); + $finish_date->setTime(0, 0, 0); + $contract_data['finish_date'] = $finish_date->getTimestamp(); + } + + if($r->cancel_date) { + $cancel_date = new DateTime("@" . $this->dateToTimestamp($r->cancel_date)); + $cancel_date->setTimezone(new DateTimeZone("Europe/Vienna")); + $cancel_date->setTime(0, 0, 0); + $contract_data['cancel_date'] = $cancel_date->getTimestamp(); + } + + + + + + //var_dump($contract_data);exit; @@ -513,7 +537,7 @@ class ContractController extends mfBaseController $this->layout()->setFlash("Bitte Produkt auswählen.", "error"); return $this->addAction(); } - if (!$contract_data['billing_period']) { + if (!in_array($contract_data['billing_period'], [0,1,12])) { $this->layout()->setFlash("Bitte Rechnungsperiode auswählen.", "error"); return $this->addAction(); } diff --git a/application/Contract/ContractModel.php b/application/Contract/ContractModel.php index 94d78d311..300fd5ef1 100644 --- a/application/Contract/ContractModel.php +++ b/application/Contract/ContractModel.php @@ -15,6 +15,7 @@ class ContractModel { public $product_external_id; public $price = null; public $price_setup = null; + public $vatrate = null; public $price_nne = null; public $price_nbe = null; public $billing_delay = null; diff --git a/application/Contractqueue/ContractqueueModel.php b/application/Contractqueue/ContractqueueModel.php index 1d8f85f67..6f195a21a 100644 --- a/application/Contractqueue/ContractqueueModel.php +++ b/application/Contractqueue/ContractqueueModel.php @@ -87,7 +87,7 @@ class ContractqueueModel { $data["order_id"] = $order->id; $data["orderproduct_id"] = $op->id; $data["owner_id"] = $order->owner_id; - $data["billingaddress_id"] = $order->billingaddress_id; + $data["billingaddress_id"] = ($order->billingaddress_id) ? $order->billingaddress_id : $order->owner_id; $data["termination_id"] = ($op->termination_id) ? $op->termination_id : null; $data["product_id"] = $op->product_id; $data["product_name"] = $product->name; diff --git a/application/Invoice/Invoice.php b/application/Invoice/Invoice.php new file mode 100644 index 000000000..871693ebb --- /dev/null +++ b/application/Invoice/Invoice.php @@ -0,0 +1,60 @@ +$name == null) { + + if(!$this->id) { + return null; + } + + + if($name == "positions") { + $positions = InvoicepositionModel::search(["invoice_id" => $this->id]); + $this->positions = $positions; + return $this->positions; + } + + if($name == "creator") { + $this->creator = mfValuecache::singleton()->get("Worker-id-".$this->create_by); + if($this->creator === null) { + $this->creator = new User($this->create_by); + if($this->creator->id) { + mfValuecache::singleton()->set("Worker-id-".$this->create_by, $this->creator); + } + } + return $this->creator; + } + + if($name == "editor") { + $this->editor = mfValuecache::singleton()->get("Worker-id-".$this->edit_by); + if($this->editor === null) { + $this->editor = new User($this->edit_by); + if($this->editor->id) { + mfValuecache::singleton()->set("Worker-id-".$this->edit_by, $this->editor); + } + } + return $this->editor; + } + + $classname = ucfirst($name); + $idfield = $name."_id"; + $this->$name = mfValuecache::singleton()->get("mfObjectmodel-$name-".$this->$idfield); + if(!$this->$name) { + $this->$name = new $classname($this->$idfield); + } + + if($this->$name->id) { + mfValuecache::singleton()->set("mfObjectmodel-$name-".$this->$name->id, $this->$name); + return $this->$name; + } else { + return null; + } + + } + + return $this->$name; + } +} \ No newline at end of file diff --git a/application/Invoice/InvoiceController.php b/application/Invoice/InvoiceController.php new file mode 100644 index 000000000..61b014674 --- /dev/null +++ b/application/Invoice/InvoiceController.php @@ -0,0 +1,345 @@ +needlogin = true; + $me = new User(); + $me->loadMe(); + $this->me = $me; + $this->layout()->set("me", $me); + + if (!$me->is(["Admin"])) { + $this->redirect("Dashboard"); + } + } + + protected function indexAction() { + $this->layout()->setTemplate("Invoice/Index"); + + if ($this->request->resetFilter) { + unset($_SESSION[MFAPPNAME . '-Invoice-filter']); + } + + $filter = []; + if (is_array($this->request->filter)) { + $filter = $this->request->filter; + $_SESSION[MFAPPNAME . '-Invoice-filter'] = $filter; + } else { + if (array_key_exists(MFAPPNAME . '-Invoice-filter', $_SESSION) && count($_SESSION[MFAPPNAME . '-Invoice-filter'])) { + $filter = $_SESSION[MFAPPNAME . '-Invoice-filter']; + } + } + + $this->layout->set("filter", $filter); + $filter = $this->getPreparedFilter($filter); + + // pagination defaults + $pagination = []; + $pagination['start'] = 0; + $pagination['count'] = 50; + $pagination['maxItems'] = 0; + + if(is_numeric($this->request->s)) { + $pagination['start'] = intval($this->request->s); + } + //var_dump($filter);exit; + $pagination['maxItems'] = InvoiceModel::count($filter); + $billings = InvoiceModel::search($filter, $pagination); + + $this->layout()->set("invoices", $billings); + $this->layout()->set("pagination", $pagination); + } + + private function getPreparedFilter($filter) + { + $new_filter = []; + + if(array_key_exists("customer", $filter)) { + if(array_key_exists("customer", $filter) && $filter["customer"]) { + $kunde = $this->db()->escape($filter['customer']); + if(!array_key_exists("add-where", $new_filter)) $new_filter["add-where"] = ""; + $new_filter['add-where'] .= " AND (company like '%$kunde%' OR firstname like '%$kunde%' OR lastname like '%$kunde%' OR concat(firstname, ' ', lastname) like '%$kunde%' OR concat(lastname, ' ', firstname) like '%$kunde%')"; + } + } + + if(array_key_exists("address", $filter)) { + if(array_key_exists("address", $filter) && $filter["address"]) { + $search = $this->db()->escape($filter['address']); + if(!array_key_exists("add-where", $new_filter)) $new_filter["add-where"] = ""; + $new_filter['add-where'] .= " AND (street like '%$search%' OR zip like '%$search%' OR city like '%$search%' OR country like '%$search%')"; + } + } + + 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 Invoice($id); + if (!$invoice->id) { + $this->layout()->setFlash("Rechnung nicht gefunden", "error"); + $this->redirect("Rechnung"); + } + + + + $vat = []; + + foreach($invoice->positions as $p) { + if(!array_key_exists($p->vatrate, $vat)) { + $vat[$p->vatrate] = 0; + } + $vat[$p->vatrate] += $p->price_gross - $p->price; + } + + $pdf_vars = [ + "invoice" => $invoice, + "vat" => $vat + ]; + + /*$this->layout()->setTemplate("Invoice/Print.pdf"); + $this->layout()->set("invoice", $invoice); + $this->layout()->set("vat", $vat); + $this->layout()->set("ressourcePathPrefix", MFFANCYBASEURL."/"); + return true;*/ + + $pdf = new PdfForm("Invoice/Print.pdf", $pdf_vars); + //$pdfpath = $pdf->render(); + $pdf->download($invoice->invoice_number.".pdf"); + + + + } + + protected function runInvoicingAction() { + $i = 0; + $p = 0; + + // get pairs of owner_id and billingaddress_id, so each will be its own invoice + foreach(BillingModel::getInvoiceBaseData(['invoice_id' => null]) as $base) { + //var_dump($base); continue; + $owner_id = $base["owner_id"]; + $billingaddress_id = $base["billingaddress_id"]; + $billing_type = $base["billing_type"]; + $billing_delivery = $base["billing_delivery"]; + + $bill_positions = []; + $credit_positions = []; + + $billing_rows = BillingModel::search([ + "owner_id" => $owner_id, + "billingaddress_id" => $billingaddress_id, + "billing_type" => $billing_type, + "billing_delivery" => $billing_delivery, + "invoice_id" => null]); + + if(!count($billing_rows)) { + die("Keine nicht verrechneten Billing records für billingaddress_id"); + } + + //var_dump($owner_id, $billingaddress_id, $bills);exit; + + $invoice_data = []; + + foreach($billing_rows as $bill) { + $vatrate = $bill->vatrate; + $price = $bill->price; + $price_total = $bill->price * $bill->amount; + $price_gross = ($vatrate) ? $price_total + ($price_total / 100) * $vatrate : $price_total; + $price_setup = $bill->price_setup; + $price_setup_total = $bill->price_setup * $bill->amount; + $price_setup_gross = ($vatrate) ? $price_setup_total + ($price_setup_total / 100) * $vatrate : $price_setup_total; + $add_setup_position = ($price > 0 && $price_setup > 0); + $is_setup_only = ($price < 0.00001 && $price_setup > 0); + + $position_data = []; + $position_data["billing_id"] = $bill->id; + $position_data["contract_id"] = $bill->contract_id; + $position_data["start_date"] = $bill->start_date; + $position_data["end_date"] = $bill->end_date; + $position_data["matchcode"] = $bill->matchcode; + $position_data["product_id"] = $bill->product_id; + $position_data["product_name"] = $bill->product_name; + $position_data["product_info"] = $bill->product_info; + $position_data["amount"] = $bill->amount; + $position_data["billing_period"] = $bill->billing_period; + + + if($is_setup_only) { + $this->log->debug("Contract ID ". $bill->contract_id." is setup only"); + $position_data["price"] = $price_setup; + $position_data["price_total"] = $price_setup_total; + $position_data["price_gross"] = $price_setup_gross; + $position_data["vatrate"] = $vatrate; + $position_data["end_date"] = $position_data["start_date"]; + } else { + $position_data["price"] = $price; + $position_data["price_total"] = $price_total; + $position_data["price_gross"] = $price_gross; + $position_data["vatrate"] = $vatrate; + } + + $new_position = InvoicepositionModel::create($position_data); + $bill_positions[] = $new_position; + + if($add_setup_position) { + $this->log->debug("Adding Setup Invoiceposition for Contract ID ". $bill->contract_id); + $setup_data = $position_data; + $setup_data["product_name"] = "Herstellungskosten ".$bill->product_name; + $setup_data["product_info"] = ""; + $setup_data["price"] = $price_setup; + $setup_data["price_total"] = $price_setup * $bill->amount; + $setup_data["price_gross"] = $price_setup_gross; + $setup_data["vatrate"] = $vatrate; + $setup_data["end_date"] = $setup_data["start_date"]; + $setup_position = InvoicepositionModel::create($setup_data); + $bill_positions[] = $setup_position; + } + + + /*if($bill->price >= 0 || $bill->price_setup >= 0) { + $bill_positions[] = InvoicepositionModel::create($position_data); + } else { + $credit_positions[] = InvoicepositionModel::create($position_data);; + }*/ + + $invoice_data["owner_id"] = $owner_id; + $invoice_data["billingaddress_id"] = $billingaddress_id; + $invoice_data["customer_number"] = $bill->customer_number; + $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["uid"] = $bill->uid; + $invoice_data["billing_type"] = $billing_type; + $invoice_data["billing_delivery"] = $billing_delivery; + $invoice_data["bank_account_bank"] = $bill->bank_account_bank; + $invoice_data["bank_account_owner"] = $bill->bank_account_owner; + $invoice_data["bank_account_iban"] = $bill->bank_account_iban; + $invoice_data["bank_account_bic"] = $bill->bank_account_bic; + $invoice_data["total"] = 0; + $invoice_data["total_gross"] = 0; + $invoice_data["total_vat"] = 0; + + + + } + + + //var_dump($bill_positions, $credit_positions);exit; + + + // create Invoice + $invoice = InvoiceModel::create($invoice_data); + $invoice->startTransaction(); + + try { + if(!$invoice->save()) { + $invoice->rollbackTransaction(); + die("Error saving Invoice"); + } + + $total_net = 0; + $total_gross = 0; + $total_vat = 0; + + foreach($bill_positions as $position) { + // on error: rollback transaction + // add Invoice::id to Invoiceposition + $position->invoice_id = $invoice->id; + + + if(!$position->vatrate) { + $total_net += $position->price_total; + } else { + $total_vat += ($position->price_total / 100) * $position->vatrate; + $total_net += $position->price_total; + $total_gross += $position->price_gross; + } + + // save Invoiceposition + if(!$position->save()) { + $invoice->rollbackTransaction(); + die("Error saving Invoiceposition"); + } + + // ad Invoice::id to Bill + $bill = new Billing($position->billing_id); + if(!$bill->id) { + $invoice->rollbackTransaction(); + die("Bill for Invoiceposition not found"); + } + $bill->invoice_id = $invoice->id; + + if(!$bill->save()) { + $invoice->rollbackTransaction(); + die("error saving invoice_id to bill"); + } + $p++; + + } + + $invoice->total = $total_net; + $invoice->total_gross = $total_gross; + $invoice->total_vat = $total_vat; + + if(!$invoice->save()) { + $invoice->rollbackTransaction(); + die("Error saving totals in Invoice"); + } + // generate Invoice number + + $new_num = InvoiceModel::getNextInvoiceNUmber(); + $invoice->invoice_number = $new_num; + $invoice->invoice_date = date("U"); + + if(!$invoice->save()) { + $invoice->rollbackTransaction(); + die("Error saving Invoice number and date"); + } + + // commit transaction + $invoice->commitTransaction(); + $i++; + } catch (Exception $e) { + if($invoice) { + $invoice->rollbackTransaction(); + } + + die("Error saving Invoice!\n"); + + } + + // Create Invoice PDF + + + // if billing_delivery == paper -> add to pdf collection + + // else -> send by email + + //var_dump($invoice); + //exit; + } + + $this->layout()->setFlash("$i Rechnungen mit $p Rechnungspositionen erstellt"); + $this->redirect("Invoice"); + + } +} \ No newline at end of file diff --git a/application/Invoice/InvoiceModel.php b/application/Invoice/InvoiceModel.php new file mode 100644 index 000000000..a9b2b7d77 --- /dev/null +++ b/application/Invoice/InvoiceModel.php @@ -0,0 +1,340 @@ + $value) { + if(property_exists(get_called_class(), $field)) { + $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 getNextInvoiceNUmber() { + $last_invoice_num = self::getLastInvoiceNumber(); + + if(!$last_invoice_num) { + return TT_FIRST_INVOICE_NUM; + } + + $year_part = 0; + $num_part = 0; + + $m = []; + if(preg_match('/^RN(\d+)-X(\d+)$/', $last_invoice_num, $m)) { + if(array_key_exists(1, $m)) { + $year_part = $m[1]; + if(array_key_exists(2, $m)) { + $num_part = $m[2]; + } + } + } + + if(!$year_part || !$num_part) { + die("NO LAST INVOICE NUMBER!!!"); + } + + //var_dump($year_part, $num_part);exit; + if(date("Y") == $year_part) { + $new_year_part = $year_part; + $new_num_part = $num_part + 1; + } else { + $new_year_part = date("Y"); + $new_num_part = 1; + } + + $new_invoice_num = "RN$new_year_part-X".str_pad($new_num_part,"6", "0", STR_PAD_LEFT); + return $new_invoice_num; + } + + public static function getLastInvoiceNumber() { + $last_invoice = self::getLast(["invoice_number" => true]); + if(!$last_invoice || !$last_invoice->invoice_number) { + return false; + } + return $last_invoice->invoice_number; + } + + public static function getAll() { + $items = []; + + $db = FronkDB::singleton(); + + $res = $db->select("Invoice", "*", "1 = 1 ORDER BY invoice_number"); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[] = new Invoice($data); + } + } + return $items; + + } + + public static function getFirst($filter) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT * FROM Invoice + WHERE $where + ORDER BY invoice_number LIMIT 1"; + //var_dump($sql);exit; + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + $item = new Invoice($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 * FROM Invoice + WHERE $where + ORDER BY invoice_number DESC LIMIT 1"; + + //mfLoghandler::singleton()->debug($sql); + + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + $item = new Invoice($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 Invoice + 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 = "invoice_number ASC"; + } + + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT * FROM Invoice + 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 Invoice($data); + } + } + + return $items; + } + + private static function getSqlFilter($filter) { + $where = "1=1 "; + + $db = FronkDB::singleton(); + + //var_dump($filter);exit; + + if(array_key_exists("id", $filter)) { + $id = $filter['id']; + if(is_numeric($id)) { + $where .= " AND Invoice.id like '%$id%'"; + } + } + + if(array_key_exists("invoice_number", $filter)) { + $invoice_number = $filter['invoice_number']; + if($invoice_number === true) { + $where .= " AND Invoice.invoice_number IS NOT NULL AND Invoice.invoice_number <> ''"; + } elseif($invoice_number) { + $invoice_number = $db->escape($invoice_number); + $where .= " AND Invoice.invoice_number='$invoice_number'"; + } elseif($invoice_number === null || $invoice_number === false) { + $where .= " AND Invoice.invoice_number IS NULL"; + } + } + + if(array_key_exists("invoice_date", $filter)) { + $invoice_date = $filter['invoice_date']; + if($invoice_date) { + $where .= " AND Invoice.invoice_date='$invoice_date'"; + } elseif($invoice_date === null || $invoice_date === false) { + $where .= " AND Invoice.invoice_date IS NULL"; + } + } + + + if(array_key_exists("billingaddress_id", $filter)) { + $Invoiceaddress_id = $filter['billingaddress_id']; + if(is_numeric($Invoiceaddress_id)) { + $where .= " AND Invoice.billingaddress_id=$Invoiceaddress_id"; + } + } + + if(array_key_exists("customer_number", $filter)) { + $customer_number = $filter['customer_number']; + if(is_numeric($customer_number)) { + $where .= " AND Invoice.customer_number LIKE $customer_number"; + } + } + + 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("add-where", $filter)) { + $where .= " ".$filter['add-where']; + } + + + if(array_key_exists("Invoice_period", $filter)) { + $Invoice_period = $filter['Invoice_period']; + if(is_numeric($Invoice_period)) { + $where .= " AND Invoice.Invoice_period = $Invoice_period"; + } + } + + //var_dump($filter, $where);exit; + return $where; + } + +} diff --git a/application/Invoiceposition/Invoiceposition.php b/application/Invoiceposition/Invoiceposition.php new file mode 100644 index 000000000..9c045f4a3 --- /dev/null +++ b/application/Invoiceposition/Invoiceposition.php @@ -0,0 +1,34 @@ +loadMe(); + + $old_id = $this->id; + $this->id = null; + + + // cleanup data + $this->invoice_id = null; + $this->create_by = $me->id; + $this->edit_by = $me->id; + + $this->create = null; + $this->edit = null; + $this->saved = 0; + $this->mode = "new"; + $this->_old_data = new StdClass(); + + /*$this->save(); + + if($old_id == $this->id) { + $this->log->error("save() of cloned Contract $old_id failed!"); + throw new Exception("Saving clone failed."); + }*/ + + //$this->log->debug("Cloned Invoiceposition $old_id"); + } +} \ No newline at end of file diff --git a/application/Invoiceposition/InvoicepositionModel.php b/application/Invoiceposition/InvoicepositionModel.php new file mode 100644 index 000000000..2bbb6460c --- /dev/null +++ b/application/Invoiceposition/InvoicepositionModel.php @@ -0,0 +1,165 @@ + $value) { + if(property_exists(get_called_class(), $field)) { + $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("Invoiceposition", "*", "1 = 1 ORDER BY invoice_id,contract_id,start_date,matchcode"); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[] = new Invoiceposition($data); + } + } + return $items; + + } + + public static function getFirst($filter) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT * FROM Invoiceposition + WHERE $where + ORDER BY invoice_id,contract_id,start_date,matchcode LIMIT 1"; + //var_dump($sql);exit; + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + $item = new Invoiceposition($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 Invoiceposition + 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 = "invoice_id,contract_id,start_date,matchcode ASC"; + } + + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT * FROM Invoiceposition + 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 Invoiceposition($data); + } + } + + return $items; + } + + private static function getSqlFilter($filter) { + $where = "1=1 "; + + $db = FronkDB::singleton(); + + //var_dump($filter);exit; + + if(array_key_exists("id", $filter)) { + $id = $filter['id']; + if(is_numeric($id)) { + $where .= " AND Invoiceposition.id = $id"; + } + } + + if(array_key_exists("invoice_id", $filter)) { + $invoice_id = $filter['invoice_id']; + if(is_numeric($invoice_id)) { + $where .= " AND Invoiceposition.invoice_id=$invoice_id"; + } + } + + if(array_key_exists("add-where", $filter)) { + $where .= " ".$filter['add-where']; + } + + //var_dump($filter, $where);exit; + return $where; + } + +} diff --git a/application/User/UserController.php b/application/User/UserController.php index 6ee1c0352..d239ce342 100644 --- a/application/User/UserController.php +++ b/application/User/UserController.php @@ -232,6 +232,7 @@ class UserController extends mfBaseController $user->permissions->canVoipnumbering = "false"; $user->permissions->canPreorder = "false"; $user->permissions->canOrder = "false"; + $user->permissions->canBilling = "false"; $user->permissions->canFibu = "false"; $user->permissions->canStatistics = "false"; diff --git a/db/migrations/20240620160026_create_billing.php b/db/migrations/20240620160026_create_billing.php index 5eb323ad2..0fdaba2c1 100644 --- a/db/migrations/20240620160026_create_billing.php +++ b/db/migrations/20240620160026_create_billing.php @@ -10,16 +10,10 @@ final class CreateBilling extends AbstractMigration if($this->getEnvironment() == "thetool") { $table = $this->table("Billing"); $table->addColumn("invoice_id", "integer", ["null" => true, "default" => null]); - $table->addColumn("invoice_date", "date", ["null" => true, "default" => null]); $table->addColumn("contract_id", "integer", ["null" => false]); $table->addColumn("start_date", "date", ["null" => false]); - /*$table->addColumn("start_year", "integer", ["null" => false]); - $table->addColumn("start_month", "integer", ["null" => false]); - $table->addColumn("start_day", "integer", ["null" => false]);*/ $table->addColumn("end_date", "date", ["null" => false]); - /*$table->addColumn("end_year", "integer", ["null" => false]); - $table->addColumn("end_month", "integer", ["null" => false]); - $table->addColumn("end_day", "integer", ["null" => false]);*/ + $table->addColumn("owner_id", "integer", ["null" => false]); $table->addColumn("billingaddress_id", "integer", ["null" => false]); $table->addColumn("customer_number", "integer", ["null" => false]); $table->addColumn("company", "string", ["null" => true, "default" => null, "length" => 1024]); @@ -44,6 +38,7 @@ final class CreateBilling extends AbstractMigration $table->addColumn("amount", "decimal", ["null" => false, "precision" => 9, "scale" => 6]); $table->addColumn("price", "decimal", ["null" => false, "precision" => 14, "scale" => 4]); $table->addColumn("price_setup", "decimal", ["null" => false, "default" => 0, "precision" => 14, "scale" => 4]); + $table->addColumn("total_vat", "decimal", ["null" => false, "precision" => 14, "scale" => 4]); $table->addColumn("billing_period", "integer", ["null" => false, "default" => 0]); $table->addColumn("create_by", "integer", ["null" => false]); diff --git a/db/migrations/20240625162005_create_invoice_tables.php b/db/migrations/20240625162005_create_invoice_tables.php new file mode 100644 index 000000000..3b5a426c8 --- /dev/null +++ b/db/migrations/20240625162005_create_invoice_tables.php @@ -0,0 +1,80 @@ +getEnvironment() == "thetool") { + $invoice = $this->table("Invoice"); + + $invoice->addColumn("invoice_number", "string", ["null" => true, "default" => null]); // RN2024-X000001 + $invoice->addColumn("invoice_date", "integer", ["default" => 0]); + $invoice->addColumn("billingaddress_id", "integer", ["null" => false]); + $invoice->addColumn("customer_number", "integer", ["null" => false]); + $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" => 4]); + $invoice->addColumn("total_gross", "decimal", ["null" => false, "precision" => 14, "scale" => 4]); + $invoice->addColumn("vatrate", "decimal", ["null" => false, "default" => 0, "precision" => 6, "scale" => 2]); + $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("Invoiceposition"); + $ip->addColumn("invoice_id", "integer", ["null" => true, "default" => null]); + $ip->addColumn("billing_id", "integer", ["null" => true, "default" => null]); + $ip->addColumn("contract_id", "integer", ["null" => false]); + $ip->addColumn("start_date", "date", ["null" => false]); + $ip->addColumn("end_date", "date", ["null" => true, "default" => null]); + $ip->addColumn("matchcode", "string", ["null" => true, "default" => null, "length" => 255]); + $ip->addColumn("product_id", "integer", ["null" => false]); + $ip->addColumn("product_name", "string", ["null" => false, "length" => 255]); + $ip->addColumn("product_info", "text", ["null" => true, "default" => null]); + $ip->addColumn("amount", "decimal", ["null" => false, "precision" => 9, "scale" => 6]); + $ip->addColumn("price", "decimal", ["null" => false, "precision" => 14, "scale" => 4]); + $ip->addColumn("price_total", "decimal", ["null" => false, "precision" => 14, "scale" => 4]); + $ip->addColumn("price_gross", "decimal", ["null" => false, "precision" => 14, "scale" => 4]); + $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("InvoicePosition")->drop()->save(); + $this->table("Invoice")->drop()->save(); + } + + if($this->getEnvironment() == "addressdb") { + + } + } +} diff --git a/db/migrations/20240625164727_contract_add_vat.php b/db/migrations/20240625164727_contract_add_vat.php new file mode 100644 index 000000000..b41b12b67 --- /dev/null +++ b/db/migrations/20240625164727_contract_add_vat.php @@ -0,0 +1,33 @@ +getEnvironment() == "thetool") { + $table = $this->table("Contract"); + $table->addColumn("vatrate", "decimal", ["null" => false, "default" => 0, "precision" => 6, "scale" => 2, "after" => "price_setup"]); + $table->update(); + } + + if($this->getEnvironment() == "addressdb") { + + } + } + + public function down(): void + { + if($this->getEnvironment() == "thetool") { + $table = $this->table("Contract"); + $table->removeColumn("vatrate"); + $table->update(); + } + + if($this->getEnvironment() == "addressdb") { + + } + } +} diff --git a/db/migrations/20240625171847_worker_permission_add_billing.php b/db/migrations/20240625171847_worker_permission_add_billing.php new file mode 100644 index 000000000..17ddc2661 --- /dev/null +++ b/db/migrations/20240625171847_worker_permission_add_billing.php @@ -0,0 +1,31 @@ +getEnvironment() == "thetool") { + $table = $this->table("WorkerPermission"); + $table->addColumn("canBilling", "enum", ["null" => false,"values" => 'false,true', "default" => "false", "after" => "canOrder"]); + $table->update(); + } + + if($this->getEnvironment() == "addressdb") { + + } + } + + public function down(): void + { + if($this->getEnvironment() == "thetool") { + $this->table("WorkerPermission")->removeColumn("canBilling")->save(); + } + + if($this->getEnvironment() == "addressdb") { + + } + } +} diff --git a/lang/de.php b/lang/de.php index b7fad5bff..5689fb64a 100644 --- a/lang/de.php +++ b/lang/de.php @@ -57,6 +57,11 @@ $l['contract.relocation'] = "Umzug"; $l['contract.productchange'] = "Produktwechsel"; $l['contract.credit'] = "Gutschrift"; +$l["billing_period.0"] = "Einmalig"; +$l["billing_period.1"] = "Monatlich"; +$l["billing_period.12"] = "Jährlich"; +$l["billing_period.24"] = "Zweijährlich"; +$l["billing_period.46"] = "Dreijährlich"; $l['cc.oesterreich'] = "AT"; $l['cc.oestereich'] = "AT"; diff --git a/public/assets/images/xinon-dark.png b/public/assets/images/xinon-dark.png new file mode 100644 index 000000000..7536c11ff Binary files /dev/null and b/public/assets/images/xinon-dark.png differ diff --git a/public/css/sknx_print.css b/public/css/sknx_print.css new file mode 100644 index 000000000..bb06c7a23 --- /dev/null +++ b/public/css/sknx_print.css @@ -0,0 +1,174 @@ +* { + font-family: "Open Sans"; +} + +html { + margin: 24pt; + height:100%; +} + +body { + font-size:9pt; + height:100%; + margin-top: 50pt; +} + +h2 { + font-size:13pt; +} + +#adressen { + width:100%; + border-collapse: collapse; + font-size:10pt; +} +#adressen tr td { + width:40%; + vertical-align: top; +} + +#adressen .left { + padding-top: 136pt; + width:55%; +} +#adressen .right { + width:45%; +} + +.adresse { + border:none; + text-align:left; + line-height: 11pt; +} +.adresse div { + font-size:10pt; +} + +.header_sknx div { + font-size:8pt; + line-height:10pt; + color:#505050; +} + +.header_sknx div b { + font-size:11pt; + line-height:12pt; + color:#000; +} + +table.position { + width:100%; + border-collapse: collapse; + font-size:9pt; +} +table tr, +table tr td { + page-break-inside: avoid !important; + break-inside: avoid !important; +} +table.position tr.head { + border-bottom: 1px solid #000; +} +table.position tr.even { + background-color:#f0f0f0; +} +table.position tr td, +table.position tr th { + text-align:center; + padding-left:4pt; +} +table.position tr td.name, +table.position tr th.name { + text-align:left; + width:50%; +} +table.position tr td.preis, +table.position tr th.preis { + text-align:right; +} +table.position tr td.freitext { + text-align:left; + padding-left:12pt; +} + +.product-name { + font-weight: 600; +} + +#total { + width:100%; + border-collapse: collapse; + font-size:10pt; + margin-bottom: 14pt; + page-break-inside: avoid; +} +#total td, +#total th { + width:50%; +} +#total th { + text-align:left; +} +#total td { + text-align:right; + font-weight:bold; +} +#total tr.ust td, +#total tr.ust th { + font-size: 9pt; + font-weight:normal; +} + +#total tr.netto { + border-top:1px solid #000; +} +#total tr.brutto { + border-top:1px solid #000; + border-bottom:3px double #000; +} +#ausdruck td.beschriftung { + font-size: 11pt; + font-weight: bold; + text-align: left; +} +#ausdruck td.text { + font-size: 10pt; + font-weight: normal; + text-align: left; + +} + +.dontsplit { + page-break-inside:avoid; +} +.dontsplitafter { + page-break-after:avoid; +} +.dontsplitbefore { + page-break-before:avoid; +} + +tbody::after { + content: ''; display: block; + page-break-after: always; + page-break-inside: avoid; + page-break-before: avoid; +} + +.ml-2 { + margin-left: 8pt; +} +.ml-2 { + margin-left: 12pt; +} +.pl-2 { + padding-left: 8pt; +} +.pl-3 { + padding-left: 12pt; +} + +.one-position { + vertical-align: top; + padding-bottom: 4pt; +} diff --git a/scripts/contract/test.php b/scripts/contract/test.php index 2d802ce8b..69e526dfd 100644 --- a/scripts/contract/test.php +++ b/scripts/contract/test.php @@ -101,7 +101,7 @@ $tbms = 0; $tbys = 0; $tbs = 0; -foreach(BillingModel::search(["price>=" => 0]) as $bill) { +foreach(BillingModel::search(["price>=" => 0, "start_date" => "2024-06-01"]) as $bill) { if($bill->billing_period == 1) { $tbms += $bill->price; } @@ -129,7 +129,7 @@ $tbcms = 0; $tbcys = 0; $tbcs = 0; -foreach(BillingModel::search(["price<" => 0]) as $bill) { +foreach(BillingModel::search(["price<" => 0, "start_date" => "2024-06-01"]) as $bill) { if($bill->billing_period == 1) { $tbcms += $bill->price; }