Ejemplo n.º 1
0
 /**
  * This method adds taxrates to the invoice rows.
  *
  * @param array  The array containing all invoice rows
  *
  * @return array The array containing (taxed) invoice rows
  *
  * @author Former03 GmbH :: Florian Lippert <*****@*****.**>
  */
 public function applyTaxRate($invoice)
 {
     $invoice_new = array();
     foreach ($invoice as $rowid => $invoice_row) {
         // If we don't have a valid taxclass, use the default one.
         if (!isset($invoice_row['taxclass']) || !isset($this->taxclasses[$invoice_row['taxclass']])) {
             $invoice_row['taxclass'] = $this->default_taxclass;
         }
         if (isset($invoice_row['taxclass']) && isset($this->taxclasses[$invoice_row['taxclass']])) {
             // Once-fees are quite easy, just get the valid taxrate and add it to the row.
             if (isset($invoice_row['service_occurence']) && $invoice_row['service_occurence'] == 'once') {
                 $taxchanges = $this->getTaxChanges($invoice_row['taxclass'], $invoice_row['service_date']);
                 $pricing = array('taxrate' => $taxchanges[0]['taxrate'], 'total_fee' => $invoice_row['setup_fee']);
                 $invoice_new[] = array_merge($invoice_row, $pricing);
             } elseif (isset($invoice_row['service_occurence']) && $invoice_row['service_occurence'] == 'period') {
                 // Get all tax changes in our service interval
                 $taxchanges = $this->getTaxChanges($invoice_row['taxclass'], $invoice_row['service_date_begin'], manipulateDate($invoice_row['service_date_end'], '-', 1, 'd'));
                 // In pricing we store all changes to our invoice row, will get merged lateron.
                 $pricing = array('taxrate' => $taxchanges[0]['taxrate'], 'total_fee' => 0);
                 // number_days will store the days we already processed...
                 $number_days = 0;
                 // ... whereas days_diff contains the whole number of days of our service interval.
                 $days_diff = calculateDayDifference($invoice_row['service_date_begin'], $invoice_row['service_date_end']);
                 $service_date_begin_array = transferDateToArray($invoice_row['service_date_begin']);
                 // Now we walk through the interval, stepping is the interval length.
                 while ($days_diff >= $number_days + $this->getDaysForInterval($invoice_row['interval_length'], $invoice_row['interval_type'], $service_date_begin_array)) {
                     // Whenever we happen to meet a tax change, remaining will reduced by the number of days to that tax change.
                     $remaining = $interval_days = $this->getDaysForInterval($invoice_row['interval_length'], $invoice_row['interval_type'], $service_date_begin_array);
                     $interval_begin = manipulateDate($invoice_row['service_date_begin'], '+', $number_days, 'd');
                     $interval_end = manipulateDate($invoice_row['service_date_begin'], '+', $number_days + $interval_days, 'd');
                     // Now get tax changes in the current interval.
                     $taxchanges = $this->getTaxChanges($invoice_row['taxclass'], $interval_begin, $interval_end);
                     // Maybe taxrate already changed on the first day of our interval.
                     if ($pricing['taxrate'] != $taxchanges[0]['taxrate']) {
                         $pricing['service_date_end'] = $taxchanges[0]['valid_to'];
                         $invoice_new[] = array_merge($invoice_row, $pricing);
                         $pricing['taxrate'] = $taxchanges[0]['taxrate'];
                         $pricing['total_fee'] = 0;
                     }
                     // Anyways, we don't need the current taxrate.
                     unset($taxchanges[0]);
                     // Walk through all taxchanges...
                     foreach ($taxchanges as $valid_from => $taxchange) {
                         $tax_days = calculateDayDifference($interval_begin, $valid_from);
                         // Subtract the days we are going to tax from the remaining days in our interval
                         $remaining -= $tax_days;
                         // total_fee is a fraction of the interval fee
                         $pricing['total_fee'] += $invoice_row['interval_fee'] * ($tax_days / $interval_days);
                         // Set ending day of row to day when tax changed
                         $pricing['service_date_end'] = $taxchange['valid_from'];
                         // And add a new row to invoice
                         $invoice_new[] = array_merge($invoice_row, $pricing);
                         // Next line begins with the day when tax changed
                         $interval_begin = $pricing['service_date_begin'] = $taxchange['valid_from'];
                         $pricing['taxrate'] = $taxchange['taxrate'];
                         $pricing['total_fee'] = 0;
                     }
                     // Incruse number_days (loop condition value)
                     $number_days += $interval_days;
                     // also update service_date_begin_array, so self::getDaysForInterval returns a correct value in our loop condition
                     $service_date_begin_array[$invoice_row['interval_type']] += $invoice_row['interval_length'];
                     // Finally add the remaining fraction to total_fee
                     $pricing['total_fee'] += $invoice_row['interval_fee'] * ($remaining / $interval_days);
                 }
                 // Last element, so use our real service_date_end
                 unset($pricing['service_date_end']);
                 // If there are still have some days left (e.g. when service was terminated during an interval)...
                 if ($days_diff > $number_days) {
                     // ... calculate last total_fee...
                     $pricing['total_fee'] += $invoice_row['interval_fee'] * (($days_diff - $number_days) / $this->getDaysForInterval($invoice_row['interval_length'], $invoice_row['interval_type'], $service_date_begin_array));
                 }
                 // ... and finally add last line.
                 $invoice_new[] = array_merge($invoice_row, $pricing);
             }
         } else {
             $invoice_new[] = $invoice_row;
         }
     }
     return $invoice_new;
 }
Ejemplo n.º 2
0
 /**
  * This method collects the invoice rows.
  *
  * @param bool   Should we fix invoice (means we call self::setLastInvoiced to latest invoiced date).
  * @param bool   Should we include the setup fee?
  * @param bool   Should we include the interval fees?
  *
  * @return array All invoice rows
  *
  * @author Former03 GmbH :: Florian Lippert <*****@*****.**>
  */
 public function collect($fixInvoice = false, $include_setup_fee = false, $include_interval_fee = false)
 {
     $invoice = array();
     reset($this->service_details);
     foreach ($this->service_details as $serviceId => $service_detail) {
         if (checkDateArray(transferDateToArray($service_detail['servicestart_date'])) === true) {
             // Load template which is valid through our setup date
             $template = $this->findValidTemplate($service_detail['servicestart_date'], $this->selectAppropriateTemplateKey($service_detail));
             foreach ($this->defaultvalues as $field => $value) {
                 // We are using $this->service_details[$serviceId] instead of $service_detail so we can see the original values, as the "working copy" ($service_detail) could have been changed...
                 if ((!isset($this->service_details[$serviceId][$field]) || isset($this->service_details[$serviceId][$field]) && $this->service_details[$serviceId][$field] == $value) && isset($template[$field]) && $template[$field] != $value) {
                     $service_detail[$field] = $template[$field];
                 }
             }
             // If quantity is not set, we do need a value 1, otherwise this doesn't make sense...
             if (!isset($service_detail['quantity'])) {
                 $service_detail['quantity'] = 1;
             }
             // Add setup fee to invoice
             if (checkDateArray(transferDateToArray($service_detail['lastinvoiced_date'])) !== true || $this->allowLastInvoicedDatePastServiceStart === false && calculateDayDifference($service_detail['lastinvoiced_date'], $service_detail['servicestart_date']) > 0) {
                 if ($include_setup_fee === true) {
                     $invoice[] = $this->buildInvoiceRowSetupFee($service_detail, $this->getServiceDescription($service_detail, 'setup'));
                 }
                 $service_detail['lastinvoiced_date'] = $service_detail['servicestart_date'];
             }
             // If payment_every is not set, we do need a value 1, otherwise nextinvoiced_date wouldn't be calculated correctly and we'll get stuck in an infinite loop
             if (!isset($service_detail['payment_every'])) {
                 $service_detail['payment_every'] = 1;
             }
             if ((int) $service_detail['interval_length'] != 0 && (int) $service_detail['payment_every'] != 0 && in_array($service_detail['interval_type'], getIntervalTypes('array'))) {
                 $original_date = $service_detail['lastinvoiced_date'];
                 $service_detail['nextinvoiced_date'] = manipulateDate($service_detail['lastinvoiced_date'], '+', (int) $service_detail['interval_length'] * (int) $service_detail['payment_every'], $service_detail['interval_type'], $original_date);
                 while ($service_detail['interval_payment'] == CONST_BILLING_INTERVALPAYMENT_PREPAID && calculateDayDifference($service_detail['lastinvoiced_date'], time()) >= 0 && !($service_detail['service_active'] == '0' && calculateDayDifference($service_detail['lastinvoiced_date'], $service_detail['serviceend_date']) <= 0) || $service_detail['interval_payment'] == CONST_BILLING_INTERVALPAYMENT_POSTPAID && (calculateDayDifference($service_detail['nextinvoiced_date'], time()) >= 0 || $this->endServiceImmediately === true && $service_detail['service_active'] == '0' && calculateDayDifference($service_detail['lastinvoiced_date'], $service_detail['serviceend_date']) > 0 && calculateDayDifference($service_detail['serviceend_date'], $service_detail['nextinvoiced_date']) >= 0 && calculateDayDifference($service_detail['lastinvoiced_date'], time()) >= 0 && calculateDayDifference($service_detail['serviceend_date'], time()) >= 0)) {
                     // Reload template which is valid through our current invoice period
                     reset($this->defaultvalues);
                     $template = $this->findValidTemplate($service_detail['lastinvoiced_date'], $this->selectAppropriateTemplateKey($service_detail));
                     foreach ($this->defaultvalues as $field => $value) {
                         // We are using $this->service_details[$serviceId] instead of $service_detail so we can see the original values, as the "working copy" ($service_detail) could have been changed...
                         if ((!isset($this->service_details[$serviceId][$field]) || isset($this->service_details[$serviceId][$field]) && $this->service_details[$serviceId][$field] == $value) && isset($template[$field]) && $template[$field] != $value) {
                             $service_detail[$field] = $template[$field];
                         }
                     }
                     if ($this->endServiceImmediately === true && $service_detail['service_active'] == '0' && calculateDayDifference($service_detail['lastinvoiced_date'], $service_detail['serviceend_date']) > 0 && calculateDayDifference($service_detail['serviceend_date'], $service_detail['nextinvoiced_date']) >= 0 && calculateDayDifference($service_detail['lastinvoiced_date'], time()) >= 0 && calculateDayDifference($service_detail['serviceend_date'], time()) >= 0) {
                         $service_detail['nextinvoiced_date'] = $service_detail['serviceend_date'];
                     }
                     // Sanity check, shouldn't be needed...
                     if (calculateDayDifference($service_detail['lastinvoiced_date'], $service_detail['nextinvoiced_date']) >= 0) {
                         $service_detail['service_date_begin'] = $service_detail['lastinvoiced_date'];
                         $service_detail['service_date_end'] = $service_detail['nextinvoiced_date'];
                         if ($include_interval_fee === true) {
                             $invoice[] = $this->buildInvoiceRowIntervalFee($service_detail, $this->getServiceDescription($service_detail, 'interval'));
                         }
                     }
                     // Go on in loop, set lastinvoiced_date to nextinvoiced_date ...
                     $service_detail['lastinvoiced_date'] = $service_detail['nextinvoiced_date'];
                     // ... and recalculate nextinvoiced_date.
                     $service_detail['nextinvoiced_date'] = manipulateDate($service_detail['lastinvoiced_date'], '+', (int) $service_detail['interval_length'] * (int) $service_detail['payment_every'], $service_detail['interval_type'], $original_date);
                 }
             }
             if ($fixInvoice === true) {
                 $this->setLastInvoiced($serviceId, $service_detail);
             }
         }
     }
     return $invoice;
 }
Ejemplo n.º 3
0
 /**
  * This method processes the given xml string and creates
  * thogether with the given language array a pdf reminder.
  *
  * @param string The xml string which contains our invoice data
  * @param array  A valid language array
  *
  * @author Former03 GmbH :: Florian Lippert <*****@*****.**>
  */
 public function processData($xml, $lng)
 {
     $this->invoiceXml = new SimpleXMLElement($xml);
     $contactdetails = '';
     if (utf8_decode((string) $this->invoiceXml->address->company[0]) != '') {
         $contactdetails .= utf8_decode((string) $this->invoiceXml->address->company[0]) . "\n";
     }
     if (utf8_decode((string) $this->invoiceXml->address->title[0]) != '') {
         $contactdetails .= utf8_decode((string) $this->invoiceXml->address->title[0]) . ' ';
     }
     if (utf8_decode((string) $this->invoiceXml->address->firstname[0]) != '' && utf8_decode((string) $this->invoiceXml->address->name[0]) != '') {
         $contactdetails .= utf8_decode((string) $this->invoiceXml->address->firstname[0]) . ' ' . utf8_decode((string) $this->invoiceXml->address->name[0]) . "\n";
     }
     $contactdetails .= utf8_decode((string) $this->invoiceXml->address->street[0]) . "\n" . utf8_decode((string) $this->invoiceXml->address->zipcode[0]) . ' ' . utf8_decode((string) $this->invoiceXml->address->city[0]) . "\n" . utf8_decode((string) $this->invoiceXml->address->country[0]);
     $this->pdf->addPage();
     // Write Sender
     $this->pdf->SetTextColor(160, 160, 160);
     $this->pdf->SetFont('', 'B', 6);
     $this->pdf->SetXY(17.5, 50.5);
     $this->pdf->Cell(0, 0, html_entity_decode($lng['invoice']['sender']));
     $this->pdf->SetTextColor(0, 0, 0);
     $this->pdf->Ln();
     // Write Address
     $this->pdf->SetFont('', '', 10);
     $this->pdf->SetXY(18, 60);
     $this->pdf->MultiCell(70, 4, $contactdetails, 0, 'L');
     $this->pdf->Ln();
     // Write Subject
     $this->pdf->SetFont('', 'B', 13);
     $this->pdf->SetXY(18, 100);
     $this->pdf->Cell(35, 4, html_entity_decode($lng['invoice']['reminder']), 0, 2, 'L');
     $this->pdf->Ln();
     // Write Date
     $this->pdf->SetFont('', '', 8);
     $this->pdf->SetXY(18, 96);
     $this->pdf->Cell(70, 4, sprintf(html_entity_decode($lng['invoice']['dateheader']), makeNicePresentableDate(date('Y-m-d'), $lng['panel']['dateformat_function'])), 0, 2, 'L');
     $this->pdf->Ln();
     // Write  invoice number
     $this->pdf->SetFont('', '', 9);
     $this->pdf->SetXY(18, 110);
     $this->pdf->Cell(0, 4, utf8_decode('Sehr geehrte Damen und Herren,'), 0, 0, 'L');
     $this->pdf->Ln(8);
     $this->pdf->MultiCell(0, 4, utf8_decode('bisher haben wir keinen Geldeingang zu unserer Rechnung ' . (string) $this->invoiceXml->invoice_number[0] . ' vom ' . (string) $this->invoiceXml->invoice_date[0] . ' feststellen können. Die Rechnung bezog sich auf:'), 0, 'L');
     $this->pdf->SetFont('', 'B', 9);
     $this->pdf->Ln(4);
     $this->pdf->SetX(23);
     $this->pdf->MultiCell(0, 4, utf8_decode((string) $this->invoiceXml->billing->contract_details[0]), 0, 'L');
     $this->pdf->SetFont('', '', 9);
     $this->pdf->Ln(4);
     $this->pdf->SetX(18);
     $this->pdf->Cell(0, 4, utf8_decode('Bitte überweisen Sie uns den fälligen '), 0, 0, 'L');
     $this->pdf->Ln(6);
     $this->pdf->SetX(23);
     $this->pdf->Cell(35, 4, utf8_decode('Gesamtbetrag von'), 0, 0, 'L');
     $this->pdf->SetFont('', 'B', 9);
     $this->pdf->Cell(150, 4, utf8_decode((string) $this->invoiceXml->total_fee_taxed[0]) . ' ' . chr(128), 0, 0, 'L');
     $this->pdf->SetFont('', '', 9);
     $this->pdf->Ln(6);
     $this->pdf->SetX(23);
     $this->pdf->Cell(35, 4, utf8_decode('bis spätestens zum'), 0, 0, 'L');
     $this->pdf->SetFont('', 'B', 9);
     $this->pdf->Cell(150, 4, makeNicePresentableDate(manipulateDate(time(), '+', (string) $this->invoiceXml->billing->term_of_payment[0], 'd'), $lng['panel']['dateformat_function']), 0, 0, 'L');
     $this->pdf->SetFont('', '', 9);
     $this->pdf->Ln(6);
     $this->pdf->SetX(18);
     $this->pdf->MultiCell(0, 4, utf8_decode('auf unser unten genanntes Konto. Wir möchten Sie freundlich darauf hinweisen, dass Sie mit der Zahlung in Verzug sind. Sollten Sie die Forderung nicht bis zum genannten Datum begleichen, werden wir nebst der Forderung noch Verzugszinsen geltend machen.'), 0, 'L');
 }
Ejemplo n.º 4
0
 /**
  * This method exports the invoice rows as an 2-dim array with the following style:
  *
  *	array(5) {
  *	  ["hosting"]=>
  *	  array(5) {
  *	    ["caption"]=>
  *	    string(7) "HOSTING"
  *	    ["service_date_begin"]=>
  *	    string(10) "2008-01-01"
  *	    ["service_date_end"]=>
  *	    string(9) "2008-6-30"
  *	    ["interval"]=>
  *	    string(23) "01.01.2008 - 30.06.2008"
  *	    ["rows"]=>
  *	    array(1) {
  *	      [0]=>
  *	      array(15) {
  *	        ["service_type"]=>
  *	        string(7) "hosting"
  *	        ["service_occurence"]=>
  *	        string(4) "once"
  *	        ["quantity"]=>
  *	        int(1)
  *	        ["setup_fee"]=>
  *	        string(6) "200.00"
  *	        ["taxclass"]=>
  *	        string(1) "1"
  *	        ["service_date"]=>
  *	        string(10) "2008-01-01"
  *	        ["description"]=>
  *	        array(2) {
  *	          ["loginname"]=>
  *	          string(9) "reseller1"
  *	          ["caption"]=>
  *	          string(38) "Hostingpaket - Einrichtungsgeb&uuml;hr"
  *	        }
  *	        ["taxrate"]=>
  *	        string(6) "0.1900"
  *	        ["total_fee"]=>
  *	        string(6) "200.00"
  *	        ["key"]=>
  *	        string(32) "090578d4cb0b28db6f2a17f8b268dbc5"
  *	        ["interval"]=>
  *	        string(10) "01.01.2008"
  *	        ["single_fee"]=>
  *	        string(6) "200.00"
  *	        ["tax"]=>
  *	        string(5) "38.00"
  *	        ["total_fee_taxed"]=>
  *	        string(6) "238.00"
  *	        ["taxrate_percent"]=>
  *	        float(19)
  *	      }
  *	    }
  *	  }
  *	}
  *
  * @param array  Language array
  * @param bool   True if changes should be attached to the array (useful in edit mode ;-)
  * @return array The array, see above for example.
  *
  * @author Former03 GmbH :: Florian Lippert <*****@*****.**>
  */
 function exportArray($lng = array(), $attachHistory = false)
 {
     if ($this->userId == 0 || !is_array($this->user) || empty($this->user) || $this->user[getModeDetails($this->mode, 'TABLE_PANEL_USERS', 'key')] != $this->userId) {
         return false;
     }
     $invoice_changes = array();
     $invoice_changes_result = $this->db->query('SELECT * FROM `' . getModeDetails($this->mode, 'TABLE_BILLING_INVOICE_CHANGES', 'table') . '` WHERE `' . getModeDetails($this->mode, 'TABLE_BILLING_INVOICE_CHANGES', 'key') . '` = \'' . $this->userId . '\' ORDER BY `timestamp` ASC');
     while ($invoice_changes_row = $this->db->fetch_array($invoice_changes_result)) {
         $invoice_changes[$invoice_changes_row['key']][$invoice_changes_row['timestamp']] = $invoice_changes_row;
     }
     $returnval = array();
     reset($this->invoice);
     foreach ($this->invoice as $rowid => $invoice_row) {
         if (!empty($invoice_row)) {
             if (!isset($invoice_row['service_occurence'])) {
                 if (isset($invoice_row['service_date_begin']) && isset($invoice_row['service_date_end'])) {
                     $invoice_row['service_occurence'] = 'period';
                 } elseif (isset($invoice_row['service_date'])) {
                     $invoice_row['service_occurence'] = 'once';
                 }
             }
             if (!isset($invoice_row['key'])) {
                 // Begin Key generation to detect changes in invoice
                 $invoice_row['key'] = $invoice_row['service_type'] . '-' . $invoice_row['service_occurence'] . '-';
                 if (isset($invoice_row['service_occurence'])) {
                     switch ($invoice_row['service_occurence']) {
                         case 'once':
                             $invoice_row['key'] .= $invoice_row['service_date'] . '-';
                             break;
                         case 'period':
                             $invoice_row['key'] .= $invoice_row['service_date_begin'] . '-' . $invoice_row['service_date_end'] . '-';
                             break;
                     }
                 }
                 reset($invoice_row['description']);
                 foreach ($invoice_row['description'] as $description_key => $description_value) {
                     $invoice_row['key'] .= $description_key . '.' . $description_value . '-';
                 }
                 $invoice_row['key'] = md5($invoice_row['key']);
                 // End key generation
             }
             if (isset($invoice_row['service_occurence'])) {
                 switch ($invoice_row['service_occurence']) {
                     case 'once':
                         $rowcaption_field = 'category_rowcaption_setup';
                         if (!isset($invoice_row['interval'])) {
                             $invoice_row['interval'] = makeNicePresentableDate($invoice_row['service_date'], $lng['panel']['dateformat_function']);
                         }
                         break;
                     case 'period':
                         $rowcaption_field = 'category_rowcaption_interval';
                         if (!isset($invoice_row['interval'])) {
                             $invoice_row['service_date_end'] = manipulateDate($invoice_row['service_date_end'], '-', 1, 'd');
                             if (calculateDayDifference($invoice_row['service_date_begin'], $invoice_row['service_date_end']) != 0) {
                                 $invoice_row['interval'] = makeNicePresentableDate($invoice_row['service_date_begin'], $lng['panel']['dateformat_function']) . ' - ' . makeNicePresentableDate($invoice_row['service_date_end'], $lng['panel']['dateformat_function']);
                             } else {
                                 $invoice_row['interval'] = makeNicePresentableDate($invoice_row['service_date_begin'], $lng['panel']['dateformat_function']);
                             }
                         }
                         break;
                 }
             }
             if (!isset($invoice_row['description']['caption']) || $invoice_row['description']['caption'] == '') {
                 if (isset($invoice_row['description']['caption_class']) && isset($lng['billing']['categories'][$this->service_categories[$invoice_row['service_type']][$rowcaption_field] . '_' . $invoice_row['description']['caption_class']])) {
                     $invoice_row['description']['caption'] = $lng['billing']['categories'][$this->service_categories[$invoice_row['service_type']][$rowcaption_field] . '_' . $invoice_row['description']['caption_class']];
                 } elseif (isset($lng['billing']['categories'][$this->service_categories[$invoice_row['service_type']][$rowcaption_field]])) {
                     $invoice_row['description']['caption'] = $lng['billing']['categories'][$this->service_categories[$invoice_row['service_type']][$rowcaption_field]];
                 } else {
                     $invoice_row['description']['caption'] = $this->service_categories[$invoice_row['service_type']]['category_rowcaption'];
                 }
             }
             reset($invoice_row['description']);
             foreach ($invoice_row['description'] as $description_key => $description_value) {
                 $invoice_row['description']['caption'] = str_replace('{' . $description_key . '}', $description_value, $invoice_row['description']['caption']);
             }
             // See if the key is set and what to do
             $show_row = true;
             if (isset($invoice_changes[$invoice_row['key']]) && is_array($invoice_changes[$invoice_row['key']])) {
                 if ($attachHistory === true) {
                     $invoice_row['history'] = $invoice_changes[$invoice_row['key']];
                     $invoice_row['history'][0] = array('caption' => $invoice_row['description']['caption'], 'interval' => $invoice_row['interval'], 'taxrate' => $invoice_row['taxrate'], 'quantity' => $invoice_row['quantity'], 'total_fee' => $invoice_row['total_fee']);
                     krsort($invoice_row['history']);
                 }
                 foreach ($invoice_changes[$invoice_row['key']] as $timestamp => $invoice_changes_row) {
                     switch ($invoice_changes_row['action']) {
                         case '1':
                             // Don't show this row
                             $invoice_row['deleted'] = true;
                             $show_row = $attachHistory;
                             break;
                         case '2':
                             // Change caption, interval, taxrate and total_fee
                             $invoice_row['deleted'] = false;
                             if (isset($invoice_changes_row['caption'])) {
                                 $invoice_row['description']['caption'] = $invoice_changes_row['caption'];
                             }
                             if (isset($invoice_changes_row['interval'])) {
                                 $invoice_row['interval'] = $invoice_changes_row['interval'];
                             }
                             if (isset($invoice_changes_row['taxrate'])) {
                                 $invoice_row['taxrate'] = $invoice_changes_row['taxrate'];
                             }
                             if (isset($invoice_changes_row['quantity'])) {
                                 $invoice_row['quantity'] = $invoice_changes_row['quantity'];
                             }
                             if (isset($invoice_changes_row['total_fee'])) {
                                 $invoice_row['total_fee'] = $invoice_changes_row['total_fee'];
                             }
                             break;
                     }
                 }
             }
             if ($show_row) {
                 if (!isset($returnval[$invoice_row['service_type']]) || !is_array($returnval[$invoice_row['service_type']])) {
                     if (isset($this->service_categories[$invoice_row['service_type']]) && is_array($this->service_categories[$invoice_row['service_type']]) && isset($this->service_categories[$invoice_row['service_type']]['category_caption']) && $this->service_categories[$invoice_row['service_type']]['category_caption'] != '') {
                         if (isset($lng['billing']['categories'][$this->service_categories[$invoice_row['service_type']]['category_caption']])) {
                             $caption = $lng['billing']['categories'][$this->service_categories[$invoice_row['service_type']]['category_caption']];
                         } else {
                             $caption = $this->service_categories[$invoice_row['service_type']]['category_caption'];
                         }
                     }
                     $returnval[$invoice_row['service_type']] = array('caption' => $caption, 'service_date_begin' => 0, 'service_date_end' => 0, 'interval' => '', 'rows' => array());
                 }
                 if (isset($invoice_row['service_occurence'])) {
                     switch ($invoice_row['service_occurence']) {
                         case 'once':
                             if (calculateDayDifference($invoice_row['service_date'], $returnval[$invoice_row['service_type']]['service_date_begin']) > 0 || $returnval[$invoice_row['service_type']]['service_date_begin'] == 0) {
                                 $returnval[$invoice_row['service_type']]['service_date_begin'] = $invoice_row['service_date'];
                             }
                             if (calculateDayDifference($returnval[$invoice_row['service_type']]['service_date_end'], $invoice_row['service_date']) > 0 || $returnval[$invoice_row['service_type']]['service_date_end'] == 0) {
                                 $returnval[$invoice_row['service_type']]['service_date_end'] = $invoice_row['service_date'];
                             }
                             break;
                         case 'period':
                             if (calculateDayDifference($invoice_row['service_date_begin'], $returnval[$invoice_row['service_type']]['service_date_begin']) > 0 || $returnval[$invoice_row['service_type']]['service_date_begin'] == 0) {
                                 $returnval[$invoice_row['service_type']]['service_date_begin'] = $invoice_row['service_date_begin'];
                             }
                             if (calculateDayDifference($returnval[$invoice_row['service_type']]['service_date_end'], $invoice_row['service_date_end']) > 0 || $returnval[$invoice_row['service_type']]['service_date_end'] == 0) {
                                 $returnval[$invoice_row['service_type']]['service_date_end'] = $invoice_row['service_date_end'];
                             }
                             break;
                     }
                     if (calculateDayDifference($returnval[$invoice_row['service_type']]['service_date_begin'], $returnval[$invoice_row['service_type']]['service_date_end']) != 0) {
                         $returnval[$invoice_row['service_type']]['interval'] = makeNicePresentableDate($returnval[$invoice_row['service_type']]['service_date_begin'], $lng['panel']['dateformat_function']) . ' - ' . makeNicePresentableDate($returnval[$invoice_row['service_type']]['service_date_end'], $lng['panel']['dateformat_function']);
                     } else {
                         $returnval[$invoice_row['service_type']]['interval'] = makeNicePresentableDate($returnval[$invoice_row['service_type']]['service_date_begin'], $lng['panel']['dateformat_function']);
                     }
                 }
                 $invoice_row['single_fee'] = $invoice_row['total_fee'];
                 $invoice_row['total_fee'] *= $invoice_row['quantity'];
                 $invoice_row['tax'] = sprintf("%01.2f", round($invoice_row['total_fee'] * $invoice_row['taxrate'], 2));
                 $invoice_row['total_fee_taxed'] = sprintf("%01.2f", round($invoice_row['total_fee'] + $invoice_row['tax'], 2));
                 $invoice_row['taxrate_percent'] = $invoice_row['taxrate'] * 100;
                 $invoice_row['total_fee'] = sprintf("%01.2f", $invoice_row['total_fee']);
                 $returnval[$invoice_row['service_type']]['rows'][] = $invoice_row;
             }
         }
     }
     return $returnval;
 }