Exemplo n.º 1
0
 public static function save_invoice($invoice_id, $data)
 {
     if (!(int) $invoice_id && isset($data['job_id']) && $data['job_id']) {
         $linkedjob = module_job::get_job($data['job_id']);
         $data['currency_id'] = $linkedjob['currency_id'];
         $data['customer_id'] = $linkedjob['customer_id'];
     }
     if ($invoice_id) {
         // used when working out the hourly rate fix below
         $original_invoice_data = self::get_invoice($invoice_id);
     } else {
         $original_invoice_data = 0;
     }
     $invoice_id = update_insert("invoice_id", $invoice_id, "invoice", $data);
     if ($invoice_id) {
         module_cache::clear('invoice');
         // save the invoice tax rates (copied to finance.php)
         if (isset($data['tax_ids']) && isset($data['tax_names']) && $data['tax_percents']) {
             $existing_taxes = get_multiple('invoice_tax', array('invoice_id' => $invoice_id), 'invoice_tax_id', 'exact', 'order');
             $order = 1;
             foreach ($data['tax_ids'] as $key => $val) {
                 if (isset($data['tax_percents'][$key]) && $data['tax_percents'][$key] == 0) {
                     // we are not saving this particular tax item because it has a 0% tax rate
                 } else {
                     if ((int) $val > 0 && isset($existing_taxes[$val])) {
                         // this means we are trying to update an existing record on the invoice_tax table, we confirm this id matches this invoice.
                         $invoice_tax_id = $val;
                         unset($existing_taxes[$invoice_tax_id]);
                         // so we know which ones to remove from the end.
                     } else {
                         $invoice_tax_id = false;
                         // create new record
                     }
                     $invoice_tax_data = array('invoice_id' => $invoice_id, 'percent' => isset($data['tax_percents'][$key]) ? $data['tax_percents'][$key] : 0, 'amount' => 0, 'name' => isset($data['tax_names'][$key]) ? $data['tax_names'][$key] : 'TAX', 'order' => $order++, 'increment' => isset($data['tax_increment_checkbox']) && $data['tax_increment_checkbox'] ? 1 : 0);
                     $invoice_tax_id = update_insert('invoice_tax_id', $invoice_tax_id, 'invoice_tax', $invoice_tax_data);
                 }
             }
             foreach ($existing_taxes as $existing_tax) {
                 delete_from_db('invoice_tax', array('invoice_id', 'invoice_tax_id'), array($invoice_id, $existing_tax['invoice_tax_id']));
             }
         }
         $invoice_data = self::get_invoice($invoice_id);
         if (!$invoice_data) {
             set_error('No permissions to access invoice.');
             return $invoice_id;
         }
         // check for new invoice_items or changed invoice_items.
         $invoice_items = self::get_invoice_items($invoice_id, $invoice_data);
         if (isset($data['invoice_invoice_item']) && is_array($data['invoice_invoice_item'])) {
             foreach ($data['invoice_invoice_item'] as $invoice_item_id => $invoice_item_data) {
                 $invoice_item_id = (int) $invoice_item_id;
                 if (!is_array($invoice_item_data)) {
                     continue;
                 }
                 if ($invoice_item_id > 0 && !isset($invoice_items[$invoice_item_id])) {
                     continue;
                 }
                 // wrong invoice_item save - will never happen.
                 if (!isset($invoice_item_data['description']) || $invoice_item_data['description'] == '') {
                     if ($invoice_item_id > 0) {
                         // remove invoice_item.
                         $sql = "DELETE FROM `" . _DB_PREFIX . "invoice_item` WHERE invoice_item_id = '{$invoice_item_id}' AND invoice_id = {$invoice_id} LIMIT 1";
                         query($sql);
                     }
                     continue;
                 }
                 // add / save this invoice_item.
                 $invoice_item_data['invoice_id'] = $invoice_id;
                 // what type of task is this?
                 $invoice_task_type = isset($invoice_item_data['manual_task_type']) && $invoice_item_data['manual_task_type'] >= 0 ? $invoice_item_data['manual_task_type'] : $invoice_data['default_task_type'];
                 $invoice_item_data['hours_mins'] = 0;
                 if (isset($invoice_item_data['hours']) && $invoice_task_type == _TASK_TYPE_HOURS_AMOUNT) {
                 }
                 if (isset($invoice_item_data['hours']) && $invoice_task_type == _TASK_TYPE_HOURS_AMOUNT && function_exists('decimal_time_in')) {
                     $invoice_item_data['hours'] = decimal_time_in($invoice_item_data['hours']);
                     if (strpos($invoice_item_data['hours'], ':') !== false) {
                         $invoice_item_data['hours_mins'] = str_replace(":", ".", $invoice_item_data['hours']);
                     }
                 } else {
                     if (isset($invoice_item_data['hours']) && strlen($invoice_item_data['hours'])) {
                         $invoice_item_data['hours'] = number_in($invoice_item_data['hours']);
                     } else {
                         $invoice_item_data['hours'] = 0;
                     }
                 }
                 // number formatting
                 //print_r($invoice_item_data);
                 if (isset($invoice_item_data['hourly_rate']) && strlen($invoice_item_data['hourly_rate'])) {
                     $invoice_item_data['hourly_rate'] = number_in($invoice_item_data['hourly_rate'], module_config::c('task_amount_decimal_places', -1));
                 }
                 //print_r($invoice_item_data);exit;
                 // somenew hacks here to support out new method of creating an item.
                 // the 'amount' column is never edited any more
                 // this column is now always automatically calculated based on
                 // 'hours' and 'hourly_rate'
                 if (!isset($invoice_item_data['amount'])) {
                     if ($invoice_task_type == _TASK_TYPE_AMOUNT_ONLY) {
                         // ignore the quantity field all together.
                         $invoice_item_data['amount'] = $invoice_item_data['hourly_rate'];
                         $invoice_item_data['hourly_rate'] = 0;
                     } else {
                         if (isset($invoice_item_data['hourly_rate']) && strlen($invoice_item_data['hourly_rate']) > 0) {
                             // if we have inputted an hourly rate (ie: not left empty)
                             if (isset($invoice_item_data['hours']) && strlen($invoice_item_data['hours']) == 0) {
                                 // no hours entered (eg: empty) so we treat whatever was in 'hourly_rate' as the amount
                                 $invoice_item_data['amount'] = $invoice_item_data['hourly_rate'];
                             } else {
                                 if (isset($invoice_item_data['hours']) && strlen($invoice_item_data['hours']) > 0) {
                                     // hours inputted, along with hourly rate. work out the new amount.
                                     $invoice_item_data['amount'] = round($invoice_item_data['hours'] * $invoice_item_data['hourly_rate'], module_config::c('currency_decimal_places', 2));
                                 }
                             }
                         }
                     }
                 }
                 if ($invoice_task_type == _TASK_TYPE_HOURS_AMOUNT) {
                     if ($invoice_item_data['hourly_rate'] == $invoice_data['hourly_rate'] || isset($original_invoice_data['hourly_rate']) && $invoice_item_data['hourly_rate'] == $original_invoice_data['hourly_rate']) {
                         $invoice_item_data['hourly_rate'] = -1;
                     }
                 }
                 // remove the amount of it equals the hourly rate.
                 /*if(isset($invoice_item_data['amount']) && isset($invoice_item_data['hours']) && $invoice_item_data['amount'] > 0 && $invoice_item_data['hours'] > 0){
                       if($invoice_item_data['amount'] - ($invoice_item_data['hours'] * $data['hourly_rate']) == 0){
                           unset($invoice_item_data['amount']);
                       }
                   }*/
                 // check if we haven't unticked a non-hourly invoice_item
                 /*if(isset($invoice_item_data['completed_t']) && $invoice_item_data['completed_t'] && !isset($invoice_item_data['completed'])){
                       $invoice_item_data['completed'] = 0;
                   }*/
                 if (!isset($invoice_item_data['taxable_t'])) {
                     $invoice_item_data['taxable'] = module_config::c('task_taxable_default', 1);
                 } else {
                     if (isset($invoice_item_data['taxable_t']) && $invoice_item_data['taxable_t'] && !isset($invoice_item_data['taxable'])) {
                         $invoice_item_data['taxable'] = 0;
                     }
                 }
                 if (!strlen($invoice_item_data['hours'])) {
                     $invoice_item_data['hours'] = 0;
                 }
                 $invoice_item_data['hourly_rate'] = number_out($invoice_item_data['hourly_rate'], false, module_config::c('task_amount_decimal_places', -1));
                 $invoice_item_data['hours'] = number_out($invoice_item_data['hours']);
                 $invoice_item_data['amount'] = number_out($invoice_item_data['amount']);
                 update_insert('invoice_item_id', $invoice_item_id, 'invoice_item', $invoice_item_data);
             }
         }
         $last_payment_time = 0;
         if (isset($data['invoice_invoice_payment']) && is_array($data['invoice_invoice_payment'])) {
             foreach ($data['invoice_invoice_payment'] as $invoice_payment_id => $invoice_payment_data) {
                 $invoice_payment_id = (int) $invoice_payment_id;
                 if (!is_array($invoice_payment_data)) {
                     continue;
                 }
                 if (isset($invoice_payment_data['amount'])) {
                     $invoice_payment_data['amount'] = number_in($invoice_payment_data['amount']);
                     // toggle between 'normal' and 'refund' payment types
                     if (isset($invoice_payment_data['payment_type'])) {
                         if ($invoice_payment_data['amount'] < 0 && $invoice_payment_data['payment_type'] == _INVOICE_PAYMENT_TYPE_NORMAL) {
                             // this is a refund.
                             $invoice_payment_data['payment_type'] = _INVOICE_PAYMENT_TYPE_REFUND;
                         } else {
                             if ($invoice_payment_data['payment_type'] == _INVOICE_PAYMENT_TYPE_REFUND) {
                                 $invoice_payment_data['payment_type'] = _INVOICE_PAYMENT_TYPE_NORMAL;
                             }
                         }
                     }
                 }
                 // check this invoice payment actually matches this invoice.
                 $invoice_payment_data_existing = false;
                 if ($invoice_payment_id > 0) {
                     $invoice_payment_data_existing = get_single('invoice_payment', array('invoice_payment_id', 'invoice_id'), array($invoice_payment_id, $invoice_id));
                     if (!$invoice_payment_data_existing || $invoice_payment_data_existing['invoice_payment_id'] != $invoice_payment_id || $invoice_payment_data_existing['invoice_id'] != $invoice_id) {
                         $invoice_payment_id = 0;
                         $invoice_payment_data_existing = false;
                     }
                 }
                 if (!isset($invoice_payment_data['amount']) || $invoice_payment_data['amount'] == '' || $invoice_payment_data['amount'] == 0) {
                     // || $invoice_payment_data['amount'] <= 0
                     if ($invoice_payment_id > 0) {
                         // if this is a customer credit payment, return that back to the customer account.
                         if ($invoice_payment_data_existing && $invoice_data['customer_id']) {
                             switch ($invoice_payment_data_existing['payment_type']) {
                                 case _INVOICE_PAYMENT_TYPE_CREDIT:
                                     module_customer::add_credit($invoice_data['customer_id'], $invoice_payment_data_existing['amount'], 'Refunded credit from invoice payment');
                                     break;
                             }
                         }
                         // remove invoice_payment.
                         $sql = "DELETE FROM `" . _DB_PREFIX . "invoice_payment` WHERE invoice_payment_id = '{$invoice_payment_id}' AND invoice_id = {$invoice_id} LIMIT 1";
                         query($sql);
                         // delete any existing transactions from the system as well.
                         hook_handle_callback('invoice_payment_deleted', $invoice_payment_id, $invoice_id);
                     }
                     continue;
                 }
                 if (!$invoice_payment_id && (!isset($_REQUEST['add_payment']) || $_REQUEST['add_payment'] != 'go')) {
                     continue;
                     // not saving a new one.
                 }
                 // add / save this invoice_payment.
                 $invoice_payment_data['invoice_id'] = $invoice_id;
                 // $invoice_payment_data['currency_id'] = $invoice_data['currency_id'];
                 $last_payment_time = max($last_payment_time, strtotime(input_date($invoice_payment_data['date_paid'])));
                 if (isset($invoice_payment_data['custom_notes'])) {
                     $details = @unserialize($invoice_payment_data['data']);
                     if (!is_array($details)) {
                         $details = array();
                     }
                     $details['custom_notes'] = $invoice_payment_data['custom_notes'];
                     $invoice_payment_data['data'] = serialize($details);
                 }
                 $invoice_payment_data['amount'] = number_out($invoice_payment_data['amount']);
                 update_insert('invoice_payment_id', $invoice_payment_id, 'invoice_payment', $invoice_payment_data);
             }
         }
         if (!$last_payment_time) {
             $last_payment_time = strtotime(date('Y-m-d'));
         }
         // check if the invoice has been paid
         module_cache::clear('invoice');
         //module_cache::clear_cache(); // this helps fix the bug where part payments are not caulcated a correct paid date.
         $invoice_data = self::get_invoice($invoice_id);
         if (!$invoice_data) {
             set_error('No permissions to access invoice.');
             return $invoice_id;
         }
         if ((!$invoice_data['date_paid'] || $invoice_data['date_paid'] == '0000-00-00') && $invoice_data['total_amount_due'] <= 0 && ($invoice_data['total_amount_paid'] > 0 || $invoice_data['discount_amount'] > 0) && (!$invoice_data['date_cancel'] || $invoice_data['date_cancel'] == '0000-00-00')) {
             // find the date of the last payment history.
             // if the sent date is null also update that.
             $date_sent = $invoice_data['date_sent'];
             if (!$date_sent || $date_sent == '0000-00-00') {
                 $date_sent = date('Y-m-d', $last_payment_time);
             }
             update_insert("invoice_id", $invoice_id, "invoice", array('date_paid' => date('Y-m-d', $last_payment_time), 'date_sent' => $date_sent, 'status' => _l('Paid')));
             // hook for our ticketing plugin to mark a priority support ticket as paid.
             // or anything else down the track.
             module_cache::clear('invoice');
             handle_hook('invoice_paid', $invoice_id);
             if (module_config::c('invoice_automatic_receipt', 1)) {
                 // send receipt to customer.
                 self::email_invoice_to_customer($invoice_id);
             }
         }
         if ($invoice_data['total_amount_due'] > 0) {
             // update the status to unpaid.
             update_insert("invoice_id", $invoice_id, "invoice", array('date_paid' => '', 'status' => $invoice_data['status'] == _l('Paid') ? module_config::s('invoice_status_default', 'New') : $invoice_data['status']));
         }
         if (class_exists('module_extra', false) && module_extra::is_plugin_enabled()) {
             module_extra::save_extras('invoice', 'invoice_id', $invoice_id);
         }
         if ($invoice_data['customer_id']) {
             //module_cache::clear_cache();
             module_cache::clear('invoice');
             module_customer::update_customer_status($invoice_data['customer_id']);
         }
         hook_handle_callback('invoice_saved', $invoice_id, $invoice_data);
     }
     module_cache::clear('invoice');
     module_cache::clear('job');
     return $invoice_id;
 }