Files
codecanyon-rest-api-for-per…/api/models/Credit_notes_model.php
2025-10-29 11:09:43 +01:00

732 lines
39 KiB
PHP

<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Credit_notes_model extends App_Model {
private $shipping_fields = ['shipping_street', 'shipping_city', 'shipping_city', 'shipping_state', 'shipping_zip', 'shipping_country'];
public function __construct() {
parent::__construct();
$this->load->model('invoices_model');
}
public function get_statuses() {
return hooks()->apply_filters('before_get_credit_notes_statuses', [['id' => 1, 'color' => '#03a9f4', 'name' => _l('credit_note_status_open'), 'order' => 1, 'filter_default' => true, ], ['id' => 2, 'color' => '#84c529', 'name' => _l('credit_note_status_closed'), 'order' => 2, 'filter_default' => true, ], ['id' => 3, 'color' => '#777', 'name' => _l('credit_note_status_void'), 'order' => 3, 'filter_default' => false, ], ]);
}
public function get_available_creditable_invoices($credit_note_id, $playground = false) {
$has_permission_view = staff_can('view', 'invoices');
$invoices_statuses_available_for_credits = invoices_statuses_available_for_credits();
$this->db->select('clientid');
$this->db->where('id', $credit_note_id);
$credit_note = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->row();
$this->db->select('' . db_prefix() . ($playground ? 'playground_' : '') . 'invoices.id as id, status, total, date, ' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.name as currency_name');
$this->db->where('clientid', $credit_note->clientid);
$this->db->where('status IN (' . implode(', ', $invoices_statuses_available_for_credits) . ')');
if (!$has_permission_view) {
$this->db->where('addedfrom', get_staff_user_id());
}
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'currencies', '' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'invoices.currency');
$invoices = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'invoices')->result_array();
foreach ($invoices as $key => $invoice) {
$invoices[$key]['total_left_to_pay'] = get_invoice_total_left_to_pay($invoice['id'], $invoice['total']);
}
return $invoices;
}
/**
* Send credit note to client
* @param mixed $id credit note id
* @param string $template email template to sent
* @param boolean $attachpdf attach credit note pdf or not
* @return boolean
*/
public function send_credit_note_to_client($id, $attachpdf = true, $cc = '', $manually = false, $playground = false) {
$credit_note = $this->get($id);
$number = format_credit_note_number($credit_note->id);
$sent = false;
$sent_to = $this->input->post('sent_to');
$this->load->model('clients_model');
if ($manually === true) {
$sent_to = [];
$contacts = $this->clients_model->get_contacts($credit_note->clientid, ['active' => 1, 'credit_note_emails' => 1]);
foreach ($contacts as $contact) {
array_push($sent_to, $contact['id']);
}
}
if (is_array($sent_to) && count($sent_to) > 0) {
if ($attachpdf) {
set_mailing_constant();
$pdf = credit_note_pdf($credit_note);
$attach = $pdf->Output($number . '.pdf', 'S');
}
$i = 0;
foreach ($sent_to as $contact_id) {
if ($contact_id != '') {
if (!empty($cc) && $i > 0) {
$cc = '';
}
$contact = $this->clients_model->get_contact($contact_id, ['active' => 1], [], $playground);
$template = mail_template('credit_note_send_to_customer', $credit_note, $contact, $cc);
if ($attachpdf) {
$template->add_attachment(['attachment' => $attach, 'filename' => str_replace('/', '-', $number . '.pdf'), 'type' => 'application/pdf', ]);
}
if ($template->send()) {
$sent = true;
}
}
$i++;
}
} else {
return false;
}
if ($sent) {
hooks()->do_action('credit_note_sent', $id);
return true;
}
return false;
}
/**
* Get credit note/s
* @param mixed $id credit note id
* @param array $where perform where
* @return mixed
*/
public function get($id = '', $where = [], $playground = false) {
$this->db->select('*,' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.id as currencyid, ' . db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes.id as id, ' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.name as currency_name');
$this->db->from(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'currencies', '' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes.currency', 'left');
$this->db->where($where);
$this->load->model('clients_model');
if (is_numeric($id)) {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes.id', $id);
$credit_note = $this->db->get()->row();
if ($credit_note) {
$credit_note->refunds = $this->get_refunds($id, $playground);
$credit_note->total_refunds = $this->total_refunds_by_credit_note($id, $playground);
$credit_note->applied_credits = $this->get_applied_credits($id, $playground);
$credit_note->remaining_credits = $this->total_remaining_credits_by_credit_note($id, $playground);
$credit_note->credits_used = $this->total_credits_used_by_credit_note($id, $playground);
$this->load->model('invoice_items_model');
$credit_note->items = $this->invoice_items_model->get_items_by_type('credit_note', $id, $playground);
$credit_note->client = $this->clients_model->get($credit_note->clientid, $playground);
if (!$credit_note->client) {
$credit_note->client = new stdClass();
$credit_note->client->company = $credit_note->deleted_customer_name;
}
$credit_note->attachments = $this->get_attachments($id, $playground);
}
return $credit_note;
}
$this->db->order_by('number,YEAR(date)', 'desc');
return $this->db->get()->result_array();
}
public function add($data, $playground = false) {
$save_and_send = isset($data['save_and_send']);
$data['prefix'] = get_option('credit_note_prefix', $playground);
$data['number_format'] = get_option('credit_note_number_format', $playground);
$data['datecreated'] = date('Y-m-d H:i:s');
$data['addedfrom'] = get_staff_user_id();
$items = [];
if (isset($data['newitems'])) {
$items = $data['newitems'];
unset($data['newitems']);
}
if (isset($data['custom_fields'])) {
$custom_fields = $data['custom_fields'];
unset($data['custom_fields']);
}
$data = $this->map_shipping_columns($data, $playground);
$hook = hooks()->apply_filters('before_create_credit_note', ['data' => $data, 'items' => $items]);
$data = $hook['data'];
$items = $hook['items'];
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
$this->save_formatted_number($insert_id, $playground);
// Update next credit note number in settings
$this->db->where('name', 'next_credit_note_number');
$this->db->set('value', 'value+1', false);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'options');
if (isset($custom_fields)) {
$this->load->model('custom_fields_model');
$this->custom_fields_model->handle_custom_fields_post($insert_id, $custom_fields, false, $playground);
}
$this->load->model('invoice_items_model');
foreach ($items as $key => $item) {
if ($itemid = $this->invoice_items_model->add_new_sales_item_post($item, $insert_id, 'credit_note', $playground)) {
$this->invoice_items_model->_maybe_insert_post_item_tax($itemid, $item, $insert_id, 'credit_note', $playground);
}
}
update_sales_total_tax_column($insert_id, 'credit_note', db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes');
log_activity('Credit Note Created [ID: ' . $insert_id . ']');
hooks()->do_action('after_create_credit_note', $insert_id);
if ($save_and_send === true) {
$this->send_credit_note_to_client($insert_id, true, '', true, $playground);
}
return $insert_id;
}
return false;
}
/**
* Update proposal
* @param mixed $data $_POST data
* @param mixed $id proposal id
* @return boolean
*/
public function update($data, $id, $playground = false) {
$affectedRows = 0;
$save_and_send = isset($data['save_and_send']);
$items = [];
if (isset($data['items'])) {
$items = $data['items'];
unset($data['items']);
}
$newitems = [];
if (isset($data['newitems'])) {
$newitems = $data['newitems'];
unset($data['newitems']);
}
if (isset($data['custom_fields'])) {
$custom_fields = $data['custom_fields'];
$this->load->model('custom_fields_model');
if ($this->custom_fields_model->handle_custom_fields_post($id, $custom_fields, false, $playground)) {
$affectedRows++;
}
unset($data['custom_fields']);
}
$data = $this->map_shipping_columns($data, $playground);
$hook = hooks()->apply_filters('before_update_credit_note', ['data' => $data, 'items' => $items, 'newitems' => $newitems, 'removed_items' => isset($data['removed_items']) ? $data['removed_items'] : [], ], $id);
$data = $hook['data'];
$items = $hook['items'];
$newitems = $hook['newitems'];
$data['removed_items'] = $hook['removed_items'];
// Delete items checked to be removed from database
foreach ($data['removed_items'] as $remove_item_id) {
if (handle_removed_sales_item_post($remove_item_id, 'credit_note', $playground)) {
$affectedRows++;
}
}
unset($data['removed_items']);
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes', $data);
if ($this->db->affected_rows() > 0) {
$this->save_formatted_number($id, $playground);
$affectedRows++;
}
$this->load->model('custom_fields_model');
$this->load->model('invoice_items_model');
foreach ($items as $key => $item) {
if (update_sales_item_post($item['itemid'], $item, $playground)) {
$affectedRows++;
}
if (isset($item['custom_fields'])) {
if ($this->custom_fields_model->handle_custom_fields_post($item['itemid'], $item['custom_fields'], false, $playground)) {
$affectedRows++;
}
}
if (!isset($item['taxname']) || (isset($item['taxname']) && count($item['taxname']) == 0)) {
if (delete_taxes_from_item($item['itemid'], 'credit_note', $playground)) {
$affectedRows++;
}
} else {
$item_taxes = get_credit_note_item_taxes($item['itemid'], $playground);
$_item_taxes_names = [];
foreach ($item_taxes as $_item_tax) {
array_push($_item_taxes_names, $_item_tax['taxname']);
}
$i = 0;
foreach ($_item_taxes_names as $_item_tax) {
if (!in_array($_item_tax, $item['taxname'])) {
$this->db->where('id', $item_taxes[$i]['id'])->delete(db_prefix() . ($playground ? 'playground_' : '') . 'item_tax');
if ($this->db->affected_rows() > 0) {
$affectedRows++;
}
}
$i++;
}
if ($this->invoice_items_model->_maybe_insert_post_item_tax($item['itemid'], $item, $id, 'credit_note', $playground)) {
$affectedRows++;
}
}
}
foreach ($newitems as $key => $item) {
if ($new_item_added = $this->invoice_items_model->add_new_sales_item_post($item, $id, 'credit_note', $playground)) {
$this->invoice_items_model->_maybe_insert_post_item_tax($new_item_added, $item, $id, 'credit_note', $playground);
$affectedRows++;
}
}
if ($save_and_send === true) {
$this->send_credit_note_to_client($id, true, '', true, $playground);
}
if ($affectedRows > 0) {
$this->update_credit_note_status($id, $playground);
update_sales_total_tax_column($id, 'credit_note', db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes');
}
if ($affectedRows > 0) {
log_activity('Credit Note Updated [ID:' . $id . ']');
hooks()->do_action('after_update_credit_note', $id);
return true;
}
return false;
}
/**
* Delete credit note attachment
* @param mixed $id attachmentid
* @return boolean
*/
public function delete_attachment($id, $playground = false) {
$this->load->model('misc_model');
$attachment = $this->misc_model->get_file($id, $playground);
$deleted = false;
if ($attachment) {
if (empty($attachment->external)) {
unlink($this->misc_model->get_upload_path_by_type('credit_note', $playground) . $attachment->rel_id . '/' . $attachment->file_name);
}
$this->db->where('id', $attachment->id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'files');
if ($this->db->affected_rows() > 0) {
$deleted = true;
log_activity('Credit Note Attachment Deleted [Credite Note: ' . format_credit_note_number($attachment->rel_id) . ']');
}
if (is_dir($this->misc_model->get_upload_path_by_type('credit_note', $playground) . $attachment->rel_id)) {
// Check if no attachments left, so we can delete the folder also
$other_attachments = list_files($this->misc_model->get_upload_path_by_type('credit_note', $playground) . $attachment->rel_id);
if (count($other_attachments) == 0) {
// okey only index.html so we can delete the folder also
delete_dir($this->misc_model->get_upload_path_by_type('credit_note', $playground) . $attachment->rel_id);
}
}
}
return $deleted;
}
public function get_attachments($credit_note_id, $playground = false) {
$this->db->where('rel_id', $credit_note_id);
$this->db->where('rel_type', 'credit_note');
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'files')->result_array();
}
/**
* Delete credit note
* @param mixed $id credit note id
* @return boolean
*/
public function delete($id, $simpleDelete = false, $playground = false) {
hooks()->do_action('before_credit_note_deleted', $id);
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes');
if ($this->db->affected_rows() > 0) {
$current_credit_note_number = get_option('next_credit_note_number');
if ($current_credit_note_number > 1 && $simpleDelete == false && is_last_credit_note($id)) {
// Decrement next credit note number
$this->db->where('name', 'next_credit_note_number');
$this->db->set('value', 'value-1', false);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'options');
}
$this->load->model('misc_model');
$this->misc_model->delete_tracked_emails($id, 'credit_note', $playground);
// Delete the custom field values
$this->db->where('relid IN (SELECT id from ' . db_prefix() . ($playground ? 'playground_' : '') . 'itemable WHERE rel_type="credit_note" AND rel_id="' . $this->db->escape_str($id) . '")');
$this->db->where('fieldto', 'items');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues');
$this->db->where('relid', $id);
$this->db->where('fieldto', 'credit_note');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues');
$this->db->where('credit_id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'credits');
$this->db->where('credit_note_id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds');
$this->db->where('rel_id', $id);
$this->db->where('rel_type', 'credit_note');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'itemable');
$this->db->where('rel_id', $id);
$this->db->where('rel_type', 'credit_note');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'item_tax');
$attachments = $this->get_attachments($id, $playground);
foreach ($attachments as $attachment) {
$this->delete_attachment($attachment['id'], $playground);
}
$this->db->where('rel_type', 'credit_note');
$this->db->where('rel_id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'reminders');
hooks()->do_action('after_credit_note_deleted', $id);
return true;
}
return false;
}
public function mark($id, $status, $playground = false) {
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes', ['status' => $status]);
if ($this->db->affected_rows() > 0) {
hooks()->do_action('credit_note_status_changed', $id, ['status' => $status]);
return true;
}
return false;
}
public function total_remaining_credits_by_customer($customer_id, $playground = false) {
$has_permission_view = staff_can('view', 'credit_notes');
$this->db->select('total,id');
$this->db->where('clientid', $customer_id);
$this->db->where('status', 1);
if (!$has_permission_view) {
$this->db->where('addedfrom', get_staff_user_id());
}
$credits = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->result_array();
$total = $this->calc_remaining_credits($credits, $playground);
return $total;
}
public function total_remaining_credits_by_credit_note($credit_note_id, $playground = false) {
$this->db->select('total,id');
$this->db->where('id', $credit_note_id);
$credits = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->result_array();
$total = $this->calc_remaining_credits($credits, $playground);
return $total;
}
private function calc_remaining_credits($credits, $playground = false) {
$total = 0;
$credits_ids = [];
$bcadd = function_exists('bcadd');
foreach ($credits as $credit) {
if ($bcadd) {
$total = bcadd($total, $credit['total'], get_decimal_places());
} else {
$total+= $credit['total'];
}
array_push($credits_ids, $credit['id']);
}
if (count($credits_ids) > 0) {
$this->db->where('credit_id IN (' . implode(', ', $credits_ids) . ')');
$applied_credits = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'credits')->result_array();
$bcsub = function_exists('bcsub');
foreach ($applied_credits as $credit) {
if ($bcsub) {
$total = bcsub($total, $credit['amount'], get_decimal_places());
} else {
$total-= $credit['amount'];
}
}
foreach ($credits_ids as $credit_note_id) {
$total_refunds_by_credit_note = $this->total_refunds_by_credit_note($credit_note_id, $playground) ? : 0;
if ($bcsub) {
$total = bcsub($total, $total_refunds_by_credit_note, get_decimal_places());
} else {
$total-= $total_refunds_by_credit_note;
}
}
}
return $total;
}
public function delete_applied_credit($id, $credit_id, $invoice_id, $playground = false) {
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'credits');
if ($this->db->affected_rows() > 0) {
$this->update_credit_note_status($credit_id, $playground);
update_invoice_status($invoice_id, $playground);
}
}
public function credit_note_from_invoice($invoice_id, $playground = false) {
$_invoice = $this->invoices_model->get($invoice_id, $playground);
$new_credit_note_data = [];
$new_credit_note_data['clientid'] = $_invoice->clientid;
$new_credit_note_data['number'] = get_option('next_credit_note_number');
$new_credit_note_data['date'] = _d(date('Y-m-d'));
$new_credit_note_data['show_quantity_as'] = $_invoice->show_quantity_as;
$new_credit_note_data['currency'] = $_invoice->currency;
$new_credit_note_data['subtotal'] = $_invoice->subtotal;
$new_credit_note_data['total'] = $_invoice->total;
$new_credit_note_data['adminnote'] = $_invoice->adminnote;
$new_credit_note_data['adjustment'] = $_invoice->adjustment;
$new_credit_note_data['discount_percent'] = $_invoice->discount_percent;
$new_credit_note_data['discount_total'] = $_invoice->discount_total;
$new_credit_note_data['discount_type'] = $_invoice->discount_type;
$new_credit_note_data['billing_street'] = clear_textarea_breaks($_invoice->billing_street);
$new_credit_note_data['billing_city'] = $_invoice->billing_city;
$new_credit_note_data['billing_state'] = $_invoice->billing_state;
$new_credit_note_data['billing_zip'] = $_invoice->billing_zip;
$new_credit_note_data['billing_country'] = $_invoice->billing_country;
$new_credit_note_data['shipping_street'] = clear_textarea_breaks($_invoice->shipping_street);
$new_credit_note_data['shipping_city'] = $_invoice->shipping_city;
$new_credit_note_data['shipping_state'] = $_invoice->shipping_state;
$new_credit_note_data['shipping_zip'] = $_invoice->shipping_zip;
$new_credit_note_data['shipping_country'] = $_invoice->shipping_country;
$new_credit_note_data['reference_no'] = format_invoice_number($_invoice->id);
if ($_invoice->include_shipping == 1) {
$new_credit_note_data['include_shipping'] = $_invoice->include_shipping;
}
$new_credit_note_data['show_shipping_on_credit_note'] = $_invoice->show_shipping_on_invoice;
$new_credit_note_data['clientnote'] = get_option('predefined_clientnote_credit_note');
$new_credit_note_data['terms'] = get_option('predefined_terms_credit_note');
$new_credit_note_data['adminnote'] = '';
$new_credit_note_data['newitems'] = [];
$this->load->model('custom_fields_items');
$custom_fields_items = $this->custom_fields_model->get_custom_fields('items', [], false, $playground);
$key = 1;
foreach ($_invoice->items as $item) {
$new_credit_note_data['newitems'][$key]['description'] = $item['description'];
$new_credit_note_data['newitems'][$key]['long_description'] = clear_textarea_breaks($item['long_description']);
$new_credit_note_data['newitems'][$key]['qty'] = $item['qty'];
$new_credit_note_data['newitems'][$key]['unit'] = $item['unit'];
$new_credit_note_data['newitems'][$key]['taxname'] = [];
$taxes = get_invoice_item_taxes($item['id']);
foreach ($taxes as $tax) {
// tax name is in format TAX1|10.00
array_push($new_credit_note_data['newitems'][$key]['taxname'], $tax['taxname']);
}
$new_credit_note_data['newitems'][$key]['rate'] = $item['rate'];
$new_credit_note_data['newitems'][$key]['order'] = $item['item_order'];
foreach ($custom_fields_items as $cf) {
$new_credit_note_data['newitems'][$key]['custom_fields']['items'][$cf['id']] = $this->custom_fields_model->get_custom_field_value($item['id'], $cf['id'], 'items', false, $playground);
if (!defined('COPY_CUSTOM_FIELDS_LIKE_HANDLE_POST')) {
define('COPY_CUSTOM_FIELDS_LIKE_HANDLE_POST', true);
}
}
$key++;
}
$id = $this->add($new_credit_note_data);
if ($id) {
if ($_invoice->status != Invoices_model::STATUS_PAID) {
if ($_invoice->status == Invoices_model::STATUS_DRAFT) {
$this->invoices_model->change_invoice_number_when_status_draft($invoice_id, $playground);
}
if ($this->apply_credits($id, ['invoice_id' => $invoice_id, 'amount' => $_invoice->total_left_to_pay])) {
update_invoice_status($invoice_id, true);
}
$this->invoices_model->save_formatted_number($invoice_id, $playground);
}
$invoiceNumber = format_invoice_number($_invoice->id);
$this->db->where('id', $id);
$this->db->update(($playground ? 'playground_' : '') . 'creditnotes', ['reference_no' => $invoiceNumber]);
log_activity('Created Credit Note From Invoice [Invoice: ' . $invoiceNumber . ', Credit Note: ' . format_credit_note_number($id) . ']');
hooks()->do_action('created_credit_note_from_invoice', ['invoice_id' => $invoice_id, 'credit_note_id' => $id]);
return $id;
}
return false;
}
public function create_refund($id, $data, $playground = false) {
if ($data['amount'] == 0) {
return false;
}
$data['note'] = trim($data['note']);
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds', ['created_at' => date('Y-m-d H:i:s'), 'credit_note_id' => $id, 'staff_id' => $data['staff_id'], 'refunded_on' => $data['refunded_on'], 'payment_mode' => $data['payment_mode'], 'amount' => $data['amount'], 'note' => nl2br($data['note']), ]);
$insert_id = $this->db->insert_id();
if ($insert_id) {
$this->update_credit_note_status($id, $playground);
hooks()->do_action('credit_note_refund_created', ['data' => $data, 'credit_note_id' => $id]);
}
return $insert_id;
}
public function edit_refund($id, $data, $playground = false) {
if ($data['amount'] == 0) {
return false;
}
$refund = $this->get_refund($id, $playground);
$data['note'] = trim($data['note']);
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds', ['refunded_on' => $data['refunded_on'], 'payment_mode' => $data['payment_mode'], 'amount' => $data['amount'], 'note' => nl2br($data['note']), ]);
$insert_id = $this->db->insert_id();
if ($this->db->affected_rows() > 0) {
$this->update_credit_note_status($refund->credit_note_id, $playground);
hooks()->do_action('credit_note_refund_updated', ['data' => $data, 'refund_id' => $refund->credit_note_id]);
}
return $insert_id;
}
public function get_refund($id, $playground = false) {
$this->db->where('id', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds')->row();
}
public function get_refunds($credit_note_id, $playground = false) {
$this->db->select(prefixed_table_fields_array(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds', true) . ',' . db_prefix() . ($playground ? 'playground_' : '') . 'payment_modes.id as payment_mode_id, ' . db_prefix() . ($playground ? 'playground_' : '') . 'payment_modes.name as payment_mode_name');
$this->db->where('credit_note_id', $credit_note_id);
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'payment_modes', db_prefix() . ($playground ? 'playground_' : '') . 'payment_modes.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds.payment_mode', 'left');
$this->db->order_by('refunded_on', 'desc');
$refunds = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds')->result_array();
$this->load->model('payment_modes_model');
$payment_gateways = $this->payment_modes_model->get_payment_gateways(true, $playground);
$i = 0;
foreach ($refunds as $refund) {
if (is_null($refund['payment_mode_id'])) {
foreach ($payment_gateways as $gateway) {
if ($refund['payment_mode'] == $gateway['id']) {
$refunds[$i]['payment_mode_id'] = $gateway['id'];
$refunds[$i]['payment_mode_name'] = $gateway['name'];
}
}
}
$i++;
}
return $refunds;
}
public function delete_refund($refund_id, $credit_note_id, $playground = false) {
$this->db->where('id', $refund_id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds');
if ($this->db->affected_rows() > 0) {
$this->update_credit_note_status($credit_note_id, $playground);
hooks()->do_action('credit_note_refund_deleted', ['refund_id' => $refund_id, 'credit_note_id' => $credit_note_id]);
return true;
}
return false;
}
private function total_refunds_by_credit_note($id, $playground = false) {
return sum_from_table(db_prefix() . ($playground ? 'playground_' : '') . 'creditnote_refunds', ['field' => 'amount', 'where' => ['credit_note_id' => $id], ]);
}
public function apply_credits($id, $data, $playground = false) {
if ($data['amount'] == 0) {
return false;
}
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'credits', ['invoice_id' => $data['invoice_id'], 'credit_id' => $id, 'staff_id' => get_staff_user_id(), 'date' => date('Y-m-d'), 'date_applied' => date('Y-m-d H:i:s'), 'amount' => $data['amount'], ]);
$insert_id = $this->db->insert_id();
if ($insert_id) {
$this->update_credit_note_status($id, $playground);
$invoice = $this->db->select('id,status')->where('id', $data['invoice_id'])->get(db_prefix() . ($playground ? 'playground_' : '') . 'invoices')->row();
if ($invoice->status == Invoices_model::STATUS_DRAFT) {
// update invoice number for invoice with draft - V2.7.2
$this->invoices_model->change_invoice_number_when_status_draft($invoice->id, $playground);
$this->invoices_model->save_formatted_number($invoice->id, $playground);
}
$this->db->select(db_prefix() . ($playground ? 'playground_' : '') . 'currencies.name as currency_name');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'currencies', '' . db_prefix() . ($playground ? 'playground_' : '') . 'currencies.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'invoices.currency');
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'invoices.id', $data['invoice_id']);
$invoice = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'invoices')->row();
$inv_number = format_invoice_number($data['invoice_id']);
$credit_note_number = format_credit_note_number($id);
$this->invoices_model->log_invoice_activity($data['invoice_id'], 'invoice_activity_applied_credits', false, serialize([app_format_money($data['amount'], $invoice->currency_name), $credit_note_number, ]), $playground);
hooks()->do_action('credits_applied', ['data' => $data, 'credit_note_id' => $id]);
log_activity('Credit Applied to Invoice [ Invoice: ' . $inv_number . ', Credit: ' . $credit_note_number . ' ]');
}
return $insert_id;
}
private function total_credits_used_by_credit_note($id, $playground = false) {
return sum_from_table(db_prefix() . ($playground ? 'playground_' : '') . 'credits', ['field' => 'amount', 'where' => ['credit_id' => $id], ]);
}
public function update_credit_note_status($id, $playground = false) {
$total_refunds_by_credit_note = $this->total_refunds_by_credit_note($id, $playground);
$total_credits_used = $this->total_credits_used_by_credit_note($id, $playground);
$status = 1;
// sum from table returns null if nothing found
if ($total_credits_used || $total_refunds_by_credit_note) {
$compare = $total_credits_used + $total_refunds_by_credit_note;
$this->db->select('total');
$this->db->where('id', $id);
$credit = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->row();
if ($credit) {
if (function_exists('bccomp')) {
if (bccomp($credit->total, $compare, get_decimal_places()) === 0) {
$status = 2;
}
} else {
if ($credit->total == $compare) {
$status = 2;
}
}
}
}
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes', ['status' => $status]);
return $this->db->affected_rows() > 0 ? true : false;
}
public function get_open_credits($customer_id, $playground = false) {
$has_permission_view = staff_can('view', 'credit_notes');
$this->db->where('status', 1);
$this->db->where('clientid', $customer_id);
if (!$has_permission_view) {
$this->db->where('addedfrom', get_staff_user_id());
}
$credits = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->result_array();
foreach ($credits as $key => $credit) {
$credits[$key]['available_credits'] = $this->calculate_available_credits($credit['id'], $credit['total'], $playground);
}
return $credits;
}
public function get_applied_invoice_credits($invoice_id, $playground = false) {
$this->db->order_by('date', 'desc');
$this->db->where('invoice_id', $invoice_id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'credits')->result_array();
}
public function get_applied_credits($credit_id, $playground = false) {
$this->db->where('credit_id', $credit_id);
$this->db->order_by('date', 'desc');
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'credits')->result_array();
}
private function calculate_available_credits($credit_id, $credit_amount = false, $playground = false) {
if ($credit_amount === false) {
$this->db->select('total')->from(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes')->where('id', $credit_id);
$credit_amount = $this->db->get()->row()->total;
}
$available_total = $credit_amount;
$bcsub = function_exists('bcsub');
$applied_credits = $this->get_applied_credits($credit_id, $playground);
foreach ($applied_credits as $credit) {
if ($bcsub) {
$available_total = bcsub($available_total, $credit['amount'], get_decimal_places());
} else {
$available_total-= $credit['amount'];
}
}
$total_refunds = $this->total_refunds_by_credit_note($credit_id, $playground);
if ($total_refunds) {
if ($bcsub) {
$available_total = bcsub($available_total, $total_refunds, get_decimal_places());
} else {
$available_total-= $total_refunds;
}
}
return $available_total;
}
public function save_formatted_number($id, $playground = false) {
$formattedNumber = format_credit_note_number($id);
$this->db->where('id', $id);
$this->db->update(($playground ? 'playground_' : '') . 'creditnotes', ['formatted_number' => $formattedNumber]);
}
public function get_credits_years($playground = false) {
return $this->db->query('SELECT DISTINCT(YEAR(date)) as year FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes ORDER BY year DESC')->result_array();
}
private function map_shipping_columns($data, $playground = false) {
if (!isset($data['include_shipping'])) {
foreach ($this->shipping_fields as $_s_field) {
if (isset($data[$_s_field])) {
$data[$_s_field] = null;
}
}
$data['show_shipping_on_credit_note'] = 1;
$data['include_shipping'] = 0;
} else {
$data['include_shipping'] = 1;
// set by default for the next time to be checked
if (isset($data['show_shipping_on_credit_note']) && ($data['show_shipping_on_credit_note'] == 1 || $data['show_shipping_on_credit_note'] == 'on')) {
$data['show_shipping_on_credit_note'] = 1;
} else {
$data['show_shipping_on_credit_note'] = 0;
}
}
return $data;
}
}