diff --git a/Layout/default/Contractconfig/Form.php b/Layout/default/Contractconfig/Form.php index 77f3dc988..ad575bd7e 100644 --- a/Layout/default/Contractconfig/Form.php +++ b/Layout/default/Contractconfig/Form.php @@ -76,8 +76,23 @@ } $inputid = "itemvalues_".$item->id; $array_count = 0; + ?> - multiple): ?> + + name == "voicenumberblock_voiceplan_id"): ?> + + displayname?>: + + + description?> + + + multiple): ?> getValue() as $item_value): ?> displayname?> (): diff --git a/application/Admin/functions/IvtContractImport.php b/application/Admin/functions/IvtContractImport.php index 7230ab5d0..5bd14a773 100644 --- a/application/Admin/functions/IvtContractImport.php +++ b/application/Admin/functions/IvtContractImport.php @@ -6,6 +6,12 @@ class Admin_IvtContractImport { private $static_ivt_order_match = []; private $no_matchcode = []; private $ownerIdToBillingAddress = []; + private $ivt_to_voiceplan = [ + 1 => 1, // Ivt Standard + 2 => 4, // Ivt Privat Plus + 3 => 3, // Ivt Business Easy + 4 => 1 // ESIT Partner Tarif + ]; public function __construct($request = false) { $this->request = $request; @@ -185,6 +191,7 @@ class Admin_IvtContractImport { $contract_data['billingaddress_id'] = $billingaddress_id; $contract_data['product_id'] = $product->id; $contract_data['product_name'] = $ip->name; + $contract_data['vatgroup_id'] = $product->vatgroup_id; $contract_data['amount'] = 1; $contract_data['price'] = $ivt_product->price; $contract_data['price_setup'] = 0; @@ -853,10 +860,10 @@ class Admin_IvtContractImport { private function addVoipData($ivt_customer_id, $contracts) { //$this->log->debug("in addVoipData(): cid ".$ivt_customer_id); - if($ivt_customer_id == 1376) { + /*if($ivt_customer_id == 1376) { $this->log->debug("Not importing reseller voicenumbers for PROMETHEUS - Markus Paar [1376]"); return true; - } + }*/ $ported_in = []; $voicenumbers = []; @@ -867,7 +874,19 @@ class Admin_IvtContractImport { return false; } $this->log->debug("$ivt_num_count voicenumbers in ivt for cid ".$ivt_customer_id); - foreach(IvtCustomerTelephoneNrModel::search(["cid" => $ivt_customer_id]) as $ivtnum) { + $ivtnumbers = IvtCustomerTelephoneNrModel::search(["cid" => $ivt_customer_id]); + + if($ivt_customer_id == 1840) { + $injecting_ivtnumber = new IvtCustomerTelephoneNr(); + $injecting_ivtnumber->number = "433152255410"; + $ivtnumbers[] = $injecting_ivtnumber; + + $injecting_ivtnumber = new IvtCustomerTelephoneNr(); + $injecting_ivtnumber->number = "433159258910"; + $ivtnumbers[] = $injecting_ivtnumber; + } + + foreach($ivtnumbers as $ivtnum) { $number = preg_replace('/^0043/', '43', $ivtnum->number); if(!$number) continue; @@ -895,22 +914,35 @@ class Admin_IvtContractImport { $voice_contract = false; + // always create new voice contract for special customers + $new_nolink_contract = false; if($ivt_customer_id == 1376) { + $new_nolink_contract = true; + /*echo "is PROMETHEUS number\n"; //if(in_array($number, [43313228451, 43313228406, 43720666572, 43313228400, 4331324890, 43720103806])) { $prometheus_primary_contract = ContractModel::getFirst(["customer_number" => 1376]); - $voice_contract = $this->createVoiceContract($prometheus_primary_contract, true); - } else { - foreach($contracts as $contract) { - if(array_key_exists("needs_number", $contract->product->attributes) && $contract->product->attributes["needs_number"] == 1) { + $voice_contract = $this->createVoiceContract($contracts, true);*/ + } elseif($ivt_customer_id == 1840) { + $new_nolink_contract = true; + /*//if(in_array($number, [433152255410, 433159258910])) { + $lugitsch_primary_contract = ContractModel::getFirst(["customer_number" => 1840]); + $voice_contract = $this->createVoiceContract($contracts, true);*/ + } + + if(!$new_nolink_contract) { + // try finding voice contract, otherwise create it + foreach ($contracts as $contract) { + if (array_key_exists("needs_number", $contract->product->attributes) && $contract->product->attributes["needs_number"] == 1) { $voice_contract = $contract; } } - - if(!$voice_contract) { - $voice_contract = $this->createVoiceContract($contracts); - } } + if(!$voice_contract) { + $voice_contract = $this->createVoiceContract($contracts, $new_nolink_contract); + } + + //var_dump($voice_contract);exit; @@ -971,6 +1003,9 @@ class Admin_IvtContractImport { $confitem->value->set($contract_new_numbers); $confitem->save(); + // import Voiceplan + $this->getVoiceplan($voice_contract); + $voice_contract->matchcode = implode(", ", $contract_matchcode_numbers); $voice_contract->save(); @@ -980,6 +1015,31 @@ class Admin_IvtContractImport { return true; } + /** + * @var Contract $contract + */ + private function getVoiceplan($contract) { + $cid = $contract->owner->customer_number; + $ivt_customer = new IvtCustomer($cid); + if(!$ivt_customer) { + die(__METHOD__.": Ivt Customer nicht gefunden\n"); + } + + $ivt_plan = $ivt_customer->telephony_pricelist; + if(!array_key_exists($ivt_plan, $this->ivt_to_voiceplan)) { + die("Invalid Ivt Voiceplan: ".$ivt_plan."\n"); + } + + $new_plan = $this->ivt_to_voiceplan[$ivt_plan]; + $contract->setConfigValue("voicenumberblock_voiceplan_id", $new_plan); + /*$voiceplan_item = $contract->getConfigValue("voicenumberblock_voiceplan_id"); + $voiceplan_item->set($new_plan); + $voiceplan_item->save(); + */ + return true; + + } + private function createVoiceContract($contracts, $nolink = false) { // find rufnummer only product (residential or business) $product = new Product(101); // Telefonie (nur Rufnummer - Privat) @@ -1010,6 +1070,7 @@ class Admin_IvtContractImport { $data["price_setup"] = $product->price_setup; $data["price_nne"] = $product->price_nne; $data["price_nbe"] = $product->price_nbe; + $data["vatgroup_id"] = $product->vatgroup_id; $data["billing_delay"] = $product->billing_delay; $data["billing_period"] = $product->billing_period; $data["order_date"] = $fc->order_date; diff --git a/application/Admin/functions/IvtCreditImport.php b/application/Admin/functions/IvtCreditImport.php index d90d05fad..e636f31eb 100644 --- a/application/Admin/functions/IvtCreditImport.php +++ b/application/Admin/functions/IvtCreditImport.php @@ -154,6 +154,7 @@ class Admin_IvtCreditImport { $data["billing_period"] = $primary_contract->billing_period; $data["contract_term"] = $primary_contract->contract_term; $data["order_date"] = $primary_contract->order_date; + $data["vatgroup_id"] = $primary_contract->product->vatgroup_id; $data["finish_date"] = $primary_contract->finish_date; $data["finish_date_by"] = $primary_contract->finish_date_by; @@ -190,6 +191,7 @@ class Admin_IvtCreditImport { $data["sla_id"] = 4; $data["product_external"] = 0; $data["product_external_id"] = null; + $data["vatgroup_id"] = 1; $data["billing_delay"] = 0; $data["billing_period"] = 1; $data["contract_term"] = 12; diff --git a/application/Billing/BillingController.php b/application/Billing/BillingController.php index 6321227c5..19fac8430 100644 --- a/application/Billing/BillingController.php +++ b/application/Billing/BillingController.php @@ -101,6 +101,7 @@ class BillingController extends mfBaseController { //$tomorrow->setTime(0,0,0); $i = 0; + $v = 0; //$yearly_not_before = new DateTime("2023-06-01"); @@ -160,10 +161,6 @@ class BillingController extends mfBaseController { $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; - } $cancel_date = false; if($contract->cancel_date) { @@ -295,6 +292,10 @@ class BillingController extends mfBaseController { } + /*if($contract->price != 0 || $contract->price_setup != 0) { + $this->log->debug(__METHOD__.": Ignoring Contract ".$contract->id." because price and price_setup == 0"); + continue; + }*/ $sday = $start_date->format("d"); $eday = $end_date->format("d"); @@ -369,7 +370,6 @@ 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->matchcode; $data["product_id"] = $contract->product_id; $data["product_name"] = $contract->product_name; $data["product_info"] = $contract->product_info; @@ -378,6 +378,13 @@ class BillingController extends mfBaseController { $data["price_setup"] = $price_setup; $data["billing_period"] = $contract->billing_period; + $matchcode = $contract->matchcode; + // if voice product and matchcode consists oh phonenumbers only, remove matchcode + if(array_key_exists("needs_number", $contract->product->attributes) && $contract->product->attributes["needs_number"] == 1 && preg_match('/^[0-9, ]+$]/', $matchcode)) { + $matchcode = ""; + } + $data["matchcode"] = $matchcode; + if(!$contract->billingaddress->country_id) { $billcountry = CountryModel::getFirst(["isocode" => TT_HOMECOUNTRY_ISOCODE]); } else { @@ -405,10 +412,170 @@ class BillingController extends mfBaseController { } $i++; + + /* + * Create Voice Billing, if contract has voicenumbers + */ + + + $voicenumbers = VoicenumberModel::search(["contract_id" => $contract->id]); + + + + if(count($voicenumbers)) { + //var_dump($voicenumbers);exit; + $voice_start_date = clone $start_date; + $voice_start_date->modify("-1 month"); + $voice_start_date->setTime(0,0,0); + + $voice_end_date = clone $voice_start_date; + $voice_end_date->modify("first day of this month"); + $voice_end_date->modify("+1 months"); + $voice_end_date->modify("-1 day"); + $voice_end_date->setTime(23,59,59); + $this->log->debug("Voice End Date: ".$voice_end_date->format("Y-m-d H:i:s")); + $earliest_start_date = $start_date; + + $voicebills = []; + $zones = []; + $destinations_cache = []; + + $voiceplan_id = $contract->getConfigValue("voicenumberblock_voiceplan_id")->int; + + if (!$voiceplan_id) { + $this->log->debug(__METHOD__ . ": No voiceplan_id in Contract " . $contract->id. ". Numbers: ".count($voicenumbers)); + continue; + } + $voiceplan = new Voiceplan($voiceplan_id); + + + // always look for whole month + // numbers usually don't change owner without at least a few months being stale + if($voice_start_date->format("d") > 1) { + $voice_start_date->modify("first day of this month"); + } + + 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, "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 (array_key_exists($dest_nummer, $destinations_cache)) { + $destination = $destinations_cache[$dest_nummer]; + } else { + $destination = $voiceplan->getDestinationByNumber($dest_nummer); + $destinations_cache[$dest_nummer] = $destination; + } + //var_dump($destination); + + $zone = $destination->voiceplanzone; + + //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 + ]; + } + + $voicebills[$number][$zone->id]["count"]++; + $voicebills[$number][$zone->id]["zone_total"] += $call_price; + $voicebills[$number][$zone->id]["duration"] += $call->duration; + } + if(!count($voicebills)) { + continue; + } + //var_dump($voicebills);exit; + + foreach($voicebills as $vbnumber => $zones) { + foreach($zones as $zone_id => $vb) { + $vbdata = []; + $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["voiceplan"] = $vb["voiceplan"]; + $vbdata["zone"] = $vb["zone_name"]; + $vbdata["call_count"] = $vb["count"]; + $vbdata["duration"] = $vb["duration"]; + $vbdata["price"] = $vb["price"]; + $vbdata["price_total"] = $vb["zone_total"]; + $vbdata["increment"] = $vb["increment"]; + $vbdata["increment_first"] = $vb["increment_first"]; + + $bill_voice = BillingVoicenumberModel::create($vbdata); + if(!$bill_voice->save()) { + var_dump($vbdata); + die("Error saving Billing Voicenumber!"); + } + } + } + $v++; + + // save to BillingVoicenumber + + + + + } + + } + + + /*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) { + + } + }*/ } + + + + } - $this->layout()->setFlash("$i Billing records generiert"); + $this->layout()->setFlash("$i Contract Billing records generiert. $v Voicenumber Billing records generiert"); $this->redirect("Billing"); } diff --git a/application/BillingVoicenumber/BillingVoicenumberModel.php b/application/BillingVoicenumber/BillingVoicenumberModel.php index ccbc226f2..0d155f480 100644 --- a/application/BillingVoicenumber/BillingVoicenumberModel.php +++ b/application/BillingVoicenumber/BillingVoicenumberModel.php @@ -1,10 +1,21 @@ select("BillingVoicenumber", "*", "1 = 1 ORDER BY BillingVoicenumberaddress_id"); + $res = $db->select("BillingVoicenumber", "*", "1 = 1 ORDER BY id"); if($db->num_rows($res)) { while($data = $db->fetch_object($res)) { $items[] = new BillingVoicenumber($data); @@ -55,7 +66,7 @@ class BillingVoicenumberModel { $where = self::getSqlFilter($filter); $sql = "SELECT * FROM BillingVoicenumber WHERE $where - ORDER BY BillingVoicenumberaddress_id LIMIT 1"; + ORDER BY id LIMIT 1"; //var_dump($sql);exit; $res = $db->query($sql); if($db->num_rows($res)) { @@ -91,31 +102,6 @@ class BillingVoicenumberModel { return null; } - public static function getInvoiceBaseData($filter) { - $db = FronkDB::singleton(); - - $items = []; - - $where = self::getSqlFilter($filter); - $sql = "SELECT owner_id, BillingVoicenumberaddress_id, BillingVoicenumber_type, BillingVoicenumber_delivery FROM BillingVoicenumber - WHERE $where - GROUP BY owner_id, BillingVoicenumberaddress_id, BillingVoicenumber_type, BillingVoicenumber_delivery - ORDER BY owner_id, BillingVoicenumberaddress_id, BillingVoicenumber_type, BillingVoicenumber_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, - "BillingVoicenumberaddress_id" => $data->BillingVoicenumberaddress_id, - "BillingVoicenumber_type" => $data->BillingVoicenumber_type, - "BillingVoicenumber_delivery" => $data->BillingVoicenumber_delivery - ]; - } - } - return $items; - } - public static function count($filter) { $db = FronkDB::singleton(); @@ -191,14 +177,15 @@ class BillingVoicenumberModel { } } - if(array_key_exists("invoice_id", $filter)) { - $invoice_id = $filter['invoice_id']; - if(is_numeric($invoice_id)) { - $where .= " AND BillingVoicenumber.invoice_id=$invoice_id"; - } elseif($invoice_id === null || $invoice_id === false) { - $where .= " AND (BillingVoicenumber.invoice_id IS NULL OR BillingVoicenumber.invoice_id=0)"; + if(array_key_exists("billing_id", $filter)) { + $billing_id = $filter['billing_id']; + if(is_numeric($billing_id)) { + $where .= " AND BillingVoicenumber.billing_id=$billing_id"; + } elseif($billing_id === null || $billing_id === false) { + $where .= " AND (BillingVoicenumber.billing_id IS NULL OR BillingVoicenumber.billing_id=0)"; } } + if(array_key_exists("contract_id", $filter)) { $contract_id = $filter['contract_id']; @@ -207,6 +194,14 @@ class BillingVoicenumberModel { } } + if(array_key_exists("voicenumber", $filter)) { + $voicenumber = FronkDB::singleton()->escape($filter['voicenumber']); + if($voicenumber) { + $where .= " AND BillingVoicenumber.voicenumber='$voicenumber'"; + } + } + + if(array_key_exists("start_date", $filter)) { $start_date = FronkDB::singleton()->escape($filter['start_date']); if($start_date) { @@ -249,136 +244,8 @@ class BillingVoicenumberModel { } } - if(array_key_exists("BillingVoicenumberaddress_id", $filter)) { - $BillingVoicenumberaddress_id = $filter['BillingVoicenumberaddress_id']; - if(is_numeric($BillingVoicenumberaddress_id)) { - $where .= " AND BillingVoicenumber.BillingVoicenumberaddress_id=$BillingVoicenumberaddress_id"; - } - } - if(array_key_exists("customer_number", $filter)) { - $customer_number = $filter['customer_number']; - if(is_numeric($customer_number)) { - $where .= " AND BillingVoicenumber.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("matchcode", $filter)) { - $matchcode = FronkDB::singleton()->escape($filter["matchcode"]); - if($matchcode) { - $where .= " AND matchcode like '%$matchcode%'"; - } - } - - if(array_key_exists("product_id", $filter)) { - $product_id = $filter['product_id']; - if(is_numeric($product_id)) { - $where .= " AND BillingVoicenumber.product_id=$product_id"; - } - } - - if(array_key_exists("product_name", $filter)) { - $product_name = $db->escape($filter['product_name']); - if($product_name) { - $where .= " AND product_name like '%$product_name%')"; - } - } - - if(array_key_exists("matchcode", $filter)) { - $matchcode = $db->escape($filter['matchcode']); - if($matchcode) { - $where .= " AND BillingVoicenumber.`matchcode` like '%$matchcode%'"; - } - } - - if(array_key_exists("price<", $filter)) { - $price = $filter['price<']; - if(is_numeric($price)) { - $where .= " AND BillingVoicenumber.price < $price"; - } - } - if(array_key_exists("price<=", $filter)) { - $price = $filter['price<=']; - if(is_numeric($price)) { - $where .= " AND BillingVoicenumber.price <= $price"; - } - } - - if(array_key_exists("price>", $filter)) { - $price = $filter['price>']; - if(is_numeric($price)) { - $where .= " AND BillingVoicenumber.price > $price"; - } - } - if(array_key_exists("price>=", $filter)) { - $price = $filter['price>=']; - if(is_numeric($price)) { - $where .= " AND BillingVoicenumber.price >= $price"; - } - } if(array_key_exists("add-where", $filter)) { $where .= " ".$filter['add-where']; diff --git a/application/Contract/Contract.php b/application/Contract/Contract.php index 9736f4053..2d0eb89ad 100644 --- a/application/Contract/Contract.php +++ b/application/Contract/Contract.php @@ -200,8 +200,20 @@ class Contract extends mfBaseModel { if(!$configvalues) return null; if(!array_key_exists($itemname, $configvalues)) return null; - $confitem = $configvalues[$itemname]; - return $confitem->value; + $configitem = $configvalues[$itemname]; + return $configitem->value; + } + + public function setConfigValue($itemname, $value) { + $configvalues = $this->getProperty("configvalues"); + if(!$configvalues) return null; + if(!array_key_exists($itemname, $configvalues)) return null; + + $configitem = $configvalues[$itemname]; + + $configitem->value->set($value); + $configitem->value->save(); + return true; } public function getProperty($name) { diff --git a/application/Contract/ContractModel.php b/application/Contract/ContractModel.php index 300fd5ef1..3862ba2e5 100644 --- a/application/Contract/ContractModel.php +++ b/application/Contract/ContractModel.php @@ -15,7 +15,7 @@ class ContractModel { public $product_external_id; public $price = null; public $price_setup = null; - public $vatrate = null; + public $vatgroup_id = null; public $price_nne = null; public $price_nbe = null; public $billing_delay = null; diff --git a/application/VoiceCallHistory/VoiceCallHistoryController.php b/application/VoiceCallHistory/VoiceCallHistoryController.php index 96d38c093..5669c6199 100644 --- a/application/VoiceCallHistory/VoiceCallHistoryController.php +++ b/application/VoiceCallHistory/VoiceCallHistoryController.php @@ -108,15 +108,23 @@ class VoiceCallHistoryController extends mfBaseController { } public function addContractIds() { + $ignore_numbers = [ + "4331641220846", + "43623237705", + "4367761737195" + ]; $unknown_numbers = []; // get calls without contract id - foreach(VoiceCallHistoryModel::getVoiceCallHistoryAsEntity(["contract_id" => null, "billable" => "1", "duration" => ["from" => 1], "end" => "2024-06-01"]) as $call) { + foreach(VoiceCallHistoryModel::getVoiceCallHistoryAsEntity(["contract_id" => null, "billable" => "1", "duration" => ["from" => 1]]) as $call) { //var_dump($call);exit; //echo "\n"; $number = $call->voice_account; if(!$number) continue; + // server side failed/disconnected calls + if($call->duration == 1 && $call->state == 38) continue; + if(in_array($number, $unknown_numbers)) continue; $voicenumber = VoicenumberModel::getFirst(["number" => $number]); @@ -132,12 +140,24 @@ class VoiceCallHistoryController extends mfBaseController { continue; } + $contract = new Contract($voicenumber->contract_id); + if(!$contract) { + $this->log->debug(__METHOD__.": No Contract with Contract_ID ".$voicenumber->contract_id." in Voicenumber ".$voicenumber->number); + continue; + } + + if($contract->isCancelled()) { + // mail an office + $this->log->warning(__METHOD__.": Contract ".$voicenumber->contract_id." for Voicenumber ".$voicenumber->number." is cancelled!"); + } + + $calldate = new DateTime($call->start); $calldate->setTimezone(new DateTimeZone("Europe/Vienna")); - $contract = $voicenumber->contract; + // calls from before first IVT Import must be right, we don't have historic contract data diff --git a/application/VoiceCallHistory/VoiceCallHistoryModel.php b/application/VoiceCallHistory/VoiceCallHistoryModel.php index d5c48caee..399e33a99 100644 --- a/application/VoiceCallHistory/VoiceCallHistoryModel.php +++ b/application/VoiceCallHistory/VoiceCallHistoryModel.php @@ -110,7 +110,7 @@ class VoiceCallHistoryModel { $sql .= isset($filters['destination']) ? Helper::generateFilterCondition($filters['destination'], "destination") : ""; $sql .= isset($filters['billable']) ? Helper::generateFilterCondition($filters['billable'], "billable") : ""; $sql .= isset($filters['duration']) ? Helper::generateFilterCondition($filters['duration'], "duration") : ""; - $sql .= array_key_exists("contract_id", $filters) ? Helper::generateFilterCondition($filters['contract_id'], "contract_id") : ""; + $sql .= array_key_exists("contract_id", $filters) ? Helper::generateFilterCondition($filters['contract_id'], "contract_id", true) : ""; return $sql; } @@ -138,7 +138,7 @@ class VoiceCallHistoryModel { $sql .= $order === null || $order['key'] === null ? " ORDER BY `start` DESC" : " ORDER BY `" . $order['key'] . "` " . $order['order']; $sql .= $limit === null ? "" : " LIMIT " . $limit . " OFFSET " . $offset; - //mfLoghandler::singleton()->debug($sql);exit; + mfLoghandler::singleton()->debug($sql); // die($sql); $result = $db->query($sql); $rows = []; diff --git a/application/Voicenumber/VoicenumberModel.php b/application/Voicenumber/VoicenumberModel.php index 97555b4e3..28da70fb8 100644 --- a/application/Voicenumber/VoicenumberModel.php +++ b/application/Voicenumber/VoicenumberModel.php @@ -109,7 +109,7 @@ class VoicenumberModel { if(is_array($limit) && count($limit)) { if(is_numeric($limit['start']) && is_numeric($limit['count'])) { $sql .= " LIMIT ".$limit['start'].", ".$limit['count']; - } elseif(is_numeric($count)) { + } elseif(is_numeric($limit['count'])) { $sql .= " LIMIT ".$limit['count']; } } @@ -144,6 +144,13 @@ class VoicenumberModel { $where .= " AND voicenumberblock_id = $block_id"; } } + + if(array_key_exists("contract_id", $filter)) { + $contract_id = $filter['contract_id']; + if(is_numeric($contract_id)) { + $where .= " AND contract_id = $contract_id"; + } + } //var_dump($filter);exit; if(array_key_exists("countrycode", $filter)) { diff --git a/application/Voiceplan/Voiceplan.php b/application/Voiceplan/Voiceplan.php index 7b8cd0f27..7da3fd94b 100644 --- a/application/Voiceplan/Voiceplan.php +++ b/application/Voiceplan/Voiceplan.php @@ -8,7 +8,36 @@ class Voiceplan extends mfBaseModel { private $destinations; public $import_errors; - + + + public function getDestinationByNumber($number) { + if(!$number) return false; + + $prefix = $number; + + while(strlen($prefix)) { + $destination = VoiceplandestinationModel::getFirst(["prefix" => $prefix, "voiceplan_id" => $this->id]); + if($destination) { + break; + } + $prefix = substr($prefix, 0, strlen($prefix) - 1); + } + + if(!$destination) return false; + + return $destination; + } + public function getZoneByNumber($number) { + if(!$number) return false; + + $destination = $this->getDestinationByNumber($number); + if(!$destination) return false; + + return $destination->voiceplanzone; + + + } + public function importDestinationsFromCsv(File $file) { if(!$this->id) { return false; @@ -169,7 +198,7 @@ class Voiceplan extends mfBaseModel { return true; } - + public function getProperty($name) { if($this->$name == null) { diff --git a/application/Voiceplandestination/Voiceplandestination.php b/application/Voiceplandestination/Voiceplandestination.php index d140c6367..6bf20ae74 100644 --- a/application/Voiceplandestination/Voiceplandestination.php +++ b/application/Voiceplandestination/Voiceplandestination.php @@ -23,16 +23,20 @@ class Voiceplandestination extends mfBaseModel { } return $this->voiceplan; } - - $classname = ucfirst($name); - $idfield = $name."_id"; - $this->$name = new $classname($this->$idfield); - - if($this->$name->id) { - return $this->$name; - } else { - return null; - } + + $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; diff --git a/application/Voiceplandestination/VoiceplandestinationModel.php b/application/Voiceplandestination/VoiceplandestinationModel.php index fe9b88164..c1f5f0b47 100644 --- a/application/Voiceplandestination/VoiceplandestinationModel.php +++ b/application/Voiceplandestination/VoiceplandestinationModel.php @@ -52,8 +52,16 @@ class VoiceplandestinationModel { $db = FronkDB::singleton(); $where = self::getSqlFilter($filter); - mfLoghandler::singleton()->debug($where); - $res = $db->select("Voiceplandestination", "*", "$where ORDER BY destination,prefix"); + + $sql = "SELECT * FROM Voiceplandestination WHERE $where ORDER BY destination,prefix"; + $sql = "SELECT Voiceplandestination.* FROM Voiceplandestination + LEFT JOIN Voiceplanzone ON (Voiceplanzone.id = Voiceplandestination.voiceplanzone_id) + WHERE $where + ORDER BY destination,prefix LIMIT 1 + "; + mfLoghandler::singleton()->debug($sql); + $res = $db->query($sql); + //$res = $db->select("Voiceplandestination", "*", "$where ORDER BY destination,prefix"); if($db->num_rows($res)) { $data = $db->fetch_object($res); $item = new Voiceplandestination($data); @@ -99,7 +107,7 @@ class VoiceplandestinationModel { if(is_array($limit) && count($limit)) { if(is_numeric($limit['start']) && is_numeric($limit['count'])) { $sql .= " LIMIT ".$limit['start'].", ".$limit['count']; - } elseif(is_numeric($count)) { + } elseif(is_numeric($limit['count'])) { $sql .= " LIMIT ".$limit['count']; } } diff --git a/application/Voiceplanzone/Voiceplanzone.php b/application/Voiceplanzone/Voiceplanzone.php index 193cfc8da..4c209d701 100644 --- a/application/Voiceplanzone/Voiceplanzone.php +++ b/application/Voiceplanzone/Voiceplanzone.php @@ -25,16 +25,20 @@ class Voiceplanzone extends mfBaseModel { $this->destinations = VoiceplandestinationModel::search(["voiceplanzone_id" => $this->id]); return $this->destinations; } - - $classname = ucfirst($name); - $idfield = $name."_id"; - $this->$name = new $classname($this->$idfield); - - if($this->$name->id) { - return $this->$name; - } else { - return null; - } + + $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; diff --git a/db/migrations/20240620160026_create_billing.php b/db/migrations/20240620160026_create_billing.php index 0fdaba2c1..5db6ebdf8 100644 --- a/db/migrations/20240620160026_create_billing.php +++ b/db/migrations/20240620160026_create_billing.php @@ -38,7 +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("vatrate", "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/20240704170016_create_billing_voicenumber.php b/db/migrations/20240704170016_create_billing_voicenumber.php index 5d2c78e11..19e9a17e1 100644 --- a/db/migrations/20240704170016_create_billing_voicenumber.php +++ b/db/migrations/20240704170016_create_billing_voicenumber.php @@ -9,18 +9,19 @@ final class CreateBillingVoicenumber extends AbstractMigration { if($this->getEnvironment() == "thetool") { $table = $this->table("BillingVoicenumber"); - $table->addColumn("invoice_id", "integer", ["null" => true, "default" => null]); + $table->addColumn("billing_id", "integer", ["null" => true, "default" => null]); $table->addColumn("contract_id", "integer", ["null" => false]); - $table->addColumn("owner_id", "integer", ["null" => false]); - $table->addColumn("billingaddress_id", "integer", ["null" => false]); $table->addColumn("voicenumber", "string", ["null" => false, "limit" => 64]); $table->addColumn("start_date", "date", ["null" => false]); $table->addColumn("end_date", "date", ["null" => false]); + $table->addColumn("voiceplan", "string", ["null" => false, "limit" => 255]); $table->addColumn("zone", "string", ["null" => false, "limit" => 64]); + $table->addColumn("call_count", "integer", ["null" => false]); $table->addColumn("duration", "integer", ["null" => false]); - $table->addColumn("price", "decimal", ["null" => false, "precision" => 14, "scale" => 4]); - $table->addColumn("total_vat", "decimal", ["null" => false, "precision" => 14, "scale" => 4]); - + $table->addColumn("price", "decimal", ["null" => false, "precision" => 14, "scale" => 8]); + $table->addColumn("price_total", "decimal", ["null" => false, "precision" => 14, "scale" => 4]); + $table->addColumn("increment", "integer", ["null" => false]); + $table->addColumn("increment_first", "integer", ["null" => false]); $table->addColumn("create_by", "integer", ["null" => false]); $table->addColumn("edit_by", "integer", ["null" => false]); $table->addColumn("create", "integer", ["null" => false]);