Billing changes

This commit is contained in:
Frank Schubert
2024-08-05 21:09:13 +02:00
parent 75f75b6327
commit a6389f014f
10 changed files with 357 additions and 149 deletions
+160 -96
View File
@@ -88,6 +88,26 @@ class BillingController extends mfBaseController {
$new_filter["invoice_id"] = null;
}
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(array_key_exists("customer", $filter)) {
if(array_key_exists("customer", $filter) && $filter["customer"]) {
$kunde = $this->db()->escape($filter['customer']);
@@ -135,12 +155,13 @@ class BillingController extends mfBaseController {
$now_month = date("m");
$now_day = date("d");
//$now_year = 2024;
//$now_month = 6;
//$now_day = 3; // XXX for debugging only, must be removed
$now_year = 2024;
$now_month = 7;
$now_day = 10; // XXX for debugging only, must be removed
// XXX only for 1st Billing after IVT Import
$yearly_not_before = new DateTime("$now_year-$now_month-01");
// Locking to July 2024 and keeping it for now
$yearly_not_before = new DateTime("2024-07-01");
$del = 0;
// first delete all non-invoiced billing records
@@ -303,15 +324,15 @@ class BillingController extends mfBaseController {
$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) {
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"));
$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->billing_period == 1 && ($start_date->format("Y") < 2024 || ($start_date->format("Y") == 2024 && $start_date->format("m") < 7))) {
if ($contract->billing_period == 1 && ($start_date->format("Y") < 2024 || ($start_date->format("Y") == 2024 && $start_date->format("m") < 7))) {
// 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 before this month");
$this->log->debug(__METHOD__ . ": Ignoring Contract " . $contract->id . " with start_date " . $start_date->format("Y-m-d") . " because is before this month");
continue;
}
@@ -319,7 +340,7 @@ class BillingController extends mfBaseController {
// use cancel date as end_date
if ($cancel_date) {
$end_date = clone $cancel_date;
} elseif(!$end_date) {
} elseif (!$end_date) {
// else calculate last of month
$end_date = clone $start_date;
$end_date->modify("first day of this month");
@@ -346,7 +367,7 @@ class BillingController extends mfBaseController {
$total_days = $last_of_period->diff($first_of_period)->format("%a") + 1;
$period_days = ($end_date->diff($start_date)->format("%a")) + 1;
if($period_days < 0) continue; // don't bill for negative time range
if ($period_days < 0) continue; // don't bill for negative time range
$pc = $period_days / $total_days * 100;
$price = round($contract->price / 100 * $pc, 4);
@@ -377,7 +398,11 @@ class BillingController extends mfBaseController {
$owner = $contract->owner;
$billingaddress = $contract->billingaddress;
if ($contract->billingaddress_id) {
$billingaddress = $contract->billingaddress;
} else {
$billingaddress = $owner;
}
$billing_type = "invoice";
$billing_delivery = "paper";
@@ -396,9 +421,16 @@ class BillingController extends mfBaseController {
$billing_delivery = $billingaddress->billing_delivery;
}
$fibu_account_num = $contract->billingaddress->fibu_account_number;
if(!$fibu_account_num) {
die("Keine Fibu Account Nummer in Rechnungskontakt in Contract ID ".$contract->id);
if($contract->vatgroup_id == TT_VATGROUP_CREDIT) {
$fibu_account_num = $billingaddress->fibu_supplier_number;
if(!$fibu_account_num) {
die("Partner " . $billingaddress->customer_number . " hat keine Lieferantennummer (" . $billingaddress->getCompanyOrName().")");
}
} else {
$fibu_account_num = $billingaddress->fibu_account_number;
if(!$fibu_account_num) {
die("Keine Fibu Account Nummer in Rechnungskontakt in Contract ID ".$contract->id);
}
}
$data = [];
@@ -430,13 +462,12 @@ class BillingController extends mfBaseController {
$data["price"] = $price;
$data["price_setup"] = $price_setup;
$data["billing_period"] = $contract->billing_period;
$data["matchcode"] = $contract->matchcode;
if(!$contract->billingaddress->country_id) {
if(!$billingaddress->country_id) {
$billcountry = CountryModel::getFirst(["isocode" => TT_HOMECOUNTRY_ISOCODE]);
} else {
$billcountry = $contract->billingaddress->country;
$billcountry = $billingaddress->country;
}
$vatgroup = $contract->vatgroup;
@@ -453,14 +484,14 @@ class BillingController extends mfBaseController {
$data["vatarea"] = $vatarea;
if($billing_type == "sepa") {
if($contract->billingaddress->sepa_date) {
$sepa_date = new DateTime("@".$contract->billingaddress->sepa_date);
if($billingaddress->sepa_date) {
$sepa_date = new DateTime("@".$billingaddress->sepa_date);
$sepa_date->setTimezone(new DateTimeZone("Europe/Vienna"));
$data["sepa_date"] = $sepa_date->format("Y-m-d");
}
if($contract->billingaddress->last_invoice_date) {
$sepa_last_date = new DateTime("@".$contract->billingaddress->last_invoice_date);
if($billingaddress->last_invoice_date) {
$sepa_last_date = new DateTime("@".$billingaddress->last_invoice_date);
$sepa_last_date->setTimezone(new DateTimeZone("Europe/Vienna"));
$data["sepa_last_date"] = $sepa_last_date->format("Y-m-d");
}
@@ -482,6 +513,7 @@ class BillingController extends mfBaseController {
/*
* Create Voice Billing, if contract has voicenumbers
*/
$voicenumbers = VoicenumberModel::search(["contract_id" => $contract->id]);
if(count($voicenumbers)) {
@@ -522,82 +554,122 @@ class BillingController extends mfBaseController {
foreach ($voicenumbers as $voicenumber) {
$vbill = BillingVoicenumberModel::getFirst(["contract_id" => $contract->id, "voicenumber" => $voicenumber->number, "start_date" => $voice_start_date->format("Y-m-d")]);
if ($vbill) {
//var_dump($vbill);exit;
continue; // number was already billed in this period
}
$calls = VoiceCallHistoryModel::getVoiceCallHistoryAsEntity(["contract_id" => $contract->id, "voice_account" => $voicenumber->number, "start" => ["from" => $voice_start_date->getTimestamp(), "to" => $voice_end_date->getTimestamp()]]);
foreach ($calls as $call) {
//var_dump($call);
$number = $call->voice_account;
$dest_nummer = $call->destination;
if(substr($dest_nummer, 0, 2) == "00") {
$dest_nummer = substr($dest_nummer, 2);
}
if(substr($dest_nummer, 0, 1) == "+") {
$dest_nummer = substr($dest_nummer, 1);
}
// check for unbilled call records in earlier months
$call_dates = [];
$earlier_voice_start_date = clone $voice_start_date;
if (array_key_exists($dest_nummer, $destinations_cache)) {
$destination = $destinations_cache[$dest_nummer];
} else {
$destination = $voiceplan->getDestinationByNumber($dest_nummer);
if(!$destination) {
die("Destination für Zielrufnummer ".$call->destination." nicht gefunden");
}
$destinations_cache[$dest_nummer] = $destination;
}
//var_dump($destination);
//$historic_call_count = 0;
do {
$earlier_voice_start_date->modify("-1 month");
// not before first billing in thetool
if($earlier_voice_start_date->format("Y-m-d") < "2024-07-01") break;
$zone = $destination->voiceplanzone;
$earlier_voice_end_date = clone $earlier_voice_start_date;
$earlier_voice_end_date->modify("last day of this month");
$earlier_voice_end_date->setTime(23, 59, 59);
if(!$zone) {
die("Keine Zone für Destination ".$dest_nummer." gefunden");
}
//var_dump($zone);
// inc_first - first minimumm duration to bill
// inc - subsequent minimum duration to bill
$inc_first = $zone->increment_first;
$inc = $zone->increment;
$billable_duration = $call->duration;
if($billable_duration <= 0) continue;
// calculate price of first duration unit
// then subtract first minimum duration from duration
$sec_price = $zone->price / 60;
$call_price = $inc_first * $sec_price;
$billable_duration -= $inc_first;
// calculate price of remaining duration and make sure to bill in full duration units
if($billable_duration > 0) {
$multi = ceil($billable_duration / $inc);
$call_price += ($multi * $inc) * $sec_price;
}
if (!array_key_exists($number, $voicebills)) {
$voicebills[$number] = [];
}
if (!array_key_exists($zone->id, $voicebills[$number])) {
$voicebills[$number][$zone->id] = [
"zone_name" => $zone->name,
"voiceplan" => $voiceplan->name,
"duration" => 0,
"price" => $sec_price,
"zone_total" => 0,
"increment_first" => $zone->increment_first,
"increment" => $zone->increment,
"count" => 0
$historic_call_count = VoiceCallHistoryModel::countVoiceCallHistory(["contract_id" => $contract->id, "voice_account" => $voicenumber->number, "start" => ["from" => $earlier_voice_start_date->getTimestamp(), "to" => $earlier_voice_end_date->getTimestamp()]]);
//var_dump($historic_call_count);exit;
if ($historic_call_count) {
$call_dates[] = [
"start_date" => clone $earlier_voice_start_date,
"end_date" => clone $earlier_voice_end_date,
];
}
//var_dump($historic_call_count > 0);exit;
} while ($historic_call_count > 0);
$voicebills[$number][$zone->id]["count"]++;
$voicebills[$number][$zone->id]["zone_total"] += $call_price;
$voicebills[$number][$zone->id]["duration"] += $call->duration;
$call_dates = array_reverse($call_dates);
$call_dates[] = [
"start_date" => clone $voice_start_date,
"end_date" => clone $voice_end_date,
];
if(count($call_dates) > 1) {
var_dump($call_dates);exit;
}
foreach ($call_dates as $call_date) {
$calls = VoiceCallHistoryModel::getVoiceCallHistoryAsEntity(["contract_id" => $contract->id, "voice_account" => $voicenumber->number, "start" => ["from" => $call_date["start_date"]->getTimestamp(), "to" => $call_date["end_date"]->getTimestamp()]]);
foreach ($calls as $call) {
//var_dump($call);
$number = $call->voice_account;
$dest_nummer = $call->destination;
if (substr($dest_nummer, 0, 2) == "00") {
$dest_nummer = substr($dest_nummer, 2);
}
if (substr($dest_nummer, 0, 1) == "+") {
$dest_nummer = substr($dest_nummer, 1);
}
if (array_key_exists($dest_nummer, $destinations_cache)) {
$destination = $destinations_cache[$dest_nummer];
} else {
$destination = $voiceplan->getDestinationByNumber($dest_nummer);
if (!$destination) {
die("Destination für Zielrufnummer " . $call->destination . " nicht gefunden");
}
$destinations_cache[$dest_nummer] = $destination;
}
//var_dump($destination);
$zone = $destination->voiceplanzone;
if (!$zone) {
die("Keine Zone für Destination " . $dest_nummer . " gefunden");
}
//var_dump($zone);
// inc_first - first minimumm duration to bill
// inc - subsequent minimum duration to bill
$inc_first = $zone->increment_first;
$inc = $zone->increment;
$billable_duration = $call->duration;
if ($billable_duration <= 0) continue;
// calculate price of first duration unit
// then subtract first minimum duration from duration
$sec_price = $zone->price / 60;
$call_price = $inc_first * $sec_price;
$billable_duration -= $inc_first;
// calculate price of remaining duration and make sure to bill in full duration units
if ($billable_duration > 0) {
$multi = ceil($billable_duration / $inc);
$call_price += ($multi * $inc) * $sec_price;
}
if (!array_key_exists($number, $voicebills)) {
$voicebills[$number] = [];
}
if (!array_key_exists($zone->id, $voicebills[$number])) {
$voicebills[$number][$zone->id] = [
"zone_name" => $zone->name,
"voiceplan" => $voiceplan->name,
"duration" => 0,
"price" => $sec_price,
"zone_total" => 0,
"increment_first" => $zone->increment_first,
"increment" => $zone->increment,
"count" => 0,
"start_date" => $call_date["start_date"]->format("Y-m-d"),
"end_date" => $call_date["end_date"]->format("Y-m-d"),
];
}
$voicebills[$number][$zone->id]["count"]++;
$voicebills[$number][$zone->id]["zone_total"] += $call_price;
$voicebills[$number][$zone->id]["duration"] += $call->duration;
}
}
}
@@ -609,8 +681,8 @@ class BillingController extends mfBaseController {
$vbdata["billing_id"] = $billing->id;
$vbdata["contract_id"] = $contract->id;
$vbdata["voicenumber"] = $vbnumber;
$vbdata["start_date"] = $voice_start_date->format("Y-m-d");
$vbdata["end_date"] = $voice_end_date->format("Y-m-d");
$vbdata["start_date"] = $vb["start_date"];
$vbdata["end_date"] = $vb["end_date"];
$vbdata["voiceplan"] = $vb["voiceplan"];
$vbdata["zone"] = $vb["zone_name"];
$vbdata["call_count"] = $vb["count"];
@@ -633,14 +705,6 @@ class BillingController extends mfBaseController {
}
/*foreach(VoiceCallHistoryModel::getVoiceCallHistoryAsEntity(["contract_id" => $contract->id, "start" => ["from" => $start_date->getTimestamp()]]) as $call) {
// find BillingVoicenumber record for this call
$vbill = BillingVoicenumberModel::getFirst(["contract_id" => $contract->id, "voicenumber" => $call->voice_account, "start_date" => ]);
if($vbill) {
}
}*/
}