public function create_billing(int $decimals, int $contract_type, int $billing_term, int $year, int $month, $title, int $created_by, array $contracts_to_bill, array $contracts_overriding_billing_start, string $export_format, int $existing_billing, array $contracts_bill_only_one_time)
 {
     if ($contracts_overriding_billing_start == null) {
         $contracts_overriding_billing_start = array();
     }
     if ($contracts_bill_only_one_time == null) {
         $contracts_bill_only_one_time = array();
     }
     // We start a transaction before running the billing
     $this->db->transaction_begin();
     if ($existing_billing < 1) {
         //new billing
         $billing = new rental_billing(-1, $contract_type, $title, $created_by);
         // The billing job itself
         $billing->set_timestamp_start(time());
         // Start of run
         $billing->set_export_format($export_format);
         $billing->set_title($title);
         $this->store($billing);
         // Store job as it is
         $billing_end_timestamp = strtotime('-1 day', strtotime(($month == 12 ? $year + 1 : $year) . '-' . ($month == 12 ? '01' : $month + 1) . '-01'));
         // Last day of billing period is the last day of the month we're billing
         $counter = 0;
         $total_sum = 0;
     } else {
         $billing = $this->get_single($existing_billing);
         $billing_end_timestamp = strtotime('-1 day', strtotime(($month == 12 ? $year + 1 : $year) . '-' . ($month == 12 ? '01' : $month + 1) . '-01'));
         // Last day of billing period is the last day of the month we're billing
         $total_sum = $billing->get_total_sum();
     }
     $billing_info = new rental_billing_info(null, $billing->get_id(), $contract_type, $billing_term, $year, $month);
     $res = rental_sobilling_info::get_instance()->store($billing_info);
     // Get the number of months in selected term for contract
     $months = rental_socontract::get_instance()->get_months_in_term($billing_term);
     // The billing should start from the first date of the periode (term) we're billing for
     $first_day_of_selected_month = strtotime($year . '-' . $month . '-01');
     $bill_from_timestamp = strtotime('-' . ($months - 1) . ' month', $first_day_of_selected_month);
     foreach ($contracts_to_bill as $contract_id) {
         $invoice = rental_invoice::create_invoice($decimals, $billing->get_id(), $contract_id, in_array($contract_id, $contracts_overriding_billing_start) ? true : false, $bill_from_timestamp, $billing_end_timestamp, in_array($contract_id, $contracts_bill_only_one_time) ? true : false, false, $billing_term);
         // Creates an invoice of the contract
         if ($invoice != null) {
             $total_sum += $invoice->get_total_sum();
         }
     }
     $billing->set_total_sum(round($total_sum, $decimals));
     $billing->set_timestamp_stop(time());
     //  End of run
     $billing->set_success(true);
     // Billing job is a success
     $this->store($billing);
     // Store job now that we're done
     // End of transaction!
     if ($this->db->transaction_commit()) {
         return $billing;
     }
     throw new UnexpectedValueException('Transaction failed.');
 }
 /**
  * Create invoice
  * 
  * @param int $decimals	the number of decimals on the total sum of the onvoice
  * @param int $billing_id	the billing this invoice is part of
  * @param int $contract_id	the contract
  * @param bool $override	flag to indicate if the invoice start period should be overridden with the billing start date of contract
  * @param int $timestamp_invoice_start	the startdate of the invoice period
  * @param int $timestamp_invoice_end	the enddate of the invoice period
  * @param bool $bill_only_one_time	flag to indicate if the the invoice should only bil one time price elements
  * @return rental_invoice	the newly created invoice
  */
 public static function create_invoice(int $decimals, int $billing_id, int $contract_id, bool $override, int $timestamp_invoice_start, int $timestamp_invoice_end, $bill_only_one_time, $dry_run = false, $billing_term = 0)
 {
     $contract = rental_socontract::get_instance()->get_single($contract_id);
     // If the invoice period should be overriden with the biling start date
     if ($override) {
         $timestamp_invoice_start = $contract->get_billing_start_date();
     }
     // If no account out is specified: check if the contract type defines any data to be used in this field (AGRESSO specific logic)
     $account_out = $contract->get_account_out();
     if (!isset($account_out) || $account_out == '') {
         //If no account out - check the contract type for default
         $account_tmp = rental_socontract::get_instance()->get_contract_type_account($contract->get_contract_type_id());
         if (isset($account_tmp) && $account_tmp != '') {
             $account_out = $account_tmp;
         } else {
             $account_out = rental_socontract::get_instance()->get_default_account($contract->get_location_id(), false);
         }
     }
     // Create invoice ...
     $invoice = new rental_invoice(-1, $billing_id, $contract_id, time(), $timestamp_invoice_start, $timestamp_invoice_end, 0, $contract->get_rented_area(), $contract->get_invoice_header(), $contract->get_account_in(), $account_out, $contract->get_service_id(), $contract->get_responsibility_id());
     // ... and add party identifier, project number and the old contract identifier
     $invoice->set_party_id($contract->get_payer_id());
     $invoice->set_project_id($contract->get_project_id());
     $invoice->set_old_contract_id($contract->get_old_contract_id());
     if (!$dry_run) {
         rental_soinvoice::get_instance()->store($invoice);
         // We must store the invoice at this point to have an id to give to the price item
     }
     // Retrieve the contract price items: only one-time or all
     if ($bill_only_one_time) {
         $filters2 = array('contract_id' => $contract->get_id(), 'contract_ids_one_time' => true, 'billing_term_id' => $billing_term, 'year' => date('Y', $timestamp_invoice_start), 'month' => date('m', $timestamp_invoice_start));
         //$contract_price_items = $socontract_price_item->get($start_index, $num_of_objects, $sort_field, $sort_ascending, $search_for, $search_type, $filters2);
         $contract_price_items = rental_socontract_price_item::get_instance()->get(null, null, null, null, null, null, $filters2);
         //$contract_price_items = rental_socontract_price_item::get_instance()->get(null, null, null, null, null, null, array('contract_id' => $contract->get_id(), 'one_time' => true));
     } else {
         $contract_price_items = rental_socontract_price_item::get_instance()->get(null, null, null, null, null, null, array('contract_id' => $contract->get_id()));
     }
     $total_sum = 0;
     // Holding the total price of the invoice
     $contract_dates = $contract->get_contract_date();
     if (isset($contract_dates)) {
         $contract_start = $contract->get_contract_date()->get_start_date();
         $contract_end = $contract->get_contract_date()->get_end_date();
     }
     // Run through the contract price items
     foreach ($contract_price_items as $contract_price_item) {
         // ---- Period calculation ---
         // Determine start date for price item
         $contract_price_item_start = $contract_price_item->get_date_start();
         if ($contract_price_item_start == null || $contract_price_item_start == '') {
             // We just use the invoice date for our calculations
             $contract_price_item_start = $timestamp_invoice_start;
         }
         // Determine end date for price item
         $contract_price_item_end = $contract_price_item->get_date_end();
         if ($contract_price_item_end == null || $contract_price_item_end == '') {
             // We just use the invoice date for our calculations
             $contract_price_item_end = $timestamp_invoice_end;
         }
         // Sanity check - end date should never be before start date
         if ($contract_price_item_end < $contract_price_item_start) {
             continue;
             // We don't add this price item - continue to next
         }
         // Checking the start date against the invoice dates
         if ($contract_price_item_start < $timestamp_invoice_start) {
             $invoice_price_item_start = $timestamp_invoice_start;
             // We use the invoice start
         } else {
             if ($contract_price_item_start > $timestamp_invoice_end) {
                 continue;
                 // We don't add this price item - continue to next
             } else {
                 $invoice_price_item_start = $contract_price_item_start;
                 // We use the price item start
             }
         }
         // Checking the end date against invoice dates
         if ($contract_price_item_end < $timestamp_invoice_start) {
             continue;
             // We don't add this price item - continue to next
         } else {
             if ($contract_price_item_end < $timestamp_invoice_end) {
                 $invoice_price_item_end = $contract_price_item_end;
                 // We use the price item end
             } else {
                 $invoice_price_item_end = $timestamp_invoice_end;
                 // We use the invoice end
             }
         }
         // Checking the contract dates against the temporary price item dates
         if (isset($contract_start) && !$contract_price_item->is_one_time()) {
             if ($contract_start > $timestamp_invoice_end) {
                 continue;
                 //No price items for this contract will be billed
             }
             if ($contract_start > $invoice_price_item_start) {
                 $invoice_price_item_start = $contract_start;
             }
         }
         if (isset($contract_end) && !$contract_price_item->is_one_time()) {
             if ($contract_end < $timestamp_invoice_start) {
                 continue;
                 //No price items for this contract will be billed
             }
             if ($contract_end < $invoice_price_item_end) {
                 $invoice_price_item_end = $contract_end;
             }
         }
         // --- End of period calculation ---
         // Create a new invoice price item
         $invoice_price_item = new rental_invoice_price_item($decimals, -1, $invoice->get_id(), $contract_price_item->get_title(), $contract_price_item->get_agresso_id(), $contract_price_item->is_area(), $contract_price_item->get_price(), $contract_price_item->get_area(), $contract_price_item->get_count(), $invoice_price_item_start, $invoice_price_item_end);
         // If the contract price item is of type one-time and it's dates are within the invoice period ...
         if ($contract_price_item->is_one_time()) {
             if ($contract_price_item_start >= $timestamp_invoice_start && $contract_price_item_start <= $timestamp_invoice_end) {
                 // ... set the total price of the invoice price item to the total price of the contract price item
                 $invoice_price_item->set_total_price($contract_price_item->get_total_price());
                 // ... and set the contract price item as billed
                 $contract_price_item->set_is_billed(true);
                 if (!$dry_run) {
                     rental_socontract_price_item::get_instance()->store($contract_price_item);
                 }
             }
         }
         if (!$dry_run) {
             // Store the invoice price item
             rental_soinvoice_price_item::get_instance()->store($invoice_price_item);
         }
         // Add the price item to the invoice
         $invoice->add_invoice_price_item($invoice_price_item);
         // Add this price item's total sum to the tota sum of the invoice
         $total_sum += $invoice_price_item->get_total_price();
     }
     // end of looping through the contract price items
     // Set the total sum of the invoice rounded to the specified number of decimals
     $invoice->set_total_sum(round($total_sum, $decimals));
     if (!$dry_run) {
         // ... and store the invoice
         rental_soinvoice::get_instance()->store($invoice);
     }
     return $invoice;
 }
 protected function populate(int $invoice_id, &$invoice)
 {
     if ($invoice == null) {
         $invoice = new rental_invoice($this->db->f('id', true), $this->db->f('billing_id', true), $this->db->f('contract_id', true), $this->db->f('timestamp_created', true), $this->db->f('timestamp_start', true), $this->db->f('timestamp_end', true), $this->db->f('total_sum', true), $this->db->f('total_area', true), $this->db->f('header', true), $this->db->f('account_in', true), $this->db->f('account_out', true), $this->db->f('service_id', true), $this->db->f('responsibility_id', true), $this->db->f('project_id', true));
         $invoice->set_party_id($this->unmarshal($this->db->f('party_id'), 'int'));
         $invoice->set_project_id($this->unmarshal($this->db->f('project_id'), 'string'));
         $invoice->set_old_contract_id($this->unmarshal($this->db->f('old_contract_id'), 'string'));
         $invoice->set_term_id($this->unmarshal($this->db->f('term_id'), 'int'));
         $invoice->set_month($this->unmarshal($this->db->f('month'), 'int'));
         $invoice->set_billing_title($this->unmarshal($this->db->f('billing_title'), 'string'));
         $invoice->set_serial_number($this->unmarshal($this->db->f('serial_number'), 'int'));
         $invoice->set_reference($this->unmarshal($this->db->f('reference'), 'string'));
         $party = new rental_party($this->unmarshal($this->db->f('party_id'), 'int'));
         $party->set_account_number($this->unmarshal($this->db->f('party_account_number'), 'string'));
         $party->set_address_1($this->unmarshal($this->db->f('party_address_1'), 'string'));
         $party->set_address_2($this->unmarshal($this->db->f('party_address_2'), 'string'));
         $party->set_comment($this->unmarshal($this->db->f('party_comment'), 'string'));
         $party->set_company_name($this->unmarshal($this->db->f('party_company_name'), 'string'));
         $party->set_department($this->unmarshal($this->db->f('party_department'), 'string'));
         $party->set_email($this->unmarshal($this->db->f('party_email'), 'string'));
         $party->set_fax($this->unmarshal($this->db->f('party_fax'), 'string'));
         $party->set_first_name($this->unmarshal($this->db->f('party_first_name'), 'string'));
         $party->set_is_inactive($this->unmarshal($this->db->f('party_in_active'), 'bool'));
         $party->set_last_name($this->unmarshal($this->db->f('party_last_name'), 'string'));
         $party->set_location_id($this->unmarshal($this->db->f('party_org_location_id'), 'int'));
         $party->set_identifier($this->unmarshal($this->db->f('party_identifier'), 'string'));
         $party->set_mobile_phone($this->unmarshal($this->db->f('party_mobile_phone'), 'string'));
         $party->set_place($this->unmarshal($this->db->f('party_place'), 'string'));
         $party->set_postal_code($this->unmarshal($this->db->f('party_postal_code'), 'string'));
         $party->set_reskontro($this->unmarshal($this->db->f('party_reskontro'), 'string'));
         $party->set_title($this->unmarshal($this->db->f('party_title'), 'string'));
         $party->set_url($this->unmarshal($this->db->f('party_url'), 'string'));
         $invoice->set_party($party);
         if ($invoice->get_term_id() == 2) {
             // yearly
             $invoice->set_term_label(lang('annually'));
         } else {
             if ($invoice->get_term_id() == 3) {
                 // half year
                 if ($invoice->get_month() == 6) {
                     $invoice->set_term_label(lang('first_half'));
                 } else {
                     $invoice->set_term_label(lang('second_half'));
                 }
             } else {
                 if ($invoice->get_term_id() == 4) {
                     // quarterly
                     if ($invoice->get_month() == 3) {
                         $invoice->set_term_label(lang('first_quarter'));
                     } else {
                         if ($invoice->get_month() == 6) {
                             $invoice->set_term_label(lang('second_quarter'));
                         } else {
                             if ($invoice->get_month() == 9) {
                                 $invoice->set_term_label(lang('third_quarter'));
                             } else {
                                 $invoice->set_term_label(lang('fourth_quarter'));
                             }
                         }
                     }
                 } else {
                     $invoice->set_term_label(lang('month ' . $invoice->get_month() . ' capitalized'));
                 }
             }
         }
     }
     $invoice->add_composite_name($this->unmarshal($this->db->f('composite_name'), 'string'));
     return $invoice;
 }