diff --git a/application/config/constants.php b/application/config/constants.php index 9136d470b..c16578918 100644 --- a/application/config/constants.php +++ b/application/config/constants.php @@ -144,3 +144,13 @@ define('SALE_TYPE_RETURN', 4); define('PERCENT', 0); define('FIXED', 1); + +define('PRICE_MODE_STANDARD', 0); +define('PRICE_MODE_KIT', 1); + +define('PAYMENT_TYPE_UNASSIGNED', '--'); + +define('CASH_ADJUSTMENT_TRUE', 1); +define('CASH_ADJUSTMENT_FALSE', 0); +define('CASH_MODE_TRUE', 1); +define('CASH_MODE_FALSE', 0); diff --git a/application/controllers/Sales.php b/application/controllers/Sales.php index 7ed3a8831..2d1f65fa8 100644 --- a/application/controllers/Sales.php +++ b/application/controllers/Sales.php @@ -2,10 +2,6 @@ require_once("Secure_Controller.php"); -define('PRICE_MODE_STANDARD', 0); -define('PRICE_MODE_KIT', 1); -define('PAYMENT_TYPE_UNASSIGNED', '--'); - class Sales extends Secure_Controller { public function __construct() @@ -263,7 +259,7 @@ class Sales extends Secure_Controller $data = array(); $payment_type = $this->input->post('payment_type'); - if($payment_type != $this->lang->line('sales_giftcard')) + if($payment_type !== $this->lang->line('sales_giftcard')) { $this->form_validation->set_rules('amount_tendered', 'lang:sales_amount_tendered', 'trim|required|callback_numeric'); } @@ -274,7 +270,7 @@ class Sales extends Secure_Controller if($this->form_validation->run() == FALSE) { - if($payment_type == $this->lang->line('sales_giftcard')) + if($payment_type === $this->lang->line('sales_giftcard')) { $data['error'] = $this->lang->line('sales_must_enter_numeric_giftcard'); } @@ -285,7 +281,7 @@ class Sales extends Secure_Controller } else { - if($payment_type == $this->lang->line('sales_giftcard')) + if($payment_type === $this->lang->line('sales_giftcard')) { // in case of giftcard payment the register input amount_tendered becomes the giftcard number $giftcard_num = $this->input->post('amount_tendered'); @@ -316,7 +312,7 @@ class Sales extends Secure_Controller $this->sale_lib->add_payment($payment_type, $amount_tendered); } } - elseif($payment_type == $this->lang->line('sales_rewards')) + elseif($payment_type === $this->lang->line('sales_rewards')) { $customer_id = $this->sale_lib->get_customer(); $package_id = $this->Customer->get_info($customer_id)->package_id; @@ -348,6 +344,20 @@ class Sales extends Secure_Controller } } } + elseif($payment_type === $this->lang->line('sales_cash')) + { + $amount_due = $this->sale_lib->get_total(); + $sales_total = $this->sale_lib->get_total(FALSE); + + $amount_tendered = $this->input->post('amount_tendered'); + $this->sale_lib->add_payment($payment_type, $amount_tendered); + $cash_adjustment_amount = $amount_due - $sales_total; + if($cash_adjustment_amount <> 0) + { + $this->session->set_userdata('cash_mode', CASH_MODE_TRUE); + $this->sale_lib->add_payment($this->lang->line('sales_cash_adjustment'), $cash_adjustment_amount, CASH_ADJUSTMENT_TRUE); + } + } else { $amount_tendered = $this->input->post('amount_tendered'); @@ -571,22 +581,22 @@ class Sales extends Secure_Controller $data['payments_total'] = $totals['payment_total']; $data['payments_cover_total'] = $totals['payments_cover_total']; $data['cash_rounding'] = $this->session->userdata('cash_rounding'); + $data['cash_mode'] = $this->session->userdata('cash_mode'); $data['prediscount_subtotal'] = $totals['prediscount_subtotal']; $data['cash_total'] = $totals['cash_total']; $data['non_cash_total'] = $totals['total']; $data['cash_amount_due'] = $totals['cash_amount_due']; $data['non_cash_amount_due'] = $totals['amount_due']; - if($data['cash_rounding']) + if($data['cash_mode']) { - $data['total'] = $totals['cash_total']; $data['amount_due'] = $totals['cash_amount_due']; } else { - $data['total'] = $totals['total']; $data['amount_due'] = $totals['amount_due']; } + $data['amount_change'] = $data['amount_due'] * -1; if($data['amount_change'] > 0) @@ -609,6 +619,7 @@ class Sales extends Secure_Controller if($this->sale_lib->is_invoice_mode()) { $invoice_format = $this->config->item('sales_invoice_format'); + // generate final invoice number (if using the invoice in sales by receipt mode then the invoice number can be manually entered or altered in some way if(!empty($invoice_format) && $invoice_number == NULL) { @@ -787,6 +798,7 @@ class Sales extends Secure_Controller // generate email attachment: invoice in pdf format $html = $this->load->view("sales/" . $type . "_email", $sale_data, TRUE); + // load pdf helper $this->load->helper(array('dompdf', 'file')); $filename = sys_get_temp_dir() . '/' . $this->lang->line("sales_" . $type) . '-' . str_replace('/', '-', $number) . '.pdf'; @@ -903,7 +915,9 @@ class Sales extends Secure_Controller private function _load_sale_data($sale_id) { $this->sale_lib->clear_all(); - $this->sale_lib->reset_cash_flags(); + $cash_rounding = $this->sale_lib->reset_cash_rounding(); + $data['cash_rounding'] = $cash_rounding; + $sale_info = $this->Sale->get_info($sale_id)->row_array(); $this->sale_lib->copy_entire_sale($sale_id); $data = array(); @@ -922,18 +936,18 @@ class Sales extends Secure_Controller // Returns 'subtotal', 'total', 'cash_total', 'payment_total', 'amount_due', 'cash_amount_due', 'payments_cover_total' $totals = $this->sale_lib->get_totals($tax_details[0]); + $this->session->set_userdata('cash_adjustment_amount', $totals['cash_adjustment_amount']); $data['subtotal'] = $totals['subtotal']; - $data['total'] = $totals['total']; $data['payments_total'] = $totals['payment_total']; $data['payments_cover_total'] = $totals['payments_cover_total']; - $data['cash_rounding'] = $this->session->userdata('cash_rounding'); + $data['cash_mode'] = $this->session->userdata('cash_mode'); $data['prediscount_subtotal'] = $totals['prediscount_subtotal']; $data['cash_total'] = $totals['cash_total']; $data['non_cash_total'] = $totals['total']; $data['cash_amount_due'] = $totals['cash_amount_due']; $data['non_cash_amount_due'] = $totals['amount_due']; - if($this->session->userdata('cash_rounding')) + if($data['cash_mode'] && ($data['selected_payment_type'] === $this->lang->line('sales_cash') || $data['payments_total'] > 0)) { $data['total'] = $totals['cash_total']; $data['amount_due'] = $totals['cash_amount_due']; @@ -943,6 +957,7 @@ class Sales extends Secure_Controller $data['total'] = $totals['total']; $data['amount_due'] = $totals['amount_due']; } + $data['amount_change'] = $data['amount_due'] * -1; $employee_info = $this->Employee->get_info($this->sale_lib->get_employee()); @@ -1013,6 +1028,11 @@ class Sales extends Secure_Controller $sale_id = -1; $this->session->set_userdata('sale_id', -1); } + $cash_rounding = $this->sale_lib->reset_cash_rounding(); + + // cash_rounding indicates only that the site is configured for cash rounding + $data['cash_rounding'] = $cash_rounding; + $data['cart'] = $this->sale_lib->get_cart(); $customer_info = $this->_load_customer_data($this->sale_lib->get_customer(), $data, TRUE); @@ -1027,22 +1047,29 @@ class Sales extends Secure_Controller $data['taxes'] = $tax_details[0]; $data['discount'] = $this->sale_lib->get_discount(); $data['payments'] = $this->sale_lib->get_payments(); + // Returns 'subtotal', 'total', 'cash_total', 'payment_total', 'amount_due', 'cash_amount_due', 'payments_cover_total' $totals = $this->sale_lib->get_totals($tax_details[0]); + $data['item_count'] = $totals['item_count']; $data['total_units'] = $totals['total_units']; $data['subtotal'] = $totals['subtotal']; $data['total'] = $totals['total']; $data['payments_total'] = $totals['payment_total']; $data['payments_cover_total'] = $totals['payments_cover_total']; - $data['cash_rounding'] = $this->session->userdata('cash_rounding'); + + // cash_mode indicates whether this sale is going to be processed using cash_rounding + $cash_mode = $this->session->userdata('cash_mode'); + $data['cash_mode'] = $cash_mode; $data['prediscount_subtotal'] = $totals['prediscount_subtotal']; $data['cash_total'] = $totals['cash_total']; $data['non_cash_total'] = $totals['total']; $data['cash_amount_due'] = $totals['cash_amount_due']; $data['non_cash_amount_due'] = $totals['amount_due']; - if($data['cash_rounding']) + $data['selected_payment_type'] = $this->sale_lib->get_payment_type(); + + if($data['cash_mode'] && ($data['selected_payment_type'] == $this->lang->line('sales_cash') || $data['payments_total'] > 0)) { $data['total'] = $totals['cash_total']; $data['amount_due'] = $totals['cash_amount_due']; @@ -1052,11 +1079,12 @@ class Sales extends Secure_Controller $data['total'] = $totals['total']; $data['amount_due'] = $totals['amount_due']; } + $data['amount_change'] = $data['amount_due'] * -1; $data['comment'] = $this->sale_lib->get_comment(); $data['email_receipt'] = $this->sale_lib->is_email_receipt(); - $data['selected_payment_type'] = $this->sale_lib->get_payment_type(); + if($customer_info && $this->config->item('customer_reward_enable') == TRUE) { $data['payment_options'] = $this->Sale->get_payment_options(TRUE, TRUE); @@ -1143,8 +1171,8 @@ class Sales extends Secure_Controller $data['selected_employee_id'] = $sale_info['employee_id']; $data['selected_employee_name'] = $this->xss_clean($employee_info->first_name . ' ' . $employee_info->last_name); $data['sale_info'] = $sale_info; - $balance_due = $sale_info['amount_due'] - $sale_info['amount_tendered']; - if($balance_due < 0) + $balance_due = round($sale_info['amount_due'] - $sale_info['amount_tendered'] + $sale_info['cash_refund'], totals_decimals(), PHP_ROUND_HALF_UP); + if(!$this->sale_lib->reset_cash_rounding() && $balance_due < 0) { $balance_due = 0; } @@ -1165,12 +1193,19 @@ class Sales extends Secure_Controller $data['balance_due'] = $balance_due != 0; // don't allow gift card to be a payment option in a sale transaction edit because it's a complex change - $new_payment_options = $this->xss_clean($this->Sale->get_payment_options(FALSE)); - $data['payment_options'] = $new_payment_options; + $payment_options = $this->Sale->get_payment_options(FALSE); + + if($this->sale_lib->reset_cash_rounding()) + { + $payment_options[$this->lang->line('sales_cash_adjustment')] = $this->lang->line('sales_cash_adjustment'); + } + + $data['payment_options'] = $this->xss_clean($payment_options); // Set up a slightly modified list of payment types for new payment entry - $new_payment_options["--"] = $this->lang->line('common_none_selected_text'); - $data['new_payment_options'] = $new_payment_options; + $payment_options["--"] = $this->lang->line('common_none_selected_text'); + + $data['new_payment_options'] = $this->xss_clean($payment_options); $this->load->view('sales/form', $data); } @@ -1248,6 +1283,7 @@ class Sales extends Secure_Controller // In order to maintain tradition the only element that can change on prior payments is the payment type $payments = array(); + $amount_tendered = 0; $number_of_payments = $this->input->post('number_of_payments'); for($i = 0; $i < $number_of_payments; ++$i) { @@ -1257,6 +1293,20 @@ class Sales extends Secure_Controller $refund_type = $this->input->post('refund_type_' . $i); $cash_refund = $this->input->post('refund_amount_' . $i); + if($payment_type == $this->lang->line('sales_cash_adjustment')) + { + $cash_adjustment = CASH_ADJUSTMENT_TRUE; + } + else + { + $cash_adjustment = CASH_ADJUSTMENT_FALSE; + } + + if(!$cash_adjustment) + { + $amount_tendered += $payment_amount - $cash_refund; + } + // if the refund is not cash ... if(empty(strstr($refund_type, $this->lang->line('sales_cash')))) { @@ -1270,11 +1320,8 @@ class Sales extends Secure_Controller } } - // To maintain tradition we will also delete any payments with 0 amount - // assuming these are mistakes introduced at sale time. - // This is now done in models/Sale.php - $payments[] = array('payment_id' => $payment_id, 'payment_type' => $payment_type, 'payment_amount' => $payment_amount, 'cash_refund' => $cash_refund, 'employee_id' => $employee_id); + $payments[] = array('payment_id' => $payment_id, 'payment_type' => $payment_type, 'payment_amount' => $payment_amount, 'cash_refund' => $cash_refund, 'cash_adjustment' => $cash_adjustment, 'employee_id' => $employee_id); } $payment_id = -1; @@ -1283,7 +1330,24 @@ class Sales extends Secure_Controller if($payment_type != PAYMENT_TYPE_UNASSIGNED && $payment_amount <> 0) { - $payments[] = array('payment_id' => $payment_id, 'payment_type' => $payment_type, 'payment_amount' => $payment_amount, 'cash_refund' => 0.00, 'employee_id' => $employee_id); + $cash_refund = 0; + if($payment_type == $this->lang->line('sales_cash_adjustment')) + { + $cash_adjustment = CASH_ADJUSTMENT_TRUE; + } + else + { + $cash_adjustment = CASH_ADJUSTMENT_FALSE; + $amount_tendered += $payment_amount; + $sale_info = $this->Sale->get_info($sale_id)->row_array(); + + if($amount_tendered > $sale_info['amount_due']) + { + $cash_refund = $amount_tendered - $sale_info['amount_due']; + } + } + + $payments[] = array('payment_id' => $payment_id, 'payment_type' => $payment_type, 'payment_amount' => $payment_amount, 'cash_refund' => $cash_refund, 'cash_adjustment' => $cash_adjustment, 'employee_id' => $employee_id); } $this->Inventory->update('POS '.$sale_id, ['trans_date' => $sale_time]); diff --git a/application/language/en-GB/sales_lang.php b/application/language/en-GB/sales_lang.php index 5c93d9df6..5cd042bd3 100644 --- a/application/language/en-GB/sales_lang.php +++ b/application/language/en-GB/sales_lang.php @@ -10,6 +10,7 @@ $lang["sales_amount_tendered"] = "Amount Tendered"; $lang["sales_authorized_signature"] = "Authorised Signature"; $lang["sales_cancel_sale"] = "Cancel"; $lang["sales_cash"] = "Cash"; +$lang["sales_cash_adjustment"] = "Cash Adjustment"; $lang["sales_cash_deposit"] = "Cash Deposit"; $lang["sales_cash_filter"] = "Cash"; $lang["sales_change_due"] = "Change Due"; diff --git a/application/language/en-US/sales_lang.php b/application/language/en-US/sales_lang.php index a3a293a4b..09a09e809 100644 --- a/application/language/en-US/sales_lang.php +++ b/application/language/en-US/sales_lang.php @@ -10,6 +10,7 @@ $lang["sales_amount_tendered"] = "Amount Tendered"; $lang["sales_authorized_signature"] = "Authorized Signature"; $lang["sales_cancel_sale"] = "Cancel"; $lang["sales_cash"] = "Cash"; +$lang["sales_cash_adjustment"] = "Cash Adjustment"; $lang["sales_cash_deposit"] = "Cash Deposit"; $lang["sales_cash_filter"] = "Cash"; $lang["sales_change_due"] = "Change Due"; diff --git a/application/libraries/Sale_lib.php b/application/libraries/Sale_lib.php index ebf372350..567674ec7 100644 --- a/application/libraries/Sale_lib.php +++ b/application/libraries/Sale_lib.php @@ -366,8 +366,14 @@ class Sale_lib $this->CI->session->set_userdata('sales_payments', $payments_data); } - // Multiple Payments - public function add_payment($payment_id, $payment_amount) + /** + * Adds a new payment to the payments array or updates an existing one. + * It will also disable cash_mode if an non-qualifying payment type is added. + * @param $payment_id + * @param $payment_amount + * @param int $cash_adjustment + */ + public function add_payment($payment_id, $payment_amount, $cash_adjustment = CASH_ADJUSTMENT_FALSE) { $payments = $this->get_payments(); if(isset($payments[$payment_id])) @@ -378,11 +384,20 @@ class Sale_lib else { //add to existing array - $payment = array($payment_id => array('payment_type' => $payment_id, 'payment_amount' => $payment_amount, 'cash_refund' => 0)); + $payment = array($payment_id => array('payment_type' => $payment_id, 'payment_amount' => $payment_amount, + 'cash_refund' => 0, 'cash_adjustment' => $cash_adjustment)); $payments += $payment; } + if($this->CI->session->userdata('cash_mode')) + { + if($this->CI->session->userdata('cash_rounding') && $payment_id != $this->CI->lang->line('sales_cash') && $payment_id != $this->CI->lang->line('sales_cash_adjustment')) + { + $this->CI->session->set_userdata('cash_mode', CASH_MODE_FALSE); + } + } + $this->set_payments($payments); } @@ -402,11 +417,28 @@ class Sale_lib return FALSE; } - // Multiple Payments + /** + * Delete the selected payment from the payment array and if cash rounding is enabled + * and the payment type is one of the cash types then automatically delete the other + * @param $payment_id + */ public function delete_payment($payment_id) { $payments = $this->get_payments(); - unset($payments[urldecode($payment_id)]); + $decoded_payment_id = urldecode($payment_id); + unset($payments[$decoded_payment_id]); + $cash_rounding = $this->reset_cash_rounding(); + if($cash_rounding) + { + if($decoded_payment_id == $this->CI->lang->line('sales_cash')) + { + unset($payments[$this->CI->lang->line('sales_cash_adjustment')]); + } + if($decoded_payment_id == $this->CI->lang->line('sales_cash_adjustment')) + { + unset($payments[$this->CI->lang->line('sales_cash')]); + } + } $this->set_payments($payments); } @@ -416,18 +448,30 @@ class Sale_lib $this->CI->session->unset_userdata('sales_payments'); } - // Multiple Payments + /** + * Retrieve the total payments made, excluding any cash adjustments + * and establish if cash_mode is in play + */ public function get_payments_total() { $subtotal = 0.0; - $this->reset_cash_flags(); + $cash_mode_eligible = CASH_MODE_TRUE; + foreach($this->get_payments() as $payments) { - $subtotal = bcadd($payments['payment_amount'], $subtotal); - if($this->CI->session->userdata('cash_rounding') && $this->CI->lang->line('sales_cash') != $payments['payment_type']) + if(!$payments['cash_adjustment']) { - $this->CI->session->set_userdata('cash_rounding', 0); + $subtotal = bcadd($payments['payment_amount'], $subtotal); } + if($this->CI->lang->line('sales_cash') != $payments['payment_type'] && $this->CI->lang->line('sales_cash_adjustment') != $payments['payment_type']) + { + $cash_mode_eligible = CASH_MODE_FALSE; + } + } + + if($cash_mode_eligible && $this->CI->session->userdata('cash_rounding')) + { + $this->CI->session->set_userdata('cash_mode', CASH_MODE_TRUE); } return $subtotal; @@ -435,12 +479,10 @@ class Sale_lib /** * Returns 'subtotal', 'total', 'cash_total', 'payment_total', 'amount_due', 'cash_amount_due', 'paid_in_full' - * 'subtotal', 'discounted_subtotal', 'tax_exclusive_subtotal', 'item_count', 'total_units' + * 'subtotal', 'discounted_subtotal', 'tax_exclusive_subtotal', 'item_count', 'total_units', 'cash_adjustment_amount' */ public function get_totals($taxes) { - $cash_rounding = $this->CI->session->userdata('cash_rounding'); - $totals = array(); $prediscount_subtotal = 0.0; @@ -489,6 +531,11 @@ class Sale_lib $totals['total'] = $total; $totals['tax_total'] = $sales_tax; + $payment_total = $this->get_payments_total(); + $totals['payment_total'] = $payment_total; + $cash_rounding = $this->CI->session->userdata('cash_rounding'); + $cash_mode = $this->CI->session->userdata('cash_mode'); + if($cash_rounding) { $cash_total = $this->check_for_cash_rounding($total); @@ -500,16 +547,13 @@ class Sale_lib $totals['cash_total'] = $cash_total; } - $payment_total = $this->get_payments_total(); - $totals['payment_total'] = $payment_total; - $amount_due = bcsub($total, $payment_total); $totals['amount_due'] = $amount_due; $cash_amount_due = bcsub($cash_total, $payment_total); $totals['cash_amount_due'] = $cash_amount_due; - if($cash_rounding) + if($cash_mode) { $current_due = $cash_amount_due; } @@ -532,6 +576,14 @@ class Sale_lib $totals['item_count'] = $item_count; $totals['total_units'] = $total_units; + $totals['cash_adjustment_amount'] = 0; + + if($totals['payments_cover_total']) + { + $totals['cash_adjustment_amount'] = round($cash_total - $totals['total'], totals_decimals(), PHP_ROUND_HALF_UP); + } + + $cash_mode = $this->CI->session->userdata('cash_mode'); return $totals; } @@ -1006,9 +1058,34 @@ class Sale_lib $this->add_item($row->item_id, $row->quantity_purchased, $row->item_location, $row->discount, $row->discount_type, PRICE_MODE_STANDARD, NULL, NULL, $row->item_unit_price, $row->description, $row->serialnumber, $sale_id, TRUE, $row->print_option); } + $this->CI->session->set_userdata('cash_mode', CASH_MODE_FALSE); + + // Establish cash_mode for this sale by inspecting the payments + if($this->CI->session->userdata('cash_rounding')) + { + $cash_types_only = true; + foreach($this->CI->Sale->get_sale_payments($sale_id)->result() as $row) + { + if($row->payment_type != $this->CI->lang->line('sales_cash') && $row->payment_type != $this->CI->lang->line('sales_cash_adjustment')) + { + $cash_types_only = FALSE; + } + + } + if($cash_types_only) + { + $this->CI->session->set_userdata('cash_mode', CASH_MODE_TRUE); + } + else + { + $this->CI->session->set_userdata('cash_mode', CASH_MODE_FALSE); + } + } + + // Now load payments foreach($this->CI->Sale->get_sale_payments($sale_id)->result() as $row) { - $this->add_payment($row->payment_type, $row->payment_amount); + $this->add_payment($row->payment_type, $row->payment_amount, $row->cash_adjustment); } $this->set_customer($this->CI->Sale->get_customer($sale_id)->person_id); @@ -1051,18 +1128,13 @@ class Sale_lib $this->CI->session->unset_userdata('payment_type'); } - public function reset_cash_flags() + /** + * Determines if cash rounding should be a consideration for this site + * It also set resets the cash mode to disabled which will then be re-evaluated when + * retrieving payments. + */ + public function reset_cash_rounding() { - if($this->CI->config->item('payment_options_order') != 'cashdebitcredit') - { - $cash_mode = 1; - } - else - { - $cash_mode = 0; - } - $this->CI->session->set_userdata('cash_mode', $cash_mode); - $cash_rounding_code = $this->CI->config->item('cash_rounding_code'); if(cash_decimals() < totals_decimals() || $cash_rounding_code == Rounding_mode::HALF_FIVE) @@ -1074,6 +1146,9 @@ class Sale_lib $cash_rounding = 0; } $this->CI->session->set_userdata('cash_rounding', $cash_rounding); + $this->CI->session->set_userdata('cash_mode', CASH_MODE_FALSE); + + return $cash_rounding; } public function is_customer_taxable() @@ -1234,11 +1309,16 @@ class Sale_lib return $subtotal; } - public function get_total() + /** + * Calculates the total sales amount with the default option to include cash rounding + * @param bool $include_cash_rounding + * @return float|int|string + */ + public function get_total($include_cash_rounding = TRUE) { $total = $this->calculate_subtotal(TRUE); - $cash_rounding = $this->CI->session->userdata('cash_rounding'); + $cash_mode = $this->CI->session->userdata('cash_mode'); if(!$this->CI->config->item('tax_included')) { @@ -1249,7 +1329,7 @@ class Sale_lib } } - if($cash_rounding) + if($include_cash_rounding && $cash_mode) { $total = $this->check_for_cash_rounding($total); } diff --git a/application/migrations/20201108100000_cashrounding.php b/application/migrations/20201108100000_cashrounding.php new file mode 100644 index 000000000..aafc6a155 --- /dev/null +++ b/application/migrations/20201108100000_cashrounding.php @@ -0,0 +1,20 @@ +db->query('ALTER TABLE ' . $this->db->dbprefix('sales_payments') . ' ADD COLUMN `cash_adjustment` tinyint NOT NULL DEFAULT 0 AFTER `cash_refund`'); + } + + public function down() + { + $this->db->query('ALTER TABLE ' . $this->db->dbprefix('sales_payments') . ' DROP COLUMN `cash_adjustment`'); + } +} +?> diff --git a/application/models/Sale.php b/application/models/Sale.php index 5e9c459c9..a67391084 100644 --- a/application/models/Sale.php +++ b/application/models/Sale.php @@ -15,7 +15,9 @@ class Sale extends CI_Model ' (PRIMARY KEY(sale_id), INDEX(sale_id)) ( SELECT payments.sale_id AS sale_id, - IFNULL(SUM(payments.payment_amount), 0) AS sale_payment_amount, + SUM(CASE WHEN payments.cash_adjustment = 0 THEN payments.payment_amount ELSE 0 END) AS sale_payment_amount, + SUM(CASE WHEN payments.cash_adjustment = 1 THEN payments.payment_amount ELSE 0 END) AS sale_cash_adjustment, + SUM(payments.cash_refund) AS sale_cash_refund, GROUP_CONCAT(CONCAT(payments.payment_type, " ", (payments.payment_amount - payments.cash_refund)) SEPARATOR ", ") AS payment_type FROM ' . $this->db->dbprefix('sales_payments') . ' AS payments INNER JOIN ' . $this->db->dbprefix('sales') . ' AS sales @@ -29,18 +31,23 @@ class Sale extends CI_Model $sale_price = 'CASE WHEN sales_items.discount_type = ' . PERCENT . ' THEN sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount / 100) ELSE sales_items.item_unit_price * sales_items.quantity_purchased - sales_items.discount END'; - $sales_tax = 'ROUND(IFNULL(SUM(sales_items_taxes.sales_tax), 0), ' . $decimals . ')'; - $internal_tax = 'ROUND(IFNULL(SUM(sales_items_taxes.internal_tax), 0), ' . $decimals . ')'; + $sales_tax = "ROUND(IFNULL(SUM(sales_items_taxes.sales_tax), 0), $decimals)"; + $internal_tax = "ROUND(IFNULL(SUM(sales_items_taxes.internal_tax), 0), $decimals)"; + $cash_adjustment = 'IFNULL(SUM(payments.sale_cash_adjustment), 0)'; if($this->config->item('tax_included')) { - $sale_total = 'ROUND(SUM(' . $sale_price . '),' . $decimals . ')'; + $sale_total = "ROUND(SUM($sale_price), $decimals) + $cash_adjustment"; +// $sale_subtotal = "$sale_total - $internal_tax"; } else { - $sale_total = 'ROUND(SUM(' . $sale_price . '),' . $decimals . ') + ' . $sales_tax; +// $sale_subtotal = "ROUND(SUM($sale_price), $decimals) - $internal_tax - $cash_adjustment"; + $sale_total = "ROUND(SUM($sale_price), $decimals) + $sales_tax + $cash_adjustment"; } + + // create a temporary table to contain all the sum of taxes per sale item $this->db->query('CREATE TEMPORARY TABLE IF NOT EXISTS ' . $this->db->dbprefix('sales_items_taxes_temp') . ' (INDEX(sale_id), INDEX(item_id)) ENGINE=MEMORY @@ -75,10 +82,12 @@ class Sale extends CI_Model MAX(customer_p.last_name) AS last_name, MAX(customer_p.email) AS email, MAX(customer_p.comments) AS comments, + MAX(IFNULL(payments.sale_cash_adjustment, 0)) AS cash_adjustment, + MAX(IFNULL(payments.sale_cash_refund, 0)) AS cash_refund, ' . " $sale_total AS amount_due, MAX(IFNULL(payments.sale_payment_amount, 0)) AS amount_tendered, - MAX(IFNULL(payments.sale_payment_amount,0)) - $sale_total AS change_due, + (MAX(payments.sale_payment_amount)) - ($sale_total) AS change_due, " . ' MAX(payments.payment_type) AS payment_type '); @@ -131,7 +140,8 @@ class Sale extends CI_Model ' (PRIMARY KEY(sale_id), INDEX(sale_id)) ( SELECT payments.sale_id AS sale_id, - IFNULL(SUM(payments.payment_amount), 0) AS sale_payment_amount, + SUM(CASE WHEN payments.cash_adjustment = 0 THEN payments.payment_amount ELSE 0 END) AS sale_payment_amount, + SUM(CASE WHEN payments.cash_adjustment = 1 THEN payments.payment_amount ELSE 0 END) AS sale_cash_adjustment, GROUP_CONCAT(CONCAT(payments.payment_type, " ", (payments.payment_amount - payments.cash_refund)) SEPARATOR ", ") AS payment_type FROM ' . $this->db->dbprefix('sales_payments') . ' AS payments INNER JOIN ' . $this->db->dbprefix('sales') . ' AS sales @@ -149,9 +159,10 @@ class Sale extends CI_Model $tax = 'IFNULL(SUM(sales_items_taxes.tax), 0)'; $sales_tax = 'IFNULL(SUM(sales_items_taxes.sales_tax), 0)'; $internal_tax = 'IFNULL(SUM(sales_items_taxes.internal_tax), 0)'; + $cash_adjustment = 'IFNULL(SUM(payments.sale_cash_adjustment), 0)'; - $sale_subtotal = 'ROUND(SUM(' . $sale_price . '), ' . $decimals . ') - ' . $internal_tax; - $sale_total = 'ROUND(SUM(' . $sale_price . '), ' . $decimals . ') + ' . $sales_tax; + $sale_subtotal = "ROUND(SUM($sale_price), $decimals) - $internal_tax"; + $sale_total = "ROUND(SUM($sale_price), $decimals) + $sales_tax + $cash_adjustment"; // create a temporary table to contain all the sum of taxes per sale item $this->db->query('CREATE TEMPORARY TABLE IF NOT EXISTS ' . $this->db->dbprefix('sales_items_taxes_temp') . @@ -197,7 +208,7 @@ class Sale extends CI_Model ($sale_total - $sale_cost) AS profit, $sale_total AS amount_due, MAX(payments.sale_payment_amount) AS amount_tendered, - (MAX(payments.sale_payment_amount) - ($sale_total)) AS change_due, + (MAX(payments.sale_payment_amount)) - ($sale_total) AS change_due, " . ' MAX(payments.payment_type) AS payment_type '); @@ -550,17 +561,19 @@ class Sale extends CI_Model $payment_type = $payment['payment_type']; $payment_amount = $payment['payment_amount']; $cash_refund = $payment['cash_refund']; + $cash_adjustment = $payment['cash_adjustment']; $employee_id = $payment['employee_id']; if($payment_id == -1 && $payment_amount != 0) { // Add a new payment transaction $sales_payments_data = array( - 'sale_id' => $sale_id, - 'payment_type' => $payment_type, - 'payment_amount' => $payment_amount, - 'cash_refund' => $cash_refund, - 'employee_id' => $employee_id + 'sale_id' => $sale_id, + 'payment_type' => $payment_type, + 'payment_amount' => $payment_amount, + 'cash_refund' => $cash_refund, + 'cash_adjustment' => $cash_adjustment, + 'employee_id' => $employee_id ); $success = $this->db->insert('sales_payments', $sales_payments_data); } @@ -572,7 +585,8 @@ class Sale extends CI_Model $sales_payments_data = array( 'payment_type' => $payment_type, 'payment_amount' => $payment_amount, - 'cash_refund' => $cash_refund + 'cash_refund' => $cash_refund, + 'cash_adjustment' => $cash_adjustment ); $this->db->where('payment_id', $payment_id); $success = $this->db->update('sales_payments', $sales_payments_data); @@ -589,7 +603,6 @@ class Sale extends CI_Model $success &= $this->db->trans_status(); } - return $success; } @@ -658,11 +671,12 @@ class Sale extends CI_Model } $sales_payments_data = array( - 'sale_id' => $sale_id, - 'payment_type' => $payment['payment_type'], - 'payment_amount' => $payment['payment_amount'], - 'cash_refund' => $payment['cash_refund'], - 'employee_id' => $employee_id + 'sale_id' => $sale_id, + 'payment_type' => $payment['payment_type'], + 'payment_amount' => $payment['payment_amount'], + 'cash_refund' => $payment['cash_refund'], + 'cash_adjustment' => $payment['cash_adjustment'], + 'employee_id' => $employee_id ); $this->db->insert('sales_payments', $sales_payments_data); @@ -1126,15 +1140,17 @@ class Sale extends CI_Model $sales_tax = 'IFNULL(SUM(sales_items_taxes.sales_tax), 0)'; $internal_tax = 'IFNULL(SUM(sales_items_taxes.internal_tax), 0)'; + $cash_adjustment = 'IFNULL(SUM(payments.sale_cash_adjustment), 0)'; + if($this->config->item('tax_included')) { - $sale_total = 'ROUND(SUM(' . $sale_price . '), ' . $decimals . ')'; - $sale_subtotal = $sale_total . ' - ' . $internal_tax; + $sale_total = "ROUND(SUM($sale_price), $decimals) + $cash_adjustment"; + $sale_subtotal = "$sale_total - $internal_tax"; } else { - $sale_subtotal = 'ROUND(SUM(' . $sale_price . '), ' . $decimals . ') - ' . $internal_tax; - $sale_total = 'ROUND(SUM(' . $sale_price . '), ' . $decimals . ') + ' . $sales_tax; + $sale_subtotal = "ROUND(SUM($sale_price), $decimals) - $internal_tax + $cash_adjustment"; + $sale_total = "ROUND(SUM($sale_price), $decimals) + $sales_tax + $cash_adjustment"; } // create a temporary table to contain all the sum of taxes per sale item @@ -1162,7 +1178,8 @@ class Sale extends CI_Model ' (PRIMARY KEY(sale_id), INDEX(sale_id)) ( SELECT payments.sale_id AS sale_id, - IFNULL(SUM(payments.payment_amount), 0) AS sale_payment_amount, + SUM(CASE WHEN payments.cash_adjustment = 0 THEN payments.payment_amount ELSE 0 END) AS sale_payment_amount, + SUM(CASE WHEN payments.cash_adjustment = 1 THEN payments.payment_amount ELSE 0 END) AS sale_cash_adjustment, GROUP_CONCAT(CONCAT(payments.payment_type, " ", (payments.payment_amount - payments.cash_refund)) SEPARATOR ", ") AS payment_type FROM ' . $this->db->dbprefix('sales_payments') . ' AS payments INNER JOIN ' . $this->db->dbprefix('sales') . ' AS sales diff --git a/application/models/reports/Summary_payments.php b/application/models/reports/Summary_payments.php index e34bb833c..b2eda838b 100644 --- a/application/models/reports/Summary_payments.php +++ b/application/models/reports/Summary_payments.php @@ -157,7 +157,9 @@ class Summary_payments extends Summary_report $this->db->query('CREATE TEMPORARY TABLE IF NOT EXISTS ' . $this->db->dbprefix('sumpay_payments_temp') . ' (INDEX(sale_id)) ENGINE=MEMORY ( - SELECT sales.sale_id, COUNT(sales.sale_id) AS number_payments, SUM(sales_payments.payment_amount) AS total_payments, + SELECT sales.sale_id, COUNT(sales.sale_id) AS number_payments, + SUM(CASE WHEN sales_payments.cash_adjustment = 0 THEN sales_payments.payment_amount ELSE 0 END) AS total_payments, + SUM(CASE WHEN sales_payments.cash_adjustment = 1 THEN sales_payments.payment_amount ELSE 0 END) AS total_cash_adjustment, SUM(sales_payments.cash_refund) AS total_cash_refund FROM ' . $this->db->dbprefix('sales') . ' AS sales LEFT OUTER JOIN ' . $this->db->dbprefix('sales_payments') . ' AS sales_payments diff --git a/application/views/sales/register.php b/application/views/sales/register.php index 7b9f81593..e6214c45f 100644 --- a/application/views/sales/register.php +++ b/application/views/sales/register.php @@ -891,8 +891,9 @@ $(document).ready(function() }); }); -function check_payment_type() { - var cash_rounding = ; +function check_payment_type() +{ + var cash_mode = ; if($("#payment_types").val() == "lang->line('sales_giftcard'); ?>") { @@ -904,9 +905,9 @@ function check_payment_type() { $(".non-giftcard-input").attr('disabled', true); $(".giftcard-input:enabled").val('').focus(); } - else if($("#payment_types").val() == "lang->line('sales_cash'); ?>" && cash_rounding) + else if(($("#payment_types").val() == "lang->line('sales_cash'); ?>" && cash_mode == '1')) { - $("#sale_total").html(""); + $("#sale_total").html(""); $("#sale_amount_due").html(""); $("#amount_tendered_label").html("lang->line('sales_amount_tendered'); ?>"); $("#amount_tendered:enabled").val(""); @@ -916,9 +917,9 @@ function check_payment_type() { else { $("#sale_total").html(""); - $("#sale_amount_due").html(""); + $("#sale_amount_due").html(""); $("#amount_tendered_label").html("lang->line('sales_amount_tendered'); ?>"); - $("#amount_tendered:enabled").val(""); + $("#amount_tendered:enabled").val(""); $(".giftcard-input").attr('disabled', true); $(".non-giftcard-input").attr('disabled', false); }