Directory access is forbidden.
- - + + + +Directory access is forbidden.
+ + diff --git a/application/language/zh/item_kits_lang.php b/application/language/zh-Hans/item_kits_lang.php old mode 100755 new mode 100644 similarity index 92% rename from application/language/zh/item_kits_lang.php rename to application/language/zh-Hans/item_kits_lang.php index d9b55ce9f..042b77e35 --- a/application/language/zh/item_kits_lang.php +++ b/application/language/zh-Hans/item_kits_lang.php @@ -6,7 +6,10 @@ $lang["item_kits_cannot_be_deleted"] = "無法刪除產品套件"; $lang["item_kits_confirm_delete"] = "你確定你要刪除選取的產品套件嗎?"; $lang["item_kits_confirm_restore"] = ""; $lang["item_kits_description"] = "產品套件描述"; +$lang["item_kits_discount"] = ""; +$lang["item_kits_discount_fixed"] = ""; $lang["item_kits_discount_percent"] = ""; +$lang["item_kits_discount_type"] = ""; $lang["item_kits_error_adding_updating"] = "新增/更新產品套件錯誤"; $lang["item_kits_find_kit_item"] = ""; $lang["item_kits_info"] = "產品套件資料"; diff --git a/application/language/zh/items_lang.php b/application/language/zh-Hans/items_lang.php old mode 100755 new mode 100644 similarity index 94% rename from application/language/zh/items_lang.php rename to application/language/zh-Hans/items_lang.php index 9801eea31..10e9b5033 --- a/application/language/zh/items_lang.php +++ b/application/language/zh-Hans/items_lang.php @@ -24,6 +24,7 @@ $lang["items_cost_price_number"] = "成本價必需是數值"; $lang["items_cost_price_required"] = "成本價為必填攔位"; $lang["items_count"] = "更新庫存"; $lang["items_current_quantity"] = "當前數量"; +$lang["items_default_pack_name"] = ""; $lang["items_description"] = "描述"; $lang["items_details_count"] = "庫存盤點詳情"; $lang["items_do_nothing"] = "沒有執行任何修改"; @@ -37,6 +38,7 @@ $lang["items_excel_import_nodata_wrongformat"] = "Your uploaded file has no data $lang["items_excel_import_partially_failed"] = "Most Items imported. But some were not, here is the list"; $lang["items_excel_import_success"] = "Import of Items successful"; $lang["items_generate_barcodes"] = "產生條碼"; +$lang["items_hsn_code"] = ""; $lang["items_image"] = "Avatar"; $lang["items_import_items_excel"] = "Import items from Excel sheet"; $lang["items_info_provided_by"] = "訊息提供者"; @@ -55,6 +57,7 @@ $lang["items_item_number_duplicate"] = "The item number is already present in th $lang["items_kit"] = ""; $lang["items_location"] = "位置"; $lang["items_low_inventory_items"] = "低庫存產品"; +$lang["items_low_sell_item"] = ""; $lang["items_manually_editing_of_quantity"] = "手動編輯數量"; $lang["items_name"] = "產品名稱"; $lang["items_name_required"] = "產品名稱為必填"; @@ -67,6 +70,8 @@ $lang["items_nonstock"] = ""; $lang["items_number_information"] = "產品編號"; $lang["items_number_required"] = "UPC/EAN/ISBN is a required field"; $lang["items_one_or_multiple"] = "產品"; +$lang["items_pack_name"] = ""; +$lang["items_qty_per_pack"] = ""; $lang["items_quantity"] = "數量"; $lang["items_quantity_number"] = "數量必需是數值"; $lang["items_quantity_required"] = "數量為必填欄位"; @@ -78,8 +83,8 @@ $lang["items_reorder_level_required"] = "補貨點為必填欄位"; $lang["items_retrive_item_info"] = "檢索產品訊息"; $lang["items_sales_tax_1"] = "營業稅"; $lang["items_sales_tax_2"] = "營業稅 2"; -$lang["items_search_attributes"] = ""; -$lang["items_search_custom_items"] = "Search Custom Fields"; +$lang["items_search_attributes"] = "Search Custom Fields"; +$lang["items_search_custom_items"] = ""; $lang["items_select_image"] = "Select Image"; $lang["items_serialized_items"] = "序列化產品"; $lang["items_standard"] = ""; @@ -93,10 +98,12 @@ $lang["items_successful_updating"] = "已成功更新產品"; $lang["items_supplier"] = "供貨商"; $lang["items_tax_1"] = "稅額 1"; $lang["items_tax_2"] = "稅額 2"; +$lang["items_tax_category"] = ""; $lang["items_tax_percent"] = "稅率"; $lang["items_tax_percent_number"] = ""; $lang["items_tax_percent_required"] = "稅率為必填欄位"; $lang["items_tax_percents"] = "稅率 %"; +$lang["items_temp"] = ""; $lang["items_type"] = ""; $lang["items_unit_price"] = "單價"; $lang["items_unit_price_number"] = "單價必需是數值"; diff --git a/application/language/zh/login_lang.php b/application/language/zh-Hans/login_lang.php old mode 100755 new mode 100644 similarity index 100% rename from application/language/zh/login_lang.php rename to application/language/zh-Hans/login_lang.php diff --git a/application/language/zh/messages_lang.php b/application/language/zh-Hans/messages_lang.php similarity index 100% rename from application/language/zh/messages_lang.php rename to application/language/zh-Hans/messages_lang.php diff --git a/application/language/zh/module_lang.php b/application/language/zh-Hans/module_lang.php old mode 100755 new mode 100644 similarity index 92% rename from application/language/zh/module_lang.php rename to application/language/zh-Hans/module_lang.php index d9cb86444..798fbe56f --- a/application/language/zh/module_lang.php +++ b/application/language/zh-Hans/module_lang.php @@ -1,6 +1,10 @@ + + +Directory access is forbidden.
+ + + diff --git a/application/language/zh-Hant/item_kits_lang.php b/application/language/zh-Hant/item_kits_lang.php new file mode 100644 index 000000000..042b77e35 --- /dev/null +++ b/application/language/zh-Hant/item_kits_lang.php @@ -0,0 +1,35 @@ +find_migrations(); + return basename(end($migrations)); + } + + public function is_latest() + { + $last_migration = $this->get_last_migration(); + $last_version = $this->_get_migration_number($last_migration); + $current_version = $this->_get_version(); + + return $last_version == $current_version; + } + +} \ No newline at end of file diff --git a/application/libraries/Receiving_lib.php b/application/libraries/Receiving_lib.php index 894474bc9..a7982d6c3 100644 --- a/application/libraries/Receiving_lib.php +++ b/application/libraries/Receiving_lib.php @@ -159,7 +159,7 @@ class Receiving_lib $this->CI->session->unset_userdata('recv_stock_destination'); } - public function add_item($item_id, $quantity = 1, $item_location = NULL, $discount = 0, $price = NULL, $description = NULL, $serialnumber = NULL, $receiving_quantity = NULL, $include_deleted = FALSE) + public function add_item($item_id, $quantity = 1, $item_location = NULL, $discount = 0, $discount_type = 0, $price = NULL, $description = NULL, $serialnumber = NULL, $receiving_quantity = NULL, $receiving_id = NULL, $include_deleted = FALSE) { //make sure item exists in database. if(!$this->CI->Item->exists($item_id, $include_deleted)) @@ -209,22 +209,48 @@ class Receiving_lib $item_info = $this->CI->Item->get_info($item_id,$item_location); //array records are identified by $insertkey and item_id is just another field. $price = $price != NULL ? $price : $item_info->cost_price; + + if($this->CI->config->item('multi_pack_enabled') == '1') + { + $item_info->name .= NAME_SEPARATOR . $item_info->pack_name; + } + + if ($item_info->receiving_quantity == 0 || $item_info->receiving_quantity == 1) + { + $receiving_quantity_choices = array(1 => 'x1'); + } + else + { + $receiving_quantity_choices = array( + to_quantity_decimals($item_info->receiving_quantity) => 'x' . to_quantity_decimals($item_info->receiving_quantity), + 1 => 'x1'); + } + + if(is_null($receiving_quantity)) + { + $receiving_quantity = $item_info->receiving_quantity; + } + $item = array($insertkey => array( 'item_id' => $item_id, 'item_location' => $item_location, + 'item_number' => $item_info->item_number, 'stock_name' => $this->CI->Stock_location->get_location_name($item_location), 'line' => $insertkey, 'name' => $item_info->name, 'description' => $description != NULL ? $description: $item_info->description, 'serialnumber' => $serialnumber != NULL ? $serialnumber: '', + 'attribute_values' => $this->CI->Attribute->get_link_values($item_id, 'receiving_id', $receiving_id, Attribute::SHOW_IN_RECEIVINGS), 'allow_alt_description' => $item_info->allow_alt_description, 'is_serialized' => $item_info->is_serialized, 'quantity' => $quantity, 'discount' => $discount, + 'discount_type' => $discount_type, 'in_stock' => $this->CI->Item_quantity->get_item_quantity($item_id, $item_location)->quantity, 'price' => $price, - 'receiving_quantity' => $receiving_quantity!=NULL ? $receiving_quantity : $item_info->receiving_quantity, - 'total' => $this->get_item_total($quantity, $price, $discount, $receiving_quantity) + 'receiving_quantity' => $receiving_quantity, + 'receiving_quantity_choices' => $receiving_quantity_choices, + 'total' => $this->get_item_total($quantity, $price, $discount, $discount_type, $receiving_quantity) ) ); @@ -232,7 +258,7 @@ class Receiving_lib if($itemalreadyinsale) { $items[$updatekey]['quantity'] += $quantity; - $items[$updatekey]['total'] = $this->get_item_total($items[$updatekey]['quantity'], $price, $discount); + $items[$updatekey]['total'] = $this->get_item_total($items[$updatekey]['quantity'], $price, $discount, $discount_type, $items[$updatekey]['receiving_quantity']); } else { @@ -245,7 +271,7 @@ class Receiving_lib return TRUE; } - public function edit_item($line, $description, $serialnumber, $quantity, $discount, $price) + public function edit_item($line, $description, $serialnumber, $quantity, $discount, $discount_type, $price, $receiving_quantity) { $items = $this->get_cart(); if(isset($items[$line])) @@ -254,9 +280,14 @@ class Receiving_lib $line['description'] = $description; $line['serialnumber'] = $serialnumber; $line['quantity'] = $quantity; + $line['receiving_quantity'] = $receiving_quantity; $line['discount'] = $discount; + if(!is_null($discount_type)) + { + $line['discount_type'] = $discount_type; + } $line['price'] = $price; - $line['total'] = $this->get_item_total($quantity, $price, $discount); + $line['total'] = $this->get_item_total($quantity, $price, $discount, $discount_type, $receiving_quantity); $this->set_cart($items); } @@ -289,13 +320,13 @@ class Receiving_lib foreach($this->CI->Receiving->get_receiving_items($receiving_id)->result() as $row) { - $this->add_item($row->item_id, -$row->quantity_purchased, $row->item_location, $row->discount_percent, $row->item_unit_price, $row->description, $row->serialnumber, $row->receiving_quantity, TRUE); + $this->add_item($row->item_id, -$row->quantity_purchased, $row->item_location, $row->discount, $row->discount_type, $row->item_unit_price, $row->description, $row->serialnumber, $receiving_id, $row->receiving_quantity, TRUE); } $this->set_supplier($this->CI->Receiving->get_supplier($receiving_id)->person_id); } - public function add_item_kit($external_item_kit_id, $item_location) + public function add_item_kit($external_item_kit_id, $item_location, $discount, $discount_type) { //KIT # $pieces = explode(' ',$external_item_kit_id); @@ -303,7 +334,7 @@ class Receiving_lib foreach($this->CI->Item_kit_items->get_info($item_kit_id) as $item_kit_item) { - $this->add_item($item_kit_item['item_id'],$item_kit_item['quantity'], $item_location); + $this->add_item($item_kit_item['item_id'], $item_kit_item['quantity'], $item_location, $discount, $discount_type); } } @@ -314,7 +345,7 @@ class Receiving_lib foreach($this->CI->Receiving->get_receiving_items($receiving_id)->result() as $row) { - $this->add_item($row->item_id, $row->quantity_purchased, $row->item_location, $row->discount_percent, $row->item_unit_price, $row->description, $row->serialnumber, $row->receiving_quantity, TRUE); + $this->add_item($row->item_id, $row->quantity_purchased, $row->item_location, $row->discount, $row->discount_type, $row->item_unit_price, $row->description, $row->serialnumber, $row->receiving_quantity, $receiving_id, TRUE); } $this->set_supplier($this->CI->Receiving->get_supplier($receiving_id)->person_id); @@ -330,11 +361,16 @@ class Receiving_lib $this->clear_reference(); } - public function get_item_total($quantity, $price, $discount_percentage) + public function get_item_total($quantity, $price, $discount, $discount_type, $receiving_quantity) { - $total = bcmul($quantity, $price); - $discount_fraction = bcdiv($discount_percentage, 100); - $discount_amount = bcmul($total, $discount_fraction); + $extended_quantity = bcmul($quantity, $receiving_quantity); + $total = bcmul($extended_quantity, $price); + $discount_amount = $discount; + if($discount_type == PERCENT) + { + $discount_fraction = bcdiv($discount, 100); + $discount_amount = bcmul($total, $discount_fraction); + } return bcsub($total, $discount_amount); } @@ -344,7 +380,7 @@ class Receiving_lib $total = 0; foreach($this->get_cart() as $item) { - $total = bcadd($total, $this->get_item_total(($item['quantity']* $item['receiving_quantity']), $item['price'], $item['discount'])); + $total = bcadd($total, $this->get_item_total(($item['quantity']), $item['price'], $item['discount'], $item['discount_type'], $item['receiving_quantity'])); } return $total; diff --git a/application/libraries/Sale_lib.php b/application/libraries/Sale_lib.php index 61ca5d259..5263c87de 100644 --- a/application/libraries/Sale_lib.php +++ b/application/libraries/Sale_lib.php @@ -47,6 +47,16 @@ class Sale_lib return $register_modes; } + public function get_invoice_type_options() + { + $invoice_types = array(); + $invoice_types['invoice'] = $this->CI->lang->line('sales_invoice_type_invoice'); + $invoice_types['tax_invoice'] = $this->CI->lang->line('sales_invoice_type_tax_invoice'); + $invoice_types['custom_invoice'] = $this->CI->lang->line('sales_invoice_type_custom_invoice'); + $invoice_types['custom_tax_invoice'] = $this->CI->lang->line('sales_invoice_type_custom_tax_invoice'); + return $invoice_types; + } + public function get_cart() { if(!$this->CI->session->userdata('sales_cart')) @@ -66,10 +76,14 @@ class Sale_lib $filtered_cart = array(); - foreach($cart as $k=>$v) + foreach($cart as $k => $v) { if($v['print_option'] == PRINT_YES) { + if($v['price'] == 0.0) + { + $v['discount'] = 0.0; + } $filtered_cart[] = $v; } } @@ -78,7 +92,7 @@ class Sale_lib if($this->CI->config->item('line_sequence') == '0') { $sort = array(); - foreach($filtered_cart as $k=>$v) + foreach($filtered_cart as $k => $v) { $sort['line'][$k] = $v['line']; } @@ -88,19 +102,19 @@ class Sale_lib elseif($this->CI->config->item('line_sequence') == '1') { $sort = array(); - foreach($filtered_cart as $k=>$v) + foreach($filtered_cart as $k => $v) { $sort['stock_type'][$k] = $v['stock_type']; $sort['description'][$k] = $v['description']; $sort['name'][$k] = $v['name']; } - array_multisort($sort['stock_type'], SORT_DESC, $sort['description'], SORT_ASC, $sort['name'], SORT_ASC. $filtered_cart); + array_multisort($sort['stock_type'], SORT_DESC, $sort['description'], SORT_ASC, $sort['name'], SORT_ASC . $filtered_cart); } // Group by Item Category elseif($this->CI->config->item('line_sequence') == '2') { $sort = array(); - foreach($filtered_cart as $k=>$v) + foreach($filtered_cart as $k => $v) { $sort['category'][$k] = $v['stock_type']; $sort['description'][$k] = $v['description']; @@ -112,7 +126,7 @@ class Sale_lib else { $sort = array(); - foreach($filtered_cart as $k=>$v) + foreach($filtered_cart as $k => $v) { $sort['line'][$k] = $v['line']; } @@ -132,6 +146,19 @@ class Sale_lib $this->CI->session->unset_userdata('sales_cart'); } + public function remove_temp_items() + { + // Loop through the cart items and delete temporary items specific to this sale + $cart = $this->get_cart(); + foreach($cart as $line=>$item) + { + if($item['item_type'] == ITEM_TEMP) + { + $this->CI->Item->delete($item['item_id']); + } + } + } + public function get_comment() { // avoid returning a NULL that results in a 0 in the comment if nothing is set/available @@ -399,7 +426,7 @@ class Sale_lib // Multiple Payments public function get_payments_total() { - $subtotal = 0; + $subtotal = 0.0; $this->reset_cash_flags(); foreach($this->get_payments() as $payments) { @@ -417,18 +444,18 @@ 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' */ - public function get_totals() + public function get_totals($taxes) { $cash_rounding = $this->CI->session->userdata('cash_rounding'); $totals = array(); - $prediscount_subtotal = 0; - $subtotal = 0; - $total = 0; - $total_discount = 0; + $prediscount_subtotal = 0.0; + $subtotal = 0.0; + $total = 0.0; + $total_discount = 0.0; $item_count = 0; - $total_units = 0; + $total_units = 0.0; foreach($this->get_cart() as $item) { @@ -437,7 +464,7 @@ class Sale_lib $item_count++; $total_units += $item['quantity']; } - $discount_amount = $this->get_item_discount($item['quantity'], $item['price'], $item['discount']); + $discount_amount = $this->get_item_discount($item['quantity'], $item['price'], $item['discount'], $item['discount_type']); $total_discount = bcadd($total_discount, $discount_amount); $extended_amount = $this->get_extended_amount($item['quantity'], $item['price']); @@ -447,7 +474,7 @@ class Sale_lib if($this->CI->config->item('tax_included')) { - $subtotal = bcadd($subtotal, $this->get_extended_total_tax_exclusive($item['item_id'], $extended_discounted_amount, $item['quantity'], $item['price'], $item['discount'])); + $subtotal = bcadd($subtotal, $this->get_extended_total_tax_exclusive($item['item_id'], $extended_discounted_amount, $item['quantity'], $item['price'], $item['discount'],$item['discount_type'])); } else { @@ -459,18 +486,14 @@ class Sale_lib $totals['total_discount'] = $total_discount; $totals['subtotal'] = $subtotal; - if($this->CI->config->item('tax_included')) + foreach($taxes as $tax_excluded) { - $totals['total'] = $total; - } - else - { - foreach($this->get_taxes() as $sales_tax) + if($tax_excluded['tax_type'] == Tax_lib::TAX_TYPE_EXCLUDED) { - $total = bcadd($total, $sales_tax['sale_tax_amount']); + $total = bcadd($total, $tax_excluded['sale_tax_amount']); } - $totals['total'] = $total; } + $totals['total'] = $total; if($cash_rounding) { @@ -530,7 +553,7 @@ class Sale_lib $precision = totals_decimals(); $rounded_due = bccomp(round($amount_due, $precision, PHP_ROUND_HALF_UP), 0, $precision); // take care of rounding error introduced by round tripping payment amount to the browser - return $rounded_due == 0 ? 0 : $amount_due; + return $rounded_due == 0.0 ? 0.0 : $amount_due; } public function get_customer() @@ -683,10 +706,9 @@ class Sale_lib $this->CI->session->unset_userdata('sales_rewards_remainder'); } - public function add_item(&$item_id, $quantity = 1, $item_location, $discount = 0, $price_mode = PRICE_MODE_STANDARD, $kit_price_option = NULL, $kit_print_option = NULL, $price_override = NULL, $description = NULL, $serialnumber = NULL, $include_deleted = FALSE, $print_option = NULL ) + public function add_item(&$item_id, $quantity = 1, $item_location, $discount = 0.0, $discount_type = 0, $price_mode = PRICE_MODE_STANDARD, $kit_price_option = NULL, $kit_print_option = NULL, $price_override = NULL, $description = NULL, $serialnumber = NULL, $sale_id = NULL, $include_deleted = FALSE, $print_option = NULL, $line = NULL ) { - $item_info = $this->CI->Item->get_info_by_id_or_number($item_id); - + $item_info = $this->CI->Item->get_info_by_id_or_number($item_id, $include_deleted); //make sure item exists if(empty($item_info)) { @@ -737,11 +759,6 @@ class Sale_lib $price = $price_override; } - if($price == 0.00) - { - $discount = 0.00; - } - // Serialization and Description //Get all items in the cart so far... @@ -812,9 +829,16 @@ class Sale_lib } } - $total = $this->get_item_total($quantity, $price, $discount); - $discounted_total = $this->get_item_total($quantity, $price, $discount, TRUE); - //Item already exists and is not serialized, add to quantity + $total = $this->get_item_total($quantity, $price, $discount, $discount_type); + $discounted_total = $this->get_item_total($quantity, $price, $discount, $discount_type, TRUE); + + if($this->CI->config->item('multi_pack_enabled') == '1') + { + $item_info->name .= NAME_SEPARATOR . $item_info->pack_name; + } + + + //Item already exists and is not serialized, add to quantity if(!$itemalreadyinsale || $item_info->is_serialized) { $item = array($insertkey => array( @@ -824,12 +848,14 @@ class Sale_lib 'line' => $insertkey, 'name' => $item_info->name, 'item_number' => $item_info->item_number, + 'attribute_values' => $this->CI->Attribute->get_link_values($item_id, 'sale_id', $sale_id, Attribute::SHOW_IN_SALES), 'description' => $description != NULL ? $description : $item_info->description, 'serialnumber' => $serialnumber != NULL ? $serialnumber : '', 'allow_alt_description' => $item_info->allow_alt_description, 'is_serialized' => $item_info->is_serialized, 'quantity' => $quantity, 'discount' => $discount, + 'discount_type' => $discount_type, 'in_stock' => $this->CI->Item_quantity->get_item_quantity($item_id, $item_location)->quantity, 'price' => $price, 'cost_price' => $cost_price, @@ -838,6 +864,7 @@ class Sale_lib 'print_option' => $print_option_selected, 'stock_type' => $stock_type, 'item_type' => $item_type, + 'hsn_code' => $item_info->hsn_code, 'tax_category_id' => $item_info->tax_category_id ) ); @@ -862,7 +889,7 @@ class Sale_lib //make sure item exists if($item_id != -1) { - $item_info = $this->CI->Item->get_info_by_id_or_number($item_id); + $item_info = $this->CI->Item->get_info_by_id_or_number($item_id, TRUE); if($item_info->stock_type == HAS_STOCK) { @@ -913,7 +940,7 @@ class Sale_lib return -1; } - public function edit_item($line, $description, $serialnumber, $quantity, $discount, $price, $discounted_total=NULL) + public function edit_item($line, $description, $serialnumber, $quantity, $discount, $discount_type, $price, $discounted_total=NULL) { $items = $this->get_cart(); if(isset($items[$line])) @@ -928,9 +955,13 @@ class Sale_lib $line['serialnumber'] = $serialnumber; $line['quantity'] = $quantity; $line['discount'] = $discount; + if(!is_null($discount_type)) + { + $line['discount_type'] = $discount_type; + } $line['price'] = $price; - $line['total'] = $this->get_item_total($quantity, $price, $discount); - $line['discounted_total'] = $this->get_item_total($quantity, $price, $discount, TRUE); + $line['total'] = $this->get_item_total($quantity, $price, $discount, $line['discount_type']); + $line['discounted_total'] = $this->get_item_total($quantity, $price, $discount, $line['discount_type'], TRUE); $this->set_cart($items); } @@ -940,6 +971,12 @@ class Sale_lib public function delete_item($line) { $items = $this->get_cart(); + $item_type = $items[$line]['item_type']; + if($item_type == ITEM_TEMP) + { + $item_id = $items[$line]['item_id']; + $this->CI->Item->delete($item_id); + } unset($items[$line]); $this->set_cart($items); } @@ -955,13 +992,13 @@ class Sale_lib foreach($this->CI->Sale->get_sale_items_ordered($sale_id)->result() as $row) { - $this->add_item($row->item_id, -$row->quantity_purchased, $row->item_location, $row->discount_percent, PRICE_MODE_STANDARD, NULL, NULL, $row->item_unit_price, $row->description, $row->serialnumber, TRUE); + $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, TRUE); } $this->set_customer($this->CI->Sale->get_customer($sale_id)->person_id); } - public function add_item_kit($external_item_kit_id, $item_location, $discount, $kit_price_option, $kit_print_option, &$stock_warning) + public function add_item_kit($external_item_kit_id, $item_location, $discount, $discount_type, $kit_price_option, $kit_print_option, &$stock_warning) { //KIT # $pieces = explode(' ', $external_item_kit_id); @@ -970,7 +1007,7 @@ class Sale_lib foreach($this->CI->Item_kit_items->get_info($item_kit_id) as $item_kit_item) { - $result &= $this->add_item($item_kit_item['item_id'], $item_kit_item['quantity'], $item_location, $discount, PRICE_MODE_KIT, $kit_price_option, $kit_print_option, NULL, NULL, NULL, NULL); + $result &= $this->add_item($item_kit_item['item_id'], $item_kit_item['quantity'], $item_location, $discount, $discount_type, PRICE_MODE_KIT, $kit_price_option, $kit_print_option, NULL, NULL, NULL, FALSE); if($stock_warning == NULL) { @@ -988,7 +1025,7 @@ class Sale_lib foreach($this->CI->Sale->get_sale_items_ordered($sale_id)->result() as $row) { - $this->add_item($row->item_id, $row->quantity_purchased, $row->item_location, $row->discount_percent, PRICE_MODE_STANDARD, NULL, NULL, $row->item_unit_price, $row->description, $row->serialnumber, TRUE, $row->print_option); + $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); } foreach($this->CI->Sale->get_sale_payments($sale_id)->result() as $row) @@ -1016,7 +1053,7 @@ class Sale_lib $this->empty_cart(); foreach($this->CI->Sale->get_sale_items_ordered($sale_id)->result() as $row) { - $this->add_item($row->item_id, $row->quantity_purchased, $row->item_location, $row->discount_percent, PRICE_MODE_STANDARD, NULL, NULL, $row->item_unit_price, $row->description, $row->serialnumber, TRUE, $row->print_option); + $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); } return $this->CI->session->userdata('sales_cart'); @@ -1078,69 +1115,7 @@ class Sale_lib return $customer->taxable or $customer_id == -1; } - /* - * This returns taxes for VAT taxes and for pre 3.1.0 sales taxes. - */ - public function get_taxes() - { - $register_mode = $this->CI->config->item('default_register_mode'); - $tax_decimals = tax_decimals(); - $customer_id = $this->get_customer(); - $customer = $this->CI->Customer->get_info($customer_id); - $sales_taxes = array(); - //Do not charge sales tax if we have a customer that is not taxable - if($customer->taxable or $customer_id == -1) - { - foreach($this->get_cart() as $line => $item) - { - // Start of current VAT tax apply - $tax_info = $this->CI->Item_taxes->get_info($item['item_id']); - $tax_group_sequence = 0; - foreach($tax_info as $tax) - { - // This computes tax for each line item and adds it to the tax type total - $tax_group = (float)$tax['percent'] . '% ' . $tax['name']; - $tax_type = Tax_lib::TAX_TYPE_VAT; - $tax_basis = $this->get_item_total($item['quantity'], $item['price'], $item['discount'], TRUE); - $tax_amount = 0; - - if($this->CI->config->item('tax_included')) - { - $tax_amount = $this->get_item_tax($item['quantity'], $item['price'], $item['discount'], $tax['percent']); - } - elseif($this->CI->config->item('customer_sales_tax_support') == '0') - { - $tax_amount = $this->CI->tax_lib->get_sales_tax_for_amount($tax_basis, $tax['percent'], '0', $tax_decimals); - } - - if($tax_amount <> 0) - { - $this->CI->tax_lib->update_sales_taxes($sales_taxes, $tax_type, $tax_group, $tax['percent'], $tax_basis, $tax_amount, $tax_group_sequence, '0', -1); - $tax_group_sequence += 1; - } - } - - $tax_category = ''; - $tax_rate = ''; - $rounding_code = Rounding_mode::HALF_UP; - $tax_group_sequence = 0; - $tax_code = ''; - - if($this->CI->config->item('customer_sales_tax_support') == '1') - { - // Now calculate what the sales taxes should be (storing them in the $sales_taxes array - $this->CI->tax_lib->apply_sales_tax($item, $customer->city, $customer->state, $customer->sales_tax_code, $register_mode, 0, $sales_taxes, $tax_category, $tax_rate, $rounding_code, $tax_group_sequence, $tax_code); - } - - } - - $this->CI->tax_lib->round_sales_taxes($sales_taxes); - } - - return $sales_taxes; - } - - public function apply_customer_discount($discount_percent) + public function apply_customer_discount($discount, $discount_type) { // Get all items in the cart so far... $items = $this->get_cart(); @@ -1151,11 +1126,11 @@ class Sale_lib $price = $item['price']; // set a new discount only if the current one is 0 - if($item['discount'] == 0) + if($item['discount'] == 0.0) { - $item['discount'] = $discount_percent; - $item['total'] = $this->get_item_total($quantity, $price, $discount_percent); - $item['discounted_total'] = $this->get_item_total($quantity, $price, $discount_percent, TRUE); + $item['discount'] = $discount; + $item['total'] = $this->get_item_total($quantity, $price, $discount, $discount_type); + $item['discounted_total'] = $this->get_item_total($quantity, $price, $discount, $discount_type, TRUE); } } @@ -1164,12 +1139,12 @@ class Sale_lib public function get_discount() { - $discount = 0; + $discount = 0.0; foreach($this->get_cart() as $item) { - if($item['discount'] > 0) + if($item['discount'] > 0.0) { - $item_discount = $this->get_item_discount($item['quantity'], $item['price'], $item['discount']); + $item_discount = $this->get_item_discount($item['quantity'], $item['price'], $item['discount'], $item['discount_type']); $discount = bcadd($discount, $item_discount); } } @@ -1182,39 +1157,39 @@ class Sale_lib return $this->calculate_subtotal($include_discount, $exclude_tax); } - public function get_item_total_tax_exclusive($item_id, $quantity, $price, $discount_percentage, $include_discount = FALSE) + public function get_item_total_tax_exclusive($item_id, $quantity, $price, $discount, $discount_type, $include_discount = FALSE) { $tax_info = $this->CI->Item_taxes->get_info($item_id); - $item_total = $this->get_item_total($quantity, $price, $discount_percentage, $include_discount); + $item_total = $this->get_item_total($quantity, $price, $discount, $discount_type, $include_discount); // only additive tax here foreach($tax_info as $tax) { $tax_percentage = $tax['percent']; - $item_total = bcsub($item_total, $this->get_item_tax($quantity, $price, $discount_percentage, $tax_percentage)); + $item_total = bcsub($item_total, $this->get_item_tax($quantity, $price, $discount, $discount_type, $tax_percentage)); } return $item_total; } - public function get_extended_total_tax_exclusive($item_id, $discounted_extended_amount, $quantity, $price, $discount_percentage = 0) + public function get_extended_total_tax_exclusive($item_id, $discounted_extended_amount, $quantity, $price, $discount = 0.0, $discount_type = 0) { $tax_info = $this->CI->Item_taxes->get_info($item_id); // only additive tax here foreach($tax_info as $tax) { $tax_percentage = $tax['percent']; - $discounted_extended_amount = bcsub($discounted_extended_amount, $this->get_item_tax($quantity, $price, $discount_percentage, $tax_percentage)); + $discounted_extended_amount = bcsub($discounted_extended_amount, $this->get_item_tax($quantity, $price, $discount, $discount_type, $tax_percentage)); } return $discounted_extended_amount; } - public function get_item_total($quantity, $price, $discount_percentage, $include_discount = FALSE) + public function get_item_total($quantity, $price, $discount, $discount_type, $include_discount = FALSE) { $total = bcmul($quantity, $price); if($include_discount) { - $discount_amount = $this->get_item_discount($quantity, $price, $discount_percentage); + $discount_amount = $this->get_item_discount($quantity, $price, $discount, $discount_type); return bcsub($total, $discount_amount); } @@ -1236,24 +1211,28 @@ class Sale_lib return $quantity; } - public function get_extended_amount($quantity, $price, $discount_amount = 0) + public function get_extended_amount($quantity, $price, $discount_amount = 0.0) { $extended_amount = bcmul($quantity, $price); return bcsub($extended_amount, $discount_amount); } - public function get_item_discount($quantity, $price, $discount_percentage) + public function get_item_discount($quantity, $price, $discount, $discount_type) { $total = bcmul($quantity, $price); - $discount_fraction = bcdiv($discount_percentage, 100); - - return round(bcmul($total, $discount_fraction), totals_decimals(), PHP_ROUND_HALF_UP); + if($discount_type == PERCENT) + { + $discount_fraction = bcdiv($discount, 100); + $discount=bcmul($total,$discount_fraction); + } + + return round($discount, totals_decimals(), PHP_ROUND_HALF_UP); } - public function get_item_tax($quantity, $price, $discount_percentage, $tax_percentage) + public function get_item_tax($quantity, $price, $discount, $discount_type, $tax_percentage) { - $price = $this->get_item_total($quantity, $price, $discount_percentage, TRUE); + $price = $this->get_item_total($quantity, $price, $discount, $discount_type, TRUE); if($this->CI->config->item('tax_included')) { $tax_fraction = bcadd(100, $tax_percentage); @@ -1269,16 +1248,16 @@ class Sale_lib public function calculate_subtotal($include_discount = FALSE, $exclude_tax = FALSE) { - $subtotal = 0; + $subtotal = 0.0; foreach($this->get_cart() as $item) { if($exclude_tax && $this->CI->config->item('tax_included')) { - $subtotal = bcadd($subtotal, $this->get_item_total_tax_exclusive($item['item_id'], $item['quantity'], $item['price'], $item['discount'], $include_discount)); + $subtotal = bcadd($subtotal, $this->get_item_total_tax_exclusive($item['item_id'], $item['quantity'], $item['price'], $item['discount'], $item['discount_type'], $include_discount)); } else { - $subtotal = bcadd($subtotal, $this->get_item_total($item['quantity'], $item['price'], $item['discount'], $include_discount)); + $subtotal = bcadd($subtotal, $this->get_item_total($item['quantity'], $item['price'], $item['discount'], $item['discount_type'], $include_discount)); } } @@ -1293,9 +1272,9 @@ class Sale_lib if(!$this->CI->config->item('tax_included')) { - foreach($this->get_taxes() as $sales_tax) + foreach($this->CI->tax_lib->get_taxes($this->get_cart())[0] as $tax) { - $total = bcadd($total, $sales_tax['sale_tax_amount']); + $total = bcadd($total, $tax['sale_tax_amount']); } } diff --git a/application/libraries/Tax_lib.php b/application/libraries/Tax_lib.php index 7713f62e4..7c03c625c 100644 --- a/application/libraries/Tax_lib.php +++ b/application/libraries/Tax_lib.php @@ -8,9 +8,8 @@ class Tax_lib { - const TAX_TYPE_SALES = 1; - const TAX_TYPE_SALES_BY_INVOICE = 2; - const TAX_TYPE_VAT = 0; + const TAX_TYPE_EXCLUDED = 1; + const TAX_TYPE_INCLUDED = 0; private $CI; @@ -22,50 +21,152 @@ class Tax_lib public function get_tax_types() { return array( - TAX_TYPE_SALES => $this->CI->lang->line('taxes_sales_tax'), - TAX_TYPE_SALES_BY_INVOICE => $this->CI->lang->line('taxes_sales_tax_by_invoice'), - TAX_TYPE_VAT => $this->CI->lang->line('taxes_vat_tax') + Tax_lib::TAX_TYPE_EXCLUDED => $this->CI->lang->line('taxes_tax_excluded'), + Tax_lib::TAX_TYPE_INCLUDED => $this->CI->lang->line('taxes_tax_included') ); } /* * Compute the tax basis and returns the tax amount */ - public function get_item_sales_tax($quantity, $price, $discount_percentage, $tax_percentage, $rounding_code) + public function get_item_sales_tax($quantity, $price, $discount, $discount_type, $tax_percentage, $rounding_code) { $decimals = tax_decimals(); // The tax basis should be returned at the currency scale - $tax_basis = $this->CI->sale_lib->get_item_total($quantity, $price, $discount_percentage, TRUE); + $tax_basis = $this->CI->sale_lib->get_item_total($quantity, $price, $discount, $discount_type, TRUE); - return $this->get_sales_tax_for_amount($tax_basis, $tax_percentage, $rounding_code, $decimals); + return $this->get_tax_for_amount($tax_basis, $tax_percentage, $rounding_code, $decimals); } /* * Computes the item level sales tax amount for a given tax basis */ - public function get_sales_tax_for_amount($tax_basis, $tax_percentage, $rounding_mode, $decimals) + public function get_tax_for_amount($tax_basis, $tax_percentage, $rounding_mode, $decimals) { $tax_fraction = bcdiv($tax_percentage, 100); $tax_amount = bcmul($tax_basis, $tax_fraction); - $rounded_tax_amount = $tax_amount; return Rounding_mode::round_number($rounding_mode, $tax_amount, $decimals); } - /* - * Updates the sales_tax array which is later saved to the `sales_taxes` table and used for printing taxes on receipts and invoices - */ - public function update_sales_taxes(&$sales_taxes, $tax_type, $tax_group, $tax_rate, $tax_basis, $item_tax_amount, $tax_group_sequence, $rounding_code, $sale_id, $name='', $tax_code='') - { - $tax_group_index = $this->clean('X'.$tax_group); - if(!array_key_exists($tax_group_index, $sales_taxes)) + /** + * Compute taxes for all items in the cart + */ + public function get_taxes($cart) + { + $register_mode = $this->CI->sale_lib->get_mode(); + $tax_decimals = tax_decimals(); + $customer_id = $this->CI->sale_lib->get_customer(); + $customer_info = $this->CI->Customer->get_info($customer_id); + $taxes = array(); + $item_taxes = array(); + + // Charge sales tax if customer is not selected (walk-in) or customer is flagged as taxable + if($customer_id == -1 || $customer_info->taxable) + { + foreach($cart as $line => $item) + { + if(!($this->CI->config->item('use_destination_based_tax'))) + { + // Start of current Base System tax calculations + + $tax_info = $this->CI->Item_taxes->get_info($item['item_id']); + $tax_group_sequence = 0; + $cascade_level = 0; + $cascade_basis_level = 0; + + foreach($tax_info as $tax) + { + // This computes tax for each line item and adds it to the tax type total + $tax_basis = $this->CI->sale_lib->get_item_total($item['quantity'], $item['price'], $item['discount'], TRUE); + $tax_amount = 0.0; + + if($this->CI->config->item('tax_included')) + { + $tax_type = Tax_lib::TAX_TYPE_INCLUDED; + $tax_amount = $this->get_included_tax($item['quantity'], $item['price'], $item['discount'], $tax['percent'], $tax_decimals, Rounding_mode::HALF_UP); + } + else + { + $tax_type = Tax_lib::TAX_TYPE_EXCLUDED; + $tax_amount = $this->get_tax_for_amount($tax_basis, $tax['percent'], Rounding_mode::HALF_UP, $tax_decimals); + } + + if($tax_amount <> 0) + { + $tax_group_sequence++; + $this->update_taxes($taxes, $tax_type, $tax['name'], $tax['percent'], $tax_basis, $tax_amount, $tax_group_sequence, Rounding_mode::HALF_UP, -1, $tax['name']); + $tax_group_sequence += 1; + } + $items_taxes_detail = array(); + $items_taxes_detail['item_id'] = $item['item_id']; + $items_taxes_detail['line'] = $item['line']; + $items_taxes_detail['name'] = $tax['name']; + $items_taxes_detail['percent'] = $tax['percent']; + $items_taxes_detail['tax_type'] = $tax_type; + $items_taxes_detail['rounding_code'] = Rounding_mode::HALF_UP; + $items_taxes_detail['cascade_sequence'] = 0; + $items_taxes_detail['item_tax_amount'] = $tax_amount; + $items_taxes_detail['sales_tax_code_id'] = NULL; + $items_taxes_detail['jurisdiction_id'] = NULL; + $items_taxes_detail['tax_category_id'] = NULL; + + $item_taxes[] = $items_taxes_detail; + } + } + else + { + // Start of destination based tax calculations + + if ($item['tax_category_id'] == NULL) + { + $item['tax_category_id'] = $this->CI->config->config['default_tax_category']; + } + $tax_category = ''; + $tax_rate = ''; + $rounding_code = Rounding_mode::HALF_UP; + $tax_group_sequence = 0; + $tax_code = ''; + $this->apply_destination_tax($item, $customer_info->city, $customer_info->state, $customer_info->sales_tax_code_id, $register_mode, 0, $taxes, $item_taxes, $item['line']); + } + } + $this->round_taxes($taxes); + } + + $tax_details = array(); + $tax_details[0] = $taxes; + $tax_details[1] = $item_taxes; + + return $tax_details; + } + + public function get_included_tax($quantity, $price, $discount_percentage, $tax_percentage, $tax_decimal, $rounding_code) + { + $price = $this->CI->sale_lib->get_item_total($quantity, $price, $discount_percentage, TRUE); + $tax_fraction = bcadd(100, $tax_percentage); + $tax_fraction = bcdiv($tax_fraction, 100); + $price_tax_excl = bcdiv($price, $tax_fraction); + $tax_amount = bcsub($price, $price_tax_excl); + + return Rounding_mode::round_number($rounding_code, $tax_amount, $tax_decimal); + } + + + /* + * Updates the sales_tax array which is later saved to the `sales_taxes` table and used for printing taxes on receipts and invoices + */ + public function update_taxes(&$taxes, $tax_type, $tax_group, $tax_rate, $tax_basis, $item_tax_amount, $tax_group_sequence, $rounding_code, $sale_id, $name = '', $tax_code_id = NULL, $jurisdiction_id = NULL,$tax_category_id = NULL ) + { + $tax_group_index = $this->clean('X' . (float)$tax_rate . '% ' . $tax_group); + + if(!array_key_exists($tax_group_index, $taxes)) { $insertkey = $tax_group_index; - $sales_tax = array($insertkey => array( + $tax = array($insertkey => array( 'sale_id' => $sale_id, 'tax_type' => $tax_type, 'tax_group' => $tax_group, @@ -74,101 +175,114 @@ class Tax_lib 'print_sequence' => $tax_group_sequence, 'name' => $name, 'tax_rate' => $tax_rate, - 'sales_tax_code' => $tax_code, + 'sales_tax_code_id' => $tax_code_id, + 'jurisdiction_id' => $jurisdiction_id, + 'tax_category_id' => $tax_category_id, 'rounding_code' => $rounding_code )); //add to existing array - $sales_taxes += $sales_tax; + $taxes += $tax; } else { // Important ... the sales amounts are accumulated for the group at the maximum configurable scale value of 4 // but the scale will in reality be the scale specified by the tax_decimal configuration value used for sales_items_taxes - $sales_taxes[$tax_group_index]['sale_tax_basis'] = bcadd($sales_taxes[$tax_group_index]['sale_tax_basis'], $tax_basis, 4); - $sales_taxes[$tax_group_index]['sale_tax_amount'] = bcadd($sales_taxes[$tax_group_index]['sale_tax_amount'], $item_tax_amount, 4); + $taxes[$tax_group_index]['sale_tax_basis'] = bcadd($taxes[$tax_group_index]['sale_tax_basis'], $tax_basis, 4); + $taxes[$tax_group_index]['sale_tax_amount'] = bcadd($taxes[$tax_group_index]['sale_tax_amount'], $item_tax_amount, 4); } } /* * If invoice taxing (as opposed to invoice_item_taxing) rules apply then recalculate the sales tax after tax group totals are final + * This is currently used ONLY for the original sales tax migration. */ - public function apply_invoice_taxing(&$sales_taxes) + public function apply_invoice_taxing(&$taxes) { - if(!empty($sales_taxes)) + if(!empty($taxes)) { $sort = array(); - foreach($sales_taxes as $k => $v) + foreach($taxes as $k => $v) { $sort['print_sequence'][$k] = $v['print_sequence']; } - array_multisort($sort['print_sequence'], SORT_ASC, $sales_taxes); + array_multisort($sort['print_sequence'], SORT_ASC, $taxes); } $decimals = totals_decimals(); - foreach($sales_taxes as $row_number => $sales_tax) + foreach($taxes as $row_number => $tax) { - $sales_taxes[$row_number]['sale_tax_amount'] = $this->get_sales_tax_for_amount($sales_tax['sale_tax_basis'], $sales_tax['tax_rate'], $sales_tax['rounding_code'], $decimals); + $taxes[$row_number]['sale_tax_amount'] = $this->get_tax_for_amount($tax['sale_tax_basis'], $tax['tax_rate'], $tax['rounding_code'], $decimals); } } /* * Apply rounding rules to the accumulated sales tax amounts */ - public function round_sales_taxes(&$sales_taxes) + public function round_taxes(&$taxes) { - if(!empty($sales_taxes)) + if(!empty($taxes)) { $sort = array(); - foreach($sales_taxes as $k=>$v) + foreach($taxes as $k => $v) { $sort['print_sequence'][$k] = $v['print_sequence']; } - array_multisort($sort['print_sequence'], SORT_ASC, $sales_taxes); + array_multisort($sort['print_sequence'], SORT_ASC, $taxes); } - $decimals = totals_decimals(); - - foreach($sales_taxes as $row_number => $sales_tax) + // If tax included then round decimal to tax decimals, otherwise round it to currency_decimals + if($this->CI->config->item('tax_included')) { - $sale_tax_amount = $sales_tax['sale_tax_amount']; + $decimals = tax_decimals(); + } + else + { + $decimals = totals_decimals(); + } + + + foreach($taxes as $row_number => $sales_tax) + { + $tax_amount = $sales_tax['sale_tax_amount']; $rounding_code = $sales_tax['rounding_code']; - $rounded_sale_tax_amount = $sale_tax_amount; + $rounded_tax_amount = $tax_amount; if($rounding_code == Rounding_mode::HALF_UP) { - $rounded_sale_tax_amount = round($sale_tax_amount, $decimals, PHP_ROUND_HALF_UP); + $rounded_tax_amount = round($tax_amount, $decimals, PHP_ROUND_HALF_UP); } elseif($rounding_code == Rounding_mode::HALF_DOWN) { - $rounded_sale_tax_amount = round($sale_tax_amount, $decimals, PHP_ROUND_HALF_DOWN); + $rounded_tax_amount = round($tax_amount, $decimals, PHP_ROUND_HALF_DOWN); } elseif($rounding_code == Rounding_mode::HALF_EVEN) { - $rounded_sale_tax_amount = round($sale_tax_amount, $decimals, PHP_ROUND_HALF_EVEN); + $rounded_tax_amount = round($tax_amount, $decimals, PHP_ROUND_HALF_EVEN); } elseif($rounding_code == Rounding_mode::HALF_ODD) { - $rounded_sale_tax_amount = round($sale_tax_amount, $decimals, PHP_ROUND_HALF_UP); + $rounded_tax_amount = round($tax_amount, $decimals, PHP_ROUND_HALF_UP); } elseif($rounding_code == Rounding_mode::ROUND_UP) { - $fig = (int) str_pad('1', $decimals, '0'); - $rounded_sale_tax_amount = (ceil($sale_tax_amount * $fig) / $fig); + $fig = (int)str_pad('1', $decimals, '0'); + $rounded_tax_amount = (ceil($tax_amount * $fig) / $fig); } elseif($rounding_code == Rounding_mode::ROUND_DOWN) { - $fig = (int) str_pad('1', $decimals, '0'); - $rounded_sale_tax_amount = (floor($sale_tax_amount * $fig) / $fig); + $fig = (int)str_pad('1', $decimals, '0'); + $rounded_tax_amount = (floor($tax_amount * $fig) / $fig); } elseif($rounding_code == Rounding_mode::HALF_FIVE) { - $rounded_sale_tax_amount = round($sale_tax_amount / 5) * 5; + $rounded_tax_amount = round($tax_amount / 5) * 5; } - $sales_taxes[$row_number]['sale_tax_amount'] = $rounded_sale_tax_amount; + $taxes[$row_number]['sale_tax_amount'] = $rounded_tax_amount; } + } @@ -176,110 +290,101 @@ class Tax_lib * Determine the applicable tax code and then determine the tax amount to be applied. * If a tax amount was identified then accumulate into the sales_taxes array */ - public function apply_sales_tax(&$item, &$city, &$state, &$sales_tax_code, $register_mode, $sale_id, &$sales_taxes) + public function apply_destination_tax(&$item, $city, $state, $sales_tax_code_id, $register_mode, $sale_id, &$taxes, &$item_taxes, $line) { - $tax_code = $this->get_applicable_tax_code($register_mode, $city, $state, $sales_tax_code); + $tax_code_id = $this->get_applicable_tax_code($register_mode, $city, $state, $sales_tax_code_id); // If tax code cannot be determined or the price is zero then skip this item - if($tax_code != '' && $item['price'] != 0) + if($tax_code_id != -1 && $item['price'] != 0) { - $tax_rate = 0.0000; - $rounding_code = Rounding_mode::HALF_UP; + $tax_decimals = tax_decimals(); - $tax_code_obj = $this->CI->Tax->get_info($tax_code); - $tax_category_id = $item['tax_category_id']; + $tax_definition = $this->CI->Tax->get_taxes($tax_code_id, $item['tax_category_id']); - if($tax_category_id != 0) + // The tax basis should be returned at the currency scale + $tax_basis = $this->CI->sale_lib->get_item_total($item['quantity'], $item['price'], $item['discount'], $item['discount_type'], TRUE); + + $row = 0; + + $last_cascade_sequence = 0; + $cascade_tax_amount = 0.0; + + foreach($tax_definition as $tax) { - $tax_rate_info = $this->CI->Tax->get_rate_info($tax_code, $tax_category_id); - if($tax_rate_info) + $cascade_sequence = $tax['cascade_sequence']; + if($cascade_sequence != $last_cascade_sequence) { - $tax_rate = $tax_rate_info->tax_rate; - $rounding_code = $tax_rate_info->rounding_code; + $last_cascade_sequence = $cascade_sequence; + $tax_basis = $tax_basis + $cascade_tax_amount; + } + + $tax_rate = $tax['tax_rate']; + $rounding_code = $tax['tax_rounding_code']; + + // This computes tax for each line item and adds it to the tax type total + + $tax_type = $tax['tax_type']; + + if($tax_type == Tax_lib::TAX_TYPE_INCLUDED) + { + $tax_amount = $this->get_included_tax($item['quantity'], $item['price'], $item['discount'], $tax_rate, $tax_decimals, $rounding_code); } else { - $tax_rate = $tax_code_obj->tax_rate; - $rounding_code = $tax_code_obj->rounding_code; + $tax_amount = $this->get_tax_for_amount($tax_basis, $tax_rate, $rounding_code, $tax_decimals); + $cascade_tax_amount = $cascade_tax_amount + $tax_amount; } + + if($tax_amount != 0) + { + + $this->update_taxes($taxes, $tax_type, $tax['tax_group'], $tax_rate, $tax_basis, $tax_amount, $tax['tax_group_sequence'], $rounding_code, $sale_id, $tax['tax_group'], $tax_code_id, $tax['rate_jurisdiction_id'], $item['tax_category_id']); + } + + $item_taxes_detail = array(); + $item_taxes_detail['line'] = $line; + $item_taxes_detail['item_id'] = $item['item_id']; + $item_taxes_detail['name'] = $tax['tax_group']; + $item_taxes_detail['percent'] = $tax['tax_rate']; + $item_taxes_detail['tax_type'] = $tax_type; + $item_taxes_detail['rounding_code'] = $rounding_code; + $item_taxes_detail['cascade_sequence'] = $cascade_sequence; + $item_taxes_detail['item_tax_amount'] = $tax_amount; + $item_taxes_detail['sales_tax_code_id'] = $tax_code_id; + $item_taxes_detail['jurisdiction_id'] = $tax['rate_jurisdiction_id']; + $item_taxes_detail['tax_category_id'] = $tax['rate_tax_category_id']; + $item_taxes_detail['tax_group_sequence'] = $tax['tax_group_sequence']; + + $item_taxes[] = $item_taxes_detail; } - - if($tax_category_id != 0) - { - $tax_rate_info = $this->CI->Tax->get_rate_info($tax_code, $tax_category_id); - $tax_rate = $tax_rate_info->tax_rate; - $rounding_code = $tax_rate_info->rounding_code; - $tax_group_sequence = $tax_rate_info->tax_group_sequence; - $tax_category = $tax_rate_info->tax_category; - } - else - { - $tax_rate = $tax_code_obj->tax_rate; - $rounding_code = $tax_code_obj->rounding_code; - $tax_group_sequence = $tax_code_obj->tax_group_sequence; - $tax_category = $tax_code_obj->tax_category; - } - - $decimals = tax_decimals(); - - // The tax basis should be returned at the currency scale - $tax_basis = $this->CI->sale_lib->get_item_total($item['quantity'], $item['price'], $item['discount'], TRUE); - $tax_amount = $this->get_sales_tax_for_amount($tax_basis, $tax_rate, $rounding_code, $decimals); - - $tax_group = (float)$tax_rate . '% ' . $tax_category; - $tax_type = Tax_lib::TAX_TYPE_SALES; - - if($tax_amount != 0) - { - $this->update_sales_taxes($sales_taxes, $tax_type, $tax_group, $tax_rate, $tax_basis, $tax_amount, $tax_group_sequence, $rounding_code, $sale_id, $tax_category, $tax_code); - } - - // input : register_mode - // input : city - // input : state - // input : sales_tax_code - // input : $item['price'] - // input : $item['tax_category_id'] - // input : $item['quantity'] - // input : $item['price'] - // input : $item['discount'] - // both : $sales_taxes - // output : tax_details['tax_rate'] - // output : tax_details['rounding_code'] - // output : tax_details['tax_group_sequence'] - // output : tax_details['tax_code'] - - $tax_details = array('item_tax_amount' => $tax_amount, 'tax_group' => $tax_group, 'tax_name' => $tax_category, 'tax_rate' => $tax_rate, 'rounding_code' => $rounding_code, 'tax_group_sequence' => $tax_group_sequence, 'tax_code' => $tax_code); - - return $tax_details; + return; } else { - $tax_details = array('item_tax_amount' => 0.0000, 'tax_group' => '', 'tax_name' => '', 'tax_rate' => 0.0000, 'rounding_code' => '0', 'tax_group_sequence' => '0', 'tax_code' => ''); - return $tax_details; + return; } } - public function get_applicable_tax_code($register_mode, $city, $state, $sales_tax_code) + public function get_applicable_tax_code($register_mode, $city, $state, $sales_tax_code_id) { + if($register_mode == "sale") { - $tax_code = $this->CI->config->config['default_origin_tax_code']; // overrides customer assigned code + $sales_tax_code_id = $this->CI->config->config['default_tax_code']; // overrides customer assigned code } else { - if($sales_tax_code == '') + if($sales_tax_code_id == NULL || $sales_tax_code_id == 0) { - $tax_code = $this->CI->Tax->get_sales_tax_code($city, $state); - } - else - { - // Use the customer assigned tax rate code - $tax_code = $sales_tax_code; + $sales_tax_code_id = $this->CI->Tax_code->get_sales_tax_code($city, $state); + + if($sales_tax_code_id == NULL || $sales_tax_code_id == 0) { + $sales_tax_code_id = $this->CI->config->config['default_tax_code']; // overrides customer assigned code + } } } - return $tax_code; + return $sales_tax_code_id; } public function clean($string) @@ -288,5 +393,68 @@ class Tax_lib return preg_replace('/[^A-Za-z0-9\-]/', '', $string); // Removes special chars. } + + public function get_tax_code_options() + { + $tax_codes = $this->CI->Tax_code->get_all()->result_array(); + $tax_code_options = array(); + $tax_code_options[''] = ''; + foreach($tax_codes as $tax_code) + { + $a = $tax_code['tax_code_id']; + $b = $tax_code['tax_code_name']; + $tax_code_options[$a] = $b; + } + return $tax_code_options; + } + + public function get_tax_jurisdiction_options() + { + $tax_jurisdictions = $this->CI->Tax_jurisdiction->get_all()->result_array(); + $tax_jurisdiction_options = array(); + $tax_jurisdiction_options[0] = ''; + foreach($tax_jurisdictions as $tax_jurisdiction) + { + $a = $tax_jurisdiction['jurisdiction_id']; + $b = $tax_jurisdiction['jurisdiction_name']; + $tax_jurisdiction_options[$a] = $b; + } + return $tax_jurisdiction_options; + } + + public function get_tax_category_options() + { + $tax_categories = $this->CI->Tax_category->get_all()->result_array(); + $tax_category_options = array(); + $tax_category_options[0] = ''; + foreach($tax_categories as $tax_category) + { + $a = $tax_category['tax_category_id']; + $b = $tax_category['tax_category']; + + $tax_category_options[$a] = $b; + } + return $tax_category_options; + } + + public function get_tax_type_options($selected_tax_type) + { + $selected = 'selected=\"selected\" '; + + $s1 = ''; + $s2 = ''; + + if($selected_tax_type == Tax_lib::TAX_TYPE_EXCLUDED) + { + $s1 = $selected; + } + else if($selected_tax_type == Tax_lib::TAX_TYPE_INCLUDED) + { + $s2 = $selected; + } + return ''; + } + } ?> diff --git a/application/migrations/20170502221506_sales_tax_data.php b/application/migrations/20170502221506_sales_tax_data.php index 921d5c63e..9167c97a3 100644 --- a/application/migrations/20170502221506_sales_tax_data.php +++ b/application/migrations/20170502221506_sales_tax_data.php @@ -36,18 +36,10 @@ class Migration_Sales_Tax_Data extends CI_Migration private function upgrade_tax_history_for_sale($sale_id) { $CI =& get_instance(); - $tax_decimals = $CI->config->config['tax_decimals']; - $tax_included = $CI->config->config['tax_included']; - $customer_sales_tax_support = $CI->config->config['customer_sales_tax_support']; - - if($tax_included) - { - $tax_type = Tax_lib::TAX_TYPE_VAT; - } - else - { - $tax_type = Tax_lib::TAX_TYPE_SALES; - } + $tax_decimals = $CI->Appconfig->get('tax_decimals', 2); + $tax_included = $CI->Appconfig->get('tax_included', 0); + $customer_sales_tax_support = $CI->Appconfig->get('customer_sales_tax_support', 0); + $tax_type = $tax_included ? Tax_lib::TAX_TYPE_INCLUDED : Tax_lib::TAX_TYPE_EXCLUDED; $sales_taxes = array(); $tax_group_sequence = 0; @@ -57,11 +49,11 @@ class Migration_Sales_Tax_Data extends CI_Migration { // This computes tax for each line item and adds it to the tax type total $tax_group = (float)$item['percent'] . '% ' . $item['name']; - $tax_basis = $this->sale_lib->get_item_total($item['quantity_purchased'], $item['item_unit_price'], $item['discount_percent'], TRUE); + $tax_basis = $this->sale_lib->get_item_total($item['quantity_purchased'], $item['item_unit_price'], $item['discount_percent'], PERCENT, TRUE); $item_tax_amount = 0; if($tax_included) { - $item_tax_amount = $this->sale_lib->get_item_tax($item['quantity_purchased'], $item['item_unit_price'], $item['discount_percent'], $item['percent']); + $item_tax_amount = $this->sale_lib->get_item_tax($item['quantity_purchased'], $item['item_unit_price'], $item['discount_percent'], PERCENT, $item['percent']); } else { @@ -78,7 +70,7 @@ class Migration_Sales_Tax_Data extends CI_Migration $this->tax_lib->apply_invoice_taxing($sales_taxes); } - $this->tax_lib->round_sales_taxes($sales_taxes); + $this->tax_lib->round_taxes($sales_taxes); $this->save_sales_tax($sales_taxes); } diff --git a/application/migrations/20180429100000_upgrade_to_3_2_1.php b/application/migrations/20180501100000_upgrade_to_3_2_1.php similarity index 100% rename from application/migrations/20180429100000_upgrade_to_3_2_1.php rename to application/migrations/20180501100000_upgrade_to_3_2_1.php diff --git a/application/migrations/20181015100000_attributes.php b/application/migrations/20181015100000_attributes.php new file mode 100644 index 000000000..521e7b15d --- /dev/null +++ b/application/migrations/20181015100000_attributes.php @@ -0,0 +1,20 @@ + diff --git a/application/migrations/20190111270000_upgrade_to_3_3_0.php b/application/migrations/20190111270000_upgrade_to_3_3_0.php new file mode 100644 index 000000000..fda74aa77 --- /dev/null +++ b/application/migrations/20190111270000_upgrade_to_3_3_0.php @@ -0,0 +1,20 @@ + diff --git a/application/migrations/20190129212600_indiagst.php b/application/migrations/20190129212600_indiagst.php new file mode 100644 index 000000000..9e9836cf1 --- /dev/null +++ b/application/migrations/20190129212600_indiagst.php @@ -0,0 +1,151 @@ +db->field_exists('sales_tax_code', 'customers')) + { + return; + } + + // If number of entries is greater than zero then the tax data needs to be migrated + execute_script(APPPATH . 'migrations/sqlscripts/3.3.0_indiagst.sql'); + + error_log('Migrating tax configuration'); + + $count_of_tax_codes = $this->get_count_of_tax_code_entries(); + if($count_of_tax_codes > 0) + { + $this->migrate_tax_code_data(); + } + + + $this->migrate_customer_tax_codes(); + + $count_of_rate_entries = $this->get_count_of_rate_entries(); + + if($count_of_rate_entries > 0) + { + $this->migrate_tax_rates(); + } + + $count_of_sales_taxes_entries = $this->get_count_of_sales_taxes_entries(); + if($count_of_sales_taxes_entries > 0) + { + $this->migrate_sales_taxes_data(); + } + + error_log('Migrating tax configuration completed'); + } + + public function down() + { + $this->drop_backups(); + } + + private function get_count_of_tax_code_entries() + { + $this->db->select('COUNT(*) as count'); + $this->db->from('tax_codes_backup'); + + return $this->db->get()->row()->count; + } + + private function get_count_of_sales_taxes_entries() + { + $this->db->select('COUNT(*) as count'); + $this->db->from('sales_taxes'); + + return $this->db->get()->row()->count; + } + + private function get_count_of_rate_entries() + { + $this->db->select('COUNT(*) as count'); + $this->db->from('tax_code_rates_backup'); + + return $this->db->get()->row()->count; + } + + /* + * This copies the old tax code configuration into the new tax code configuration + * assigning a tax_code_id id to the entry This only needs to be done if there are + * tax codes in the table. + */ + private function migrate_tax_code_data() + { + $this->db->query('INSERT INTO ' . $this->db->dbprefix('tax_codes') . ' (tax_code, tax_code_name, city, state) + SELECT tax_code, tax_code_name, city, state FROM ' . $this->db->dbprefix('tax_codes_backup')); + + } + + /* + * The previous upgrade script added the new column to the customers table. + * This will assign a tax code id using the tax code field that was left in place on the customer table. + * After it is complete then it will drop the old customer tax code. + * This MUST run so that the old tax code is dropped + */ + private function migrate_customer_tax_codes() + { + $this->db->query('UPDATE ' . $this->db->dbprefix('customers') . ' AS fa SET fa.sales_tax_code_id = ( + SELECT tax_code_id FROM ' . $this->db->dbprefix('tax_codes') . ' AS fb where fa.sales_tax_code = fb.tax_code)'); + + $this->db->query('ALTER TABLE ' . $this->db->dbprefix('customers') . ' DROP COLUMN sales_tax_code'); + } + + /** + * The sales taxes table is undergoing a significant primary key change + * The new table assumes that sales taxes are associated with a jurisdiction + * For base taxes and the older tax system the tax jurisdiction code table will be + * initialized with an entry that is used to represent a dummy or consolidated jurisdiction. + * If there is only one tax jurisdiction then it can be renamed and life moves on. + * If the user wants to start reporting taxes by jurisdiction then the new jurisdictions need + * to be created and defined manually AFTER the upgrade. + * CONVERTING OLD TAX DATA TO BE SPLIT OUT BY JURISDICTION IS BEYOND THE SCOPE OF THIS EFFORT + */ + private function migrate_sales_taxes_data() + { + $this->db->query('INSERT INTO ' . $this->db->dbprefix('sales_taxes') + . ' (sale_id, jurisdiction_id, tax_category_id, tax_type, tax_group, sale_tax_basis, sale_tax_amount, print_sequence, ' + . '`name`, tax_rate, sales_tax_code_id, rounding_code) ' + . 'select sale_id, rate_jurisdiction_id, rate_tax_category_id, tax_type, tax_group, sale_tax_basis, sale_tax_amount, ' + . 'print_sequence, `name`, A.tax_rate, tax_code_id, rounding_code ' + . 'from ' . $this->db->dbprefix('sales_taxes_backup') . ' AS A ' + . 'left outer join ' . $this->db->dbprefix('tax_codes') . ' AS B on sales_tax_code = tax_code ' + . 'left outer join ' . $this->db->dbprefix('tax_rates') . ' AS C on tax_code_id = rate_tax_code_id and A.tax_rate = C.tax_rate ' + . 'order by sale_id'); + } + + private function migrate_tax_rates() + { + // create a dummy jurisdiction record and retrieve the jurisdiction rate id + + $this->db->query('INSERT INTO ' . $this->db->dbprefix('tax_jurisdictions') . ' (jurisdiction_name, tax_group, tax_type, reporting_authority, ' + . "tax_group_sequence, cascade_sequence, deleted) VALUES ('Jurisdiction1','TaxGroup1','1','Authority1',1,0,'0')"); + + $jurisdiction_id = $this->db->query('SELECT jurisdiction_id FROM ' . $this->db->dbprefix('tax_jurisdictions') . " WHERE jurisdiction_name = 'Jurisdiction1'")->row()->jurisdiction_id; + + + // Insert old tax_code rates data into the new tax rates table + + $this->db->query('INSERT INTO ' . $this->db->dbprefix('tax_rates') + . ' (rate_tax_category_id, rate_jurisdiction_id, rate_tax_code_id, tax_rate, tax_rounding_code) ' + . 'SELECT rate_tax_category_id, ' . $jurisdiction_id . ', tax_code_id, tax_rate, rounding_code FROM ' + . $this->db->dbprefix('tax_code_rates_backup') . ' JOIN ' . $this->db->dbprefix('tax_codes') + . ' ON tax_code = rate_tax_code'); + } + + private function drop_backups() + { + $this->db->query('DROP TABLE IF EXISTS' . $this->db->dbprefix('tax_codes_backup')); + $this->db->query('DROP TABLE IF EXISTS' . $this->db->dbprefix('sales_taxes_backup')); + $this->db->query('DROP TABLE IF EXISTS' . $this->db->dbprefix('tax_code_rates_backup')); + } +} +?> diff --git a/application/migrations/sqlscripts/3.0.2_to_3.1.1.sql b/application/migrations/sqlscripts/3.0.2_to_3.1.1.sql index a74b193d1..f1b80715b 100644 --- a/application/migrations/sqlscripts/3.0.2_to_3.1.1.sql +++ b/application/migrations/sqlscripts/3.0.2_to_3.1.1.sql @@ -69,7 +69,8 @@ INSERT INTO `ospos_app_config` (`key`, `value`) VALUES ('last_used_invoice_number', '0'), ('last_used_quote_number', '0'), ('line_sequence', '0'), -('dinner_table_enable', '0'); +('dinner_table_enable', '0'), +('customer_sales_tax_support', '0'); -- -- Table structure for table `ospos_customer_packages` @@ -259,15 +260,8 @@ DELETE FROM `ospos_tax_categories` where tax_category_id in (0, 1, 2, 3); ALTER TABLE `ospos_tax_categories` MODIFY COLUMN `tax_category_id` int(10) NOT NULL AUTO_INCREMENT; -INSERT INTO `ospos_tax_categories` ( `tax_category_id`, `tax_category`, `tax_group_sequence` ) VALUES -(1, 'Standard', 10), -(2, 'Service', 12), -(3, 'Alcohol', 11); - ALTER TABLE `ospos_items` - MODIFY COLUMN `tax_category_id` int(10) NOT NULL DEFAULT 1; - -UPDATE `ospos_items` SET `tax_category_id` = 1 WHERE `tax_category_id` = 0; + MODIFY COLUMN `tax_category_id` int(10) DEFAULT NULL; -- If you have added any tax codes, the following will correct the rate_tax_category_id on the tax_code_rates table, -- but you might need to add more UPDATE statements depending on how may tax codes and/or tax categories you've added @@ -415,3 +409,4 @@ ALTER TABLE ospos_items -- Change language code en to be en-US UPDATE `ospos_app_config` SET `value` = 'en-US' WHERE `key` = 'language_code' AND `value` = 'en'; + diff --git a/application/migrations/sqlscripts/3.1.1_to_3.2.0.sql b/application/migrations/sqlscripts/3.1.1_to_3.2.0.sql index de433753f..4fa20ba59 100644 --- a/application/migrations/sqlscripts/3.1.1_to_3.2.0.sql +++ b/application/migrations/sqlscripts/3.1.1_to_3.2.0.sql @@ -142,9 +142,6 @@ CREATE TABLE IF NOT EXISTS `ospos_expense_categories` ( `deleted` int(1) NOT NULL DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `ospos_expense_categories` (`expense_category_id`, `category_name`, `category_description`) VALUES -(1, 'Utilities', 'Water'); - -- Table structure for table `ospos_expenses` @@ -160,12 +157,6 @@ CREATE TABLE IF NOT EXISTS `ospos_expenses` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- Dumping data for table `ospos_expenses` - -INSERT INTO `ospos_expenses` (`expense_id`, `date`, `amount`, `expense_category_id`, `description`, `employee_id`) VALUES -(1, '2017-04-20 07:00:00', '15.00', 1, 'Water', 1); - - -- Indexes for table `ospos_expense_categories` ALTER TABLE `ospos_expense_categories` diff --git a/application/migrations/sqlscripts/3.2.0_to_3.2.1.sql b/application/migrations/sqlscripts/3.2.0_to_3.2.1.sql index ac92db486..d7c93906b 100644 --- a/application/migrations/sqlscripts/3.2.0_to_3.2.1.sql +++ b/application/migrations/sqlscripts/3.2.0_to_3.2.1.sql @@ -13,3 +13,14 @@ ALTER TABLE `ospos_customers` INSERT INTO `ospos_app_config` (`key`, `value`) VALUES ('enforce_privacy', '0'); + + +-- Add print receipt autoreturn delay + +INSERT INTO `ospos_app_config` (`key`, `value`) VALUES +('print_delay_autoreturn', '0'); + + +-- Insure that the receiving quantity is not zero + +UPDATE ospos_receivings_items SET receiving_quantity = 1 WHERE receiving_quantity = 0; diff --git a/application/migrations/sqlscripts/3.2.1_to_3.3.0.sql b/application/migrations/sqlscripts/3.2.1_to_3.3.0.sql new file mode 100644 index 000000000..eb39e4595 --- /dev/null +++ b/application/migrations/sqlscripts/3.2.1_to_3.3.0.sql @@ -0,0 +1,153 @@ +-- +-- Add support for Multi-Package Items +-- + +INSERT INTO `ospos_app_config` (`key`, `value`) VALUES +('multi_pack_enabled', '0'); + +ALTER TABLE `ospos_items` + ADD COLUMN `qty_per_pack` decimal(15,3) NOT NULL DEFAULT 1 AFTER `tax_category_id`, + ADD COLUMN `pack_name` varchar(8) DEFAULT 'Each' AFTER `qty_per_pack`, + ADD COLUMN `low_sell_item_id` int(10) DEFAULT 0 AFTER `pack_name`; + +UPDATE `ospos_items` + SET `low_sell_item_id` = `item_id` + WHERE `low_sell_item_id` = 0; + +-- +-- Add support for Discount on Sales Fixed +-- + +INSERT INTO `ospos_app_config` (`key`, `value`) VALUES +('default_sales_discount_type', '0'); + +ALTER TABLE `ospos_item_kits` + CHANGE COLUMN `kit_discount_percent` `kit_discount` DECIMAL(15,2) NOT NULL DEFAULT 0 AFTER `item_id`, + ADD COLUMN `kit_discount_type` TINYINT(2) NOT NULL DEFAULT '0' AFTER `kit_discount`; + +ALTER TABLE `ospos_customers` + CHANGE COLUMN `discount_percent` `discount` DECIMAL(15,2) NOT NULL DEFAULT 0 AFTER `sales_tax_code`, + ADD COLUMN `discount_type` TINYINT(2) NOT NULL DEFAULT '0' AFTER `discount`; + +ALTER TABLE `ospos_sales_items` + CHANGE COLUMN `discount_percent` `discount` DECIMAL(15,2) NOT NULL DEFAULT 0 AFTER `item_unit_price`, + ADD COLUMN `discount_type` TINYINT(2) NOT NULL DEFAULT '0' AFTER `discount`; + +ALTER TABLE `ospos_receivings_items` + CHANGE COLUMN `discount_percent` `discount` DECIMAL(15,2) NOT NULL DEFAULT 0 AFTER `item_unit_price`, + ADD COLUMN `discount_type` TINYINT(2) NOT NULL DEFAULT '0' AFTER `discount`; + +-- +-- Add support for module cashups +-- + +-- Set config module sort number to one of the latest + +UPDATE `ospos_modules` +SET `sort` = 900 +WHERE `name_lang_key` = 'module_config'; + +-- Add cashup module + +INSERT INTO `ospos_modules` (`name_lang_key`, `desc_lang_key`, `sort`, `module_id`) VALUES +('module_cashups', 'module_cashups_desc', 110, 'cashups'); + +INSERT INTO `ospos_permissions` (`permission_id`, `module_id`) VALUES +('cashups', 'cashups'); + +INSERT INTO `ospos_grants` (`permission_id`, `person_id`) VALUES +('cashups', 1); + +-- Table structure for table `ospos_cash_up` + +CREATE TABLE `ospos_cash_up` ( + `cashup_id` int(10) NOT NULL, + `open_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `close_date` timestamp NULL, + `open_amount_cash` decimal(15,2) NOT NULL, + `transfer_amount_cash` decimal(15,2) NOT NULL, + `note` int(1) NOT NULL, + `closed_amount_cash` decimal(15,2) NOT NULL, + `closed_amount_card` decimal(15,2) NOT NULL, + `closed_amount_check` decimal(15,2) NOT NULL, + `closed_amount_total` decimal(15,2) NOT NULL, + `description` varchar(255) NOT NULL, + `open_employee_id` int(10) NOT NULL, + `close_employee_id` int(10) NOT NULL, + `deleted` int(1) NOT NULL DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Indexes for table `ospos_cash_up` + +ALTER TABLE `ospos_cash_up` + ADD PRIMARY KEY (`cashup_id`), + ADD KEY `open_employee_id` (`open_employee_id`), + ADD KEY `close_employee_id` (`close_employee_id`), + ADD CONSTRAINT `ospos_cash_up_ibfk_1` FOREIGN KEY (`open_employee_id`) REFERENCES `ospos_employees` (`person_id`), + ADD CONSTRAINT `ospos_cash_up_ibfk_2` FOREIGN KEY (`close_employee_id`) REFERENCES `ospos_employees` (`person_id`); + +-- AUTO_INCREMENT for table `ospos_cash_up` + +ALTER TABLE `ospos_cash_up` + MODIFY `cashup_id` int(10) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1; + +-- Change collation on columns to be utf8_general_ci + +ALTER TABLE ospos_cash_up CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; +ALTER TABLE ospos_expense_categories CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; +ALTER TABLE ospos_expenses CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; + +-- Add amount due + +ALTER TABLE `ospos_cash_up` + ADD `closed_amount_due` decimal(15,2) NOT NULL; + +-- +-- Add Suppliers category +-- + +ALTER TABLE `ospos_suppliers` + ADD COLUMN `category` TINYINT NOT NULL; + +UPDATE `ospos_suppliers` + SET `category` = 0; + + +-- +-- Link Expenses with Suppliers +-- + +-- Add supplier id + +ALTER TABLE `ospos_expenses` + ADD COLUMN `supplier_id` int(10) NULL; + +-- Link suppliers + +UPDATE `ospos_expenses` + INNER JOIN `ospos_suppliers` + ON `ospos_expenses`.`supplier_name` = `ospos_suppliers`.`company_name` +SET `ospos_expenses`.`supplier_id` = `ospos_suppliers`.`person_id`; + +-- Save name in description for those expenses whose supplier isn't registered + +UPDATE `ospos_expenses` + SET `description` = CONCAT(`description`, CONCAT('\nSupplier name: ', `supplier_name`)) + WHERE `supplier_id` is NULL; + +-- Add foreign key + +ALTER TABLE `ospos_expenses` + ADD CONSTRAINT `ospos_expenses_ibfk_3` FOREIGN KEY (`supplier_id`) REFERENCES `ospos_suppliers` (`person_id`); + +-- Delete supplier name + +ALTER TABLE `ospos_expenses` + DROP COLUMN `supplier_name`; + +INSERT INTO `ospos_app_config` (`key`, `value`) VALUES + ('default_receivings_discount_type', '0'), + ('default_receivings_discount', '0'); + +ALTER TABLE `ospos_suppliers` + CHANGE COLUMN `tax_id` `tax_id` VARCHAR(32) NULL; \ No newline at end of file diff --git a/application/migrations/sqlscripts/3.3.0_indiagst.sql b/application/migrations/sqlscripts/3.3.0_indiagst.sql new file mode 100644 index 000000000..f17bb2ea8 --- /dev/null +++ b/application/migrations/sqlscripts/3.3.0_indiagst.sql @@ -0,0 +1,100 @@ +-- -------------------------------- +-- Start of India GST Tax Changes +-- -------------------------------- + +INSERT INTO `ospos_app_config` (`key`, `value`) VALUES +('include_hsn', '0'), +('invoice_type', 'invoice'), +('default_tax_jurisdiction', ''), +('tax_id', ''); + +UPDATE `ospos_app_config` + SET `key` = 'use_destination_based_tax' + WHERE `key` = 'customer_sales_tax_support'; + +UPDATE `ospos_app_config` + SET `key` = 'default_tax_code' + WHERE `key` = 'default_origin_tax_code'; + +RENAME TABLE `ospos_tax_codes` TO `ospos_tax_codes_backup`; + +CREATE TABLE IF NOT EXISTS `ospos_tax_codes` ( + `tax_code_id` int(11) NOT NULL AUTO_INCREMENT, + `tax_code` varchar(32) NOT NULL, + `tax_code_name` varchar(255) NOT NULL DEFAULT '', + `city` varchar(255) NOT NULL DEFAULT '', + `state` varchar(255) NOT NULL DEFAULT '', + `deleted` int(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`tax_code_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `ospos_customers` + ADD COLUMN `tax_id` varchar(32) NOT NULL DEFAULT '' AFTER `taxable`, + ADD COLUMN `sales_tax_code_id` int(11) DEFAULT NULL AFTER `tax_id`; + +ALTER TABLE `ospos_items` + ADD COLUMN `hsn_code` varchar(32) NOT NULL DEFAULT '' AFTER `low_sell_item_id`; + +ALTER TABLE `ospos_sales_items_taxes` + ADD COLUMN `sales_tax_code_id` int(11) DEFAULT NULL AFTER `item_tax_amount`, + ADD COLUMN `jurisdiction_id` int(11) DEFAULT NULL AFTER `sales_tax_code_id`, + ADD COLUMN `tax_category_id` int(11) DEFAULT NULL AFTER `jurisdiction_id`, + DROP COLUMN `cascade_tax`; + +RENAME TABLE `ospos_sales_taxes` TO `ospos_sales_taxes_backup`; + +CREATE TABLE `ospos_sales_taxes` ( + `sales_taxes_id` int(11) NOT NULL AUTO_INCREMENT, + `sale_id` int(10) NOT NULL, + `jurisdiction_id` int(11) DEFAULT NULL, + `tax_category_id` int(11) DEFAULT NULL, + `tax_type` smallint(2) NOT NULL, + `tax_group` varchar(32) NOT NULL, + `sale_tax_basis` decimal(15,4) NOT NULL, + `sale_tax_amount` decimal(15,4) NOT NULL, + `print_sequence` tinyint(2) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL, + `tax_rate` decimal(15,4) NOT NULL, + `sales_tax_code_id` int(11) DEFAULT NULL, + `rounding_code` tinyint(2) NOT NULL DEFAULT 0, + PRIMARY KEY (`sales_taxes_id`), + KEY `print_sequence` (`sale_id`,`print_sequence`,`tax_group`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `ospos_tax_jurisdictions` ( + `jurisdiction_id` int(11) NOT NULL AUTO_INCREMENT, + `jurisdiction_name` varchar(255) DEFAULT NULL, + `tax_group` varchar(32) NOT NULL, + `tax_type` smallint(2) NOT NULL, + `reporting_authority` varchar(255) DEFAULT NULL, + `tax_group_sequence` tinyint(2) NOT NULL DEFAULT 0, + `cascade_sequence` tinyint(2) NOT NULL DEFAULT 0, + `deleted` int(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`jurisdiction_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; + +ALTER TABLE `ospos_suppliers` + ADD COLUMN `tax_id` varchar(32) NOT NULL DEFAULT '' AFTER `account_number`; + +ALTER TABLE `ospos_tax_categories` + ADD COLUMN `deleted` int(1) NOT NULL DEFAULT 0 AFTER `tax_group_sequence`; + +RENAME TABLE `ospos_tax_code_rates` TO `ospos_tax_code_rates_backup`; + +CREATE TABLE IF NOT EXISTS `ospos_tax_rates` ( + `tax_rate_id` int(11) NOT NULL AUTO_INCREMENT, + `rate_tax_code_id` int(11) NOT NULL, + `rate_tax_category_id` int(10) NOT NULL, + `rate_jurisdiction_id` int(11) NOT NULL, + `tax_rate` decimal(15,4) NOT NULL DEFAULT 0.0000, + `tax_rounding_code` tinyint(2) NOT NULL DEFAULT 0, + PRIMARY KEY (`tax_rate_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Add support for sales tax report + +INSERT INTO `ospos_permissions` (`permission_id`, `module_id`) VALUES +('reports_sales_taxes', 'reports'); + +INSERT INTO `ospos_grants` (`permission_id`, `person_id`, `menu_group`) VALUES +('reports_sales_taxes', 1, 'home'); diff --git a/application/migrations/sqlscripts/attributes.sql b/application/migrations/sqlscripts/attributes.sql new file mode 100644 index 000000000..66641d298 --- /dev/null +++ b/application/migrations/sqlscripts/attributes.sql @@ -0,0 +1,184 @@ + +CREATE TABLE IF NOT EXISTS `ospos_attribute_definitions` ( + `definition_id` INT(10) NOT NULL AUTO_INCREMENT, + `definition_name` VARCHAR(255) NOT NULL, + `definition_type` VARCHAR(45) NOT NULL, + `definition_flags` TINYINT(4) NOT NULL, + `definition_fk` INT(10) NULL, + `deleted` TINYINT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`definition_id`), + KEY `definition_fk` (`definition_fk`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; + + +CREATE TABLE IF NOT EXISTS `ospos_attribute_values` ( + `attribute_id` INT NOT NULL AUTO_INCREMENT, + `attribute_value` VARCHAR(255) UNIQUE NULL, + `attribute_datetime` DATETIME NULL, + PRIMARY KEY (`attribute_id`) +) ENGINE = InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; + + +CREATE TABLE IF NOT EXISTS `ospos_attribute_links` ( + `attribute_id` INT NULL, + `definition_id` INT NOT NULL, + `item_id` INT NULL, + `sale_id` INT NULL, + `receiving_id` INT NULL, + KEY `attribute_id` (`attribute_id`), + KEY `definition_id` (`definition_id`), + KEY `item_id` (`item_id`), + KEY `sale_id` (`sale_id`), + KEY `receiving_id` (`receiving_id`), + UNIQUE `attribute_links_uq1` (`attribute_id`, `definition_id`, `item_id`, `sale_id`, `receiving_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; + + +ALTER TABLE `ospos_attribute_definitions` + ADD CONSTRAINT `fk_ospos_attribute_definitions_ibfk_1` FOREIGN KEY (`definition_fk`) REFERENCES `ospos_attribute_definitions` (`definition_id`); + + +ALTER TABLE `ospos_attribute_links` + ADD CONSTRAINT `ospos_attribute_links_ibfk_1` FOREIGN KEY (`definition_id`) REFERENCES `ospos_attribute_definitions` (`definition_id`) ON DELETE CASCADE, + ADD CONSTRAINT `ospos_attribute_links_ibfk_2` FOREIGN KEY (`attribute_id`) REFERENCES `ospos_attribute_values` (`attribute_id`) ON DELETE CASCADE, + ADD CONSTRAINT `ospos_attribute_links_ibfk_3` FOREIGN KEY (`item_id`) REFERENCES `ospos_items` (`item_id`), + ADD CONSTRAINT `ospos_attribute_links_ibfk_4` FOREIGN KEY (`receiving_id`) REFERENCES `ospos_receivings` (`receiving_id`), + ADD CONSTRAINT `ospos_attribute_links_ibfk_5` FOREIGN KEY (`sale_id`) REFERENCES `ospos_sales` (`sale_id`); + +INSERT INTO `ospos_modules` (`name_lang_key`, `desc_lang_key`, `sort`, `module_id`) VALUES + ('module_attributes', 'module_attributes_desc', 107, 'attributes'); + +INSERT INTO `ospos_permissions` (`permission_id`, `module_id`) VALUES + ('attributes', 'attributes'); + +INSERT INTO `ospos_grants` (`permission_id`, `person_id`, `menu_group`) VALUES + ('attributes', 1, 'office'); + +-- migrate custom fields to text attributes +-- NOTE: items with custom attributes won't keep their selected category!! +INSERT INTO `ospos_attribute_definitions` (definition_name, definition_type, definition_flags) SELECT `value`, 'TEXT', 1 FROM ospos_app_config WHERE `key` = 'custom1_name' AND `value` <> ''; +INSERT INTO `ospos_attribute_definitions` (definition_name, definition_type, definition_flags) SELECT `value`, 'TEXT', 1 FROM ospos_app_config WHERE `key` = 'custom2_name' AND `value` <> ''; +INSERT INTO `ospos_attribute_definitions` (definition_name, definition_type, definition_flags) SELECT `value`, 'TEXT', 1 FROM ospos_app_config WHERE `key` = 'custom3_name' AND `value` <> ''; +INSERT INTO `ospos_attribute_definitions` (definition_name, definition_type, definition_flags) SELECT `value`, 'TEXT', 1 FROM ospos_app_config WHERE `key` = 'custom4_name' AND `value` <> ''; +INSERT INTO `ospos_attribute_definitions` (definition_name, definition_type, definition_flags) SELECT `value`, 'TEXT', 1 FROM ospos_app_config WHERE `key` = 'custom5_name' AND `value` <> ''; +INSERT INTO `ospos_attribute_definitions` (definition_name, definition_type, definition_flags) SELECT `value`, 'TEXT', 1 FROM ospos_app_config WHERE `key` = 'custom6_name' AND `value` <> ''; +INSERT INTO `ospos_attribute_definitions` (definition_name, definition_type, definition_flags) SELECT `value`, 'TEXT', 1 FROM ospos_app_config WHERE `key` = 'custom7_name' AND `value` <> ''; +INSERT INTO `ospos_attribute_definitions` (definition_name, definition_type, definition_flags) SELECT `value`, 'TEXT', 1 FROM ospos_app_config WHERE `key` = 'custom8_name' AND `value` <> ''; +INSERT INTO `ospos_attribute_definitions` (definition_name, definition_type, definition_flags) SELECT `value`, 'TEXT', 1 FROM ospos_app_config WHERE `key` = 'custom9_name' AND `value` <> ''; +INSERT INTO `ospos_attribute_definitions` (definition_name, definition_type, definition_flags) SELECT `value`, 'TEXT', 1 FROM ospos_app_config WHERE `key` = 'custom10_name' AND `value` <> ''; + +INSERT INTO ospos_attribute_links (definition_id, item_id) SELECT definition_id, item_id FROM ospos_attribute_definitions, ospos_app_config, ospos_items + WHERE ospos_app_config.`key` = 'custom1_name' AND ospos_app_config.`value` = ospos_attribute_definitions.definition_name AND custom1 IS NOT NULL AND custom1 != ''; +INSERT INTO ospos_attribute_links (definition_id, item_id) SELECT definition_id, item_id FROM ospos_attribute_definitions, ospos_app_config, ospos_items + WHERE ospos_app_config.`key` = 'custom2_name' AND ospos_app_config.`value` = ospos_attribute_definitions.definition_name AND custom2 IS NOT NULL AND custom2 != ''; +INSERT INTO ospos_attribute_links (definition_id, item_id) SELECT definition_id, item_id FROM ospos_attribute_definitions, ospos_app_config, ospos_items + WHERE ospos_app_config.`key` = 'custom3_name' AND ospos_app_config.`value` = ospos_attribute_definitions.definition_name AND custom3 IS NOT NULL AND custom3 != ''; +INSERT INTO ospos_attribute_links (definition_id, item_id) SELECT definition_id, item_id FROM ospos_attribute_definitions, ospos_app_config, ospos_items + WHERE ospos_app_config.`key` = 'custom4_name' AND ospos_app_config.`value` = ospos_attribute_definitions.definition_name AND custom4 IS NOT NULL AND custom4 != ''; +INSERT INTO ospos_attribute_links (definition_id, item_id) SELECT definition_id, item_id FROM ospos_attribute_definitions, ospos_app_config, ospos_items + WHERE ospos_app_config.`key` = 'custom5_name' AND ospos_app_config.`value` = ospos_attribute_definitions.definition_name AND custom5 IS NOT NULL AND custom5 != ''; +INSERT INTO ospos_attribute_links (definition_id, item_id) SELECT definition_id, item_id FROM ospos_attribute_definitions, ospos_app_config, ospos_items + WHERE ospos_app_config.`key` = 'custom6_name' AND ospos_app_config.`value` = ospos_attribute_definitions.definition_name AND custom6 IS NOT NULL AND custom6 != ''; +INSERT INTO ospos_attribute_links (definition_id, item_id) SELECT definition_id, item_id FROM ospos_attribute_definitions, ospos_app_config, ospos_items + WHERE ospos_app_config.`key` = 'custom7_name' AND ospos_app_config.`value` = ospos_attribute_definitions.definition_name AND custom7 IS NOT NULL AND custom7 != ''; +INSERT INTO ospos_attribute_links (definition_id, item_id) SELECT definition_id, item_id FROM ospos_attribute_definitions, ospos_app_config, ospos_items + WHERE ospos_app_config.`key` = 'custom8_name' AND ospos_app_config.`value` = ospos_attribute_definitions.definition_name AND custom8 IS NOT NULL AND custom8 != ''; +INSERT INTO ospos_attribute_links (definition_id, item_id) SELECT definition_id, item_id FROM ospos_attribute_definitions, ospos_app_config, ospos_items + WHERE ospos_app_config.`key` = 'custom9_name' AND ospos_app_config.`value` = ospos_attribute_definitions.definition_name AND custom9 IS NOT NULL AND custom9 != ''; +INSERT INTO ospos_attribute_links (definition_id, item_id) SELECT definition_id, item_id FROM ospos_attribute_definitions, ospos_app_config, ospos_items + WHERE ospos_app_config.`key` = 'custom10_name' AND ospos_app_config.`value` = ospos_attribute_definitions.definition_name AND custom10 IS NOT NULL AND custom10 != ''; + +INSERT IGNORE INTO ospos_attribute_values (attribute_value) SELECT DISTINCT custom1 FROM ospos_items WHERE custom1 <> '' AND '' <> (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom1_name'); +INSERT IGNORE INTO ospos_attribute_values (attribute_value) SELECT DISTINCT custom2 FROM ospos_items WHERE custom2 <> '' AND '' <> (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom2_name'); +INSERT IGNORE INTO ospos_attribute_values (attribute_value) SELECT DISTINCT custom3 FROM ospos_items WHERE custom3 <> '' AND '' <> (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom3_name'); +INSERT IGNORE INTO ospos_attribute_values (attribute_value) SELECT DISTINCT custom4 FROM ospos_items WHERE custom4 <> '' AND '' <> (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom4_name'); +INSERT IGNORE INTO ospos_attribute_values (attribute_value) SELECT DISTINCT custom5 FROM ospos_items WHERE custom5 <> '' AND '' <> (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom5_name'); +INSERT IGNORE INTO ospos_attribute_values (attribute_value) SELECT DISTINCT custom6 FROM ospos_items WHERE custom6 <> '' AND '' <> (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom6_name'); +INSERT IGNORE INTO ospos_attribute_values (attribute_value) SELECT DISTINCT custom7 FROM ospos_items WHERE custom7 <> '' AND '' <> (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom7_name'); +INSERT IGNORE INTO ospos_attribute_values (attribute_value) SELECT DISTINCT custom8 FROM ospos_items WHERE custom8 <> '' AND '' <> (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom8_name'); +INSERT IGNORE INTO ospos_attribute_values (attribute_value) SELECT DISTINCT custom9 FROM ospos_items WHERE custom9 <> '' AND '' <> (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom9_name'); +INSERT IGNORE INTO ospos_attribute_values (attribute_value) SELECT DISTINCT custom10 FROM ospos_items WHERE custom10 <> '' AND '' <> (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom10_name'); + +UPDATE ospos_attribute_links + INNER JOIN ospos_items ON ospos_attribute_links.item_id = ospos_items.item_id + INNER JOIN ospos_attribute_values ON attribute_value = custom1 + SET ospos_attribute_links.attribute_id = ospos_attribute_values.attribute_id + WHERE definition_id IN (SELECT definition_id FROM ospos_attribute_definitions + WHERE definition_name = (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom1_name' AND `value` IS NOT NULL)); + +UPDATE ospos_attribute_links + INNER JOIN ospos_items ON ospos_attribute_links.item_id = ospos_items.item_id + INNER JOIN ospos_attribute_values ON attribute_value = custom2 + SET ospos_attribute_links.attribute_id = ospos_attribute_values.attribute_id + WHERE definition_id IN (SELECT definition_id FROM ospos_attribute_definitions + WHERE definition_name = (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom2_name' AND `value` IS NOT NULL)); + +UPDATE ospos_attribute_links + INNER JOIN ospos_items ON ospos_attribute_links.item_id = ospos_items.item_id + INNER JOIN ospos_attribute_values ON attribute_value = custom3 + SET ospos_attribute_links.attribute_id = ospos_attribute_values.attribute_id + WHERE definition_id IN (SELECT definition_id FROM ospos_attribute_definitions + WHERE definition_name = (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom3_name' AND `value` IS NOT NULL)); + +UPDATE ospos_attribute_links + INNER JOIN ospos_items ON ospos_attribute_links.item_id = ospos_items.item_id + INNER JOIN ospos_attribute_values ON attribute_value = custom4 + SET ospos_attribute_links.attribute_id = ospos_attribute_values.attribute_id + WHERE definition_id IN (SELECT definition_id FROM ospos_attribute_definitions + WHERE definition_name = (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom4_name' AND `value` IS NOT NULL)); + +UPDATE ospos_attribute_links + INNER JOIN ospos_items ON ospos_attribute_links.item_id = ospos_items.item_id + INNER JOIN ospos_attribute_values ON attribute_value = custom5 + SET ospos_attribute_links.attribute_id = ospos_attribute_values.attribute_id + WHERE definition_id IN (SELECT definition_id FROM ospos_attribute_definitions + WHERE definition_name = (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom5_name' AND `value` IS NOT NULL)); + +UPDATE ospos_attribute_links + INNER JOIN ospos_items ON ospos_attribute_links.item_id = ospos_items.item_id + INNER JOIN ospos_attribute_values ON attribute_value = custom6 + SET ospos_attribute_links.attribute_id = ospos_attribute_values.attribute_id + WHERE definition_id IN (SELECT definition_id FROM ospos_attribute_definitions + WHERE definition_name = (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom6_name' AND `value` IS NOT NULL)); + +UPDATE ospos_attribute_links + INNER JOIN ospos_items ON ospos_attribute_links.item_id = ospos_items.item_id + INNER JOIN ospos_attribute_values ON attribute_value = custom7 + SET ospos_attribute_links.attribute_id = ospos_attribute_values.attribute_id + WHERE definition_id IN (SELECT definition_id FROM ospos_attribute_definitions + WHERE definition_name = (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom7_name' AND `value` IS NOT NULL)); + +UPDATE ospos_attribute_links + INNER JOIN ospos_items ON ospos_attribute_links.item_id = ospos_items.item_id + INNER JOIN ospos_attribute_values ON attribute_value = custom8 + SET ospos_attribute_links.attribute_id = ospos_attribute_values.attribute_id + WHERE definition_id IN (SELECT definition_id FROM ospos_attribute_definitions + WHERE definition_name = (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom8_name' AND `value` IS NOT NULL)); + +UPDATE ospos_attribute_links + INNER JOIN ospos_items ON ospos_attribute_links.item_id = ospos_items.item_id + INNER JOIN ospos_attribute_values ON attribute_value = custom9 + SET ospos_attribute_links.attribute_id = ospos_attribute_values.attribute_id + WHERE definition_id IN (SELECT definition_id FROM ospos_attribute_definitions + WHERE definition_name = (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom9_name' AND `value` IS NOT NULL)); + +UPDATE ospos_attribute_links + INNER JOIN ospos_items ON ospos_attribute_links.item_id = ospos_items.item_id + INNER JOIN ospos_attribute_values ON attribute_value = custom10 + SET ospos_attribute_links.attribute_id = ospos_attribute_values.attribute_id + WHERE definition_id IN (SELECT definition_id FROM ospos_attribute_definitions + WHERE definition_name = (SELECT `value` FROM ospos_app_config WHERE `key` = 'custom10_name' AND `value` IS NOT NULL)); + +ALTER TABLE `ospos_items` + DROP COLUMN `custom1`, + DROP COLUMN `custom2`, + DROP COLUMN `custom3`, + DROP COLUMN `custom4`, + DROP COLUMN `custom5`, + DROP COLUMN `custom6`, + DROP COLUMN `custom7`, + DROP COLUMN `custom8`, + DROP COLUMN `custom9`, + DROP COLUMN `custom10`; + + DELETE FROM `ospos_app_config` WHERE `key` IN ('custom1_name','custom2_name','custom3_name','custom4_name','custom5_name','custom6_name','custom7_name','custom8_name','custom9_name','custom10_name'); diff --git a/application/models/Appconfig.php b/application/models/Appconfig.php index 09cfef6ef..373c4a842 100644 --- a/application/models/Appconfig.php +++ b/application/models/Appconfig.php @@ -22,7 +22,7 @@ class Appconfig extends CI_Model return $this->db->get(); } - public function get($key) + public function get($key, $default = '') { $query = $this->db->get_where('app_config', array('key' => $key), 1); @@ -31,7 +31,7 @@ class Appconfig extends CI_Model return $query->row()->value; } - return ''; + return $default; } public function save($key, $value) diff --git a/application/models/Attribute.php b/application/models/Attribute.php new file mode 100644 index 000000000..c2edfe9b7 --- /dev/null +++ b/application/models/Attribute.php @@ -0,0 +1,466 @@ +getConstants()); + } + + /* + Determines if a given definition_id is an attribute + */ + public function exists($definition_id, $deleted = FALSE) + { + $this->db->from('attribute_definitions'); + $this->db->where('definition_id', $definition_id); + $this->db->where('deleted', $deleted); + + return ($this->db->get()->num_rows() == 1); + } + + public function link_exists($item_id, $definition_id = FALSE) + { + $this->db->where('sale_id'); + $this->db->where('receiving_id'); + $this->db->from('attribute_links'); + if(empty($definition_id)) + { + $this->db->where('definition_id <>'); + $this->db->where('attribute_id'); + } + else + { + $this->db->where('definition_id', $definition_id); + + } + $this->db->where('item_id', $item_id); + + return ($this->db->get()->num_rows() > 0); + } + + /* + Determines if a given attribute_value exists in the attribute_values table and returns the attribute_id if it does + */ + public function value_exists($attribute_value) + { + $this->db->distinct('attribute_id'); + $this->db->from('attribute_values'); + $this->db->where('attribute_value', $attribute_value); + + return $this->db->get()->row()->attribute_id; + } + + /* + Gets information about a particular attribute definition + */ + public function get_info($definition_id) + { + $this->db->select('parent_definition.definition_name AS definition_group, definition.*'); + $this->db->from('attribute_definitions AS definition'); + $this->db->join('attribute_definitions AS parent_definition', 'parent_definition.definition_id = definition.definition_fk', 'left'); + $this->db->where('definition.definition_id', $definition_id); + + $query = $this->db->get(); + + if($query->num_rows() == 1) + { + return $query->row(); + } + else + { + //Get empty base parent object, as $item_id is NOT an item + $item_obj = new stdClass(); + + //Get all the fields from items table + foreach($this->db->list_fields('attribute_definitions') as $field) + { + $item_obj->$field = ''; + } + + return $item_obj; + } + } + + /* + Performs a search on attribute definitions + */ + public function search($search, $rows = 0, $limit_from = 0, $sort = 'definition.definition_name', $order = 'asc') + { + $this->db->select('definition_group.definition_name AS definition_group, definition.*'); + $this->db->from('attribute_definitions AS definition'); + $this->db->join('attribute_definitions AS definition_group', 'definition_group.definition_id = definition.definition_fk', 'left'); + + $this->db->group_start(); + $this->db->like('definition.definition_name', $search); + $this->db->or_like('definition.definition_type', $search); + $this->db->group_end(); + $this->db->where('definition.deleted', 0); + $this->db->order_by($sort, $order); + + if($rows > 0) + { + $this->db->limit($rows, $limit_from); + } + + return $this->db->get(); + } + + public function get_attributes_by_item($item_id) + { + $this->db->from('attribute_definitions'); + $this->db->join('attribute_links', 'attribute_links.definition_id = attribute_definitions.definition_id'); + $this->db->where('item_id', $item_id); + $this->db->where('receiving_id'); + $this->db->where('sale_id'); + $this->db->where('deleted', 0); + + $results = $this->db->get()->result_array(); + + return $this->_to_array($results, 'definition_id'); + } + + public function get_values_by_definitions($definition_ids) + { + if(count($definition_ids ? : [])) + { + $this->db->from('attribute_definitions'); + + $this->db->group_start(); + $this->db->where_in('definition_fk', array_keys($definition_ids)); + $this->db->or_where_in('definition_id', array_keys($definition_ids)); + $this->db->where('definition_type !=', GROUP); + $this->db->group_end(); + + $this->db->where('deleted', 0); + + $results = $this->db->get()->result_array(); + + return $this->_to_array($results, 'definition_id'); + } + + return array(); + } + + public function get_definitions_by_type($attribute_type, $definition_id = -1) + { + $this->db->from('attribute_definitions'); + $this->db->where('definition_type', $attribute_type); + $this->db->where('deleted', 0); + + if($definition_id != -1) + { + $this->db->where('definition_id != ', $definition_id); + } + + $this->db->where('definition_fk'); + $results = $this->db->get()->result_array(); + + return $this->_to_array($results, 'definition_id', 'definition_name'); + } + + public function get_definitions_by_flags($definition_flags) + { + $this->db->from('attribute_definitions'); + $this->db->where('definition_flags &', $definition_flags); + $this->db->where('deleted', 0); + $this->db->where('definition_type <>', GROUP); + $this->db->order_by('definition_id'); + $results = $this->db->get()->result_array(); + + return $this->_to_array($results, 'definition_id', 'definition_name'); + } + + public function get_definition_names() + { + $this->db->from('attribute_definitions'); + $this->db->where('deleted', 0); + $results = $this->db->get()->result_array(); + + $definition_name = array(-1 => $this->lang->line('common_none_selected_text')); + + return $definition_name + $this->_to_array($results, 'definition_id', 'definition_name'); + } + + public function get_definition_values($definition_id) + { + $attribute_values = []; + + if($definition_id > -1) + { + $this->db->from('attribute_links'); + $this->db->join('attribute_values', 'attribute_values.attribute_id = attribute_links.attribute_id'); + $this->db->where('definition_id', $definition_id); + $this->db->where('item_id'); + + $results = $this->db->get()->result_array(); + + return $this->_to_array($results, 'attribute_id', 'attribute_value'); + } + + return $attribute_values; + } + + private function _to_array($results, $key, $value = '') + { + return array_column(array_map(function($result) use ($key, $value) { + return [$result[$key], empty($value) ? $result : $result[$value]]; + }, $results), 1, 0); + } + + /* + Gets total of rows + */ + public function get_total_rows() + { + $this->db->from('attribute_definitions'); + $this->db->where('deleted', 0); + + return $this->db->count_all_results(); + } + + /* + Get number of rows + */ + public function get_found_rows($search) + { + return $this->search($search)->num_rows(); + } + + /* + Inserts or updates a definition + */ + public function save_definition(&$definition_data, $definition_id = -1) + { + //Run these queries as a transaction, we want to make sure we do all or nothing + $this->db->trans_start(); + + if($definition_id === -1 || !$this->exists($definition_id)) + { + $success = $this->db->insert('attribute_definitions', $definition_data); + $definition_data['definition_id'] = $this->db->insert_id(); + } + else + { + $this->db->where('definition_id', $definition_id); + $success = $this->db->update('attribute_definitions', $definition_data); + $definition_data['definition_id'] = $definition_id; + } + + $this->db->trans_complete(); + + $success &= $this->db->trans_status(); + + return $success; + } + + public function get_definition_by_name($definition_name, $definition_type) + { + $this->db->from('attribute_definitions'); + $this->db->where('definition_name', $definition_name); + $this->db->where('definition_type', $definition_type); + + return $this->db->get()->row_object(); + } + + public function save_link($item_id, $definition_id, $attribute_id) + { + $this->db->trans_start(); + + if($this->link_exists($item_id, $definition_id)) + { + $this->db->where('definition_id', $definition_id); + $this->db->where('item_id', $item_id); + $this->db->where('sale_id'); + $this->db->where('receiving_id'); + $this->db->update('attribute_links', array('attribute_id' => $attribute_id)); + } + else + { + $this->db->insert('attribute_links', array('attribute_id' => $attribute_id, 'item_id' => $item_id, 'definition_id' => $definition_id)); + } + + $this->db->trans_complete(); + + return $this->db->trans_status(); + } + + public function delete_link($item_id) + { + $this->db->where('sale_id'); + $this->db->where('receiving_id'); + + return $this->db->delete('attribute_links', array('item_id' => $item_id)); + } + + public function get_link_value($item_id, $definition_id) + { + $this->db->where('item_id', $item_id); + $this->db->where('definition_id', $definition_id); + $this->db->where('sale_id'); + $this->db->where('receiving_id'); + + return $this->db->get('attribute_links')->row_object(); + } + + public function get_link_values($item_id, $sale_receiving_fk, $id, $definition_flags) + { + $this->db->select('GROUP_CONCAT(attribute_value SEPARATOR ", ") AS attribute_values, GROUP_CONCAT(attribute_datetime SEPARATOR ", ") AS attribute_datetimevalues'); + $this->db->from('attribute_links'); + $this->db->join('attribute_values', 'attribute_values.attribute_id = attribute_links.attribute_id'); + $this->db->join('attribute_definitions', 'attribute_definitions.definition_id = attribute_links.definition_id'); + $this->db->where('definition_type <>', GROUP); + $this->db->where('deleted', 0); + + if(!empty($id)) + { + $this->db->where($sale_receiving_fk, $id); + } + else + { + $this->db->where('sale_id'); + $this->db->where('receiving_id'); + } + $this->db->where('item_id', (int) $item_id); + $this->db->where('definition_flags & ', $definition_flags); + + $results = $this->db->get(); + + if ($results->num_rows() > 0) + { + $row_object = $results->row_object(); + + $datetime_values = explode(', ', $row_object->attribute_datetimevalues); + $attribute_values = array(); + + foreach (array_filter($datetime_values) as $datetime_value) + { + $attribute_values[] = to_datetime(strtotime($datetime_value)); + } + + return implode(',', $attribute_values) . $row_object->attribute_values; + } + return ""; + } + + public function get_attribute_value($item_id, $definition_id) + { + $this->db->from('attribute_values'); + $this->db->join('attribute_links', 'attribute_links.attribute_id = attribute_values.attribute_id'); + $this->db->where('definition_id', $definition_id); + $this->db->where('sale_id'); + $this->db->where('receiving_id'); + $this->db->where('item_id', (int) $item_id); + + return $this->db->get()->row_object(); + } + + public function copy_attribute_links($item_id, $sale_receiving_fk, $id) + { + $this->db->query( + 'INSERT INTO ospos_attribute_links (item_id, definition_id, attribute_id, ' . $sale_receiving_fk . ') + SELECT ' . $this->db->escape($item_id) . ', definition_id, attribute_id, ' . $this->db->escape($id) . ' + FROM ' . $this->db->dbprefix('attribute_links') . ' + WHERE item_id = ' . $this->db->escape($item_id) . ' AND sale_id IS NULL AND receiving_id IS NULL' + ); + } + + public function get_suggestions($definition_id, $term) + { + $suggestions = array(); + $this->db->distinct(); + $this->db->select('attribute_value, attribute_values.attribute_id'); + $this->db->from('attribute_definitions AS definition'); + $this->db->join('attribute_links', 'attribute_links.definition_id = definition.definition_id'); + $this->db->join('attribute_values', 'attribute_values.attribute_id = attribute_links.attribute_id'); + $this->db->like('attribute_value', $term); + $this->db->where('deleted', 0); + $this->db->where('definition.definition_id', $definition_id); + $this->db->order_by('attribute_value'); + foreach($this->db->get()->result() as $row) + { + $row_array = (array) $row; + $suggestions[] = array('value' => $row_array['attribute_id'], 'label' => $row_array['attribute_value']); + } + + return $suggestions; + } + + public function save_value($attribute_value, $definition_id, $item_id = FALSE, $attribute_id = FALSE, $definition_type = DROPDOWN) + { + $this->db->trans_start(); + + if(empty($attribute_id) || empty($item_id)) + { + if($definition_type != DATETIME) + { + $attribute_id_check = $this->value_exists($attribute_value); + if(empty($attribute_id_check)) + { + $this->db->insert('attribute_values', array('attribute_value' => $attribute_value)); + $attribute_id = $this->db->insert_id(); + } + else + { + $attribute_id = $attribute_id_check; + } + } + else + { + $this->db->insert('attribute_values', array('attribute_datetime' => date('Y-m-d H:i:s', strtotime($attribute_value)))); + $attribute_id = $this->db->insert_id(); + } + + $this->db->insert('attribute_links', array( + 'attribute_id' => empty($attribute_id) ? NULL : $attribute_id, + 'item_id' => empty($item_id) ? NULL : $item_id, + 'definition_id' => $definition_id)); + } + else + { + $this->db->where('attribute_id', $attribute_id); + $this->db->update('attribute_values', array('attribute_value' => $attribute_value)); + } + + $this->db->trans_complete(); + + return $attribute_id; + } + + public function delete_value($attribute_value, $definition_id) + { + return $this->db->query("DELETE atrv, atrl FROM " . $this->db->dbprefix('attribute_values') . " atrv, " . $this->db->dbprefix('attribute_links') . " atrl " . + "WHERE atrl.attribute_id = atrv.attribute_id AND atrv.attribute_value = " . $this->db->escape($attribute_value) . " AND atrl.definition_id = " . $this->db->escape($definition_id)); + } + + public function delete_definition($definition_id) + { + $this->db->where('definition_id', $definition_id); + + return $this->db->update('attribute_definitions', array('deleted' => 1)); + } + + public function delete_definition_list($definition_ids) + { + $this->db->where_in('definition_id', $definition_ids); + + return $this->db->update('attribute_definitions', array('deleted' => 1)); + } +} diff --git a/application/models/Cashup.php b/application/models/Cashup.php new file mode 100644 index 000000000..de03509b9 --- /dev/null +++ b/application/models/Cashup.php @@ -0,0 +1,212 @@ +db->from('cash_up'); + $this->db->where('cashup_id', $cashup_id); + + return ($this->db->get()->num_rows() == 1); + } + + /* + Gets employee info + */ + public function get_employee($cashup_id) + { + $this->db->from('cash_up'); + $this->db->where('cashup_id', $cashup_id); + + return $this->Employee->get_info($this->db->get()->row()->employee_id); + } + + public function get_multiple_info($cash_up_ids) + { + $this->db->from('cash_up'); + $this->db->where_in('cashup_id', $cashup_ids); + $this->db->order_by('cashup_id', 'asc'); + + return $this->db->get(); + } + + /* + Gets rows + */ + public function get_found_rows($search, $filters) + { + return $this->search($search, $filters, 0, 0, 'cashup_id', 'asc', TRUE); + } + + /* + Searches cashups + */ + public function search($search, $filters, $rows = 0, $limit_from = 0, $sort = 'cashup_id', $order = 'asc', $count_only = FALSE) + { + // get_found_rows case + if($count_only == TRUE) + { + $this->db->select('COUNT(cash_up.cashup_id) as count'); + } + + $this->db->select(' + cash_up.cashup_id, + MAX(cash_up.open_date) AS open_date, + MAX(cash_up.close_date) AS close_date, + MAX(cash_up.open_amount_cash) AS open_amount_cash, + MAX(cash_up.transfer_amount_cash) AS transfer_amount_cash, + MAX(cash_up.closed_amount_cash) AS closed_amount_cash, + MAX(cash_up.closed_amount_due) AS closed_amount_due, + MAX(cash_up.closed_amount_card) AS closed_amount_card, + MAX(cash_up.closed_amount_check) AS closed_amount_check, + MAX(cash_up.closed_amount_total) AS closed_amount_total, + MAX(cash_up.description) AS description, + MAX(cash_up.note) AS note, + MAX(cash_up.open_employee_id) AS open_employee_id, + MAX(cash_up.close_employee_id) AS close_employee_id, + MAX(open_employees.first_name) AS open_first_name, + MAX(open_employees.last_name) AS open_last_name, + MAX(close_employees.first_name) AS close_first_name, + MAX(close_employees.last_name) AS close_last_name + '); + $this->db->from('cash_up AS cash_up'); + $this->db->join('people AS open_employees', 'open_employees.person_id = cash_up.open_employee_id', 'LEFT'); + $this->db->join('people AS close_employees', 'close_employees.person_id = cash_up.close_employee_id', 'LEFT'); + + $this->db->group_start(); + $this->db->like('cash_up.open_date', $search); + $this->db->or_like('open_employees.first_name', $search); + $this->db->or_like('open_employees.last_name', $search); + $this->db->or_like('close_employees.first_name', $search); + $this->db->or_like('close_employees.last_name', $search); + $this->db->or_like('cash_up.closed_amount_total', $search); + $this->db->or_like('CONCAT(open_employees.first_name, " ", open_employees.last_name)', $search); + $this->db->or_like('CONCAT(close_employees.first_name, " ", close_employees.last_name)', $search); + $this->db->group_end(); + + $this->db->where('cash_up.deleted', $filters['is_deleted']); + + if(empty($this->config->item('date_or_time_format'))) + { + $this->db->where('DATE_FORMAT(cash_up.open_date, "%Y-%m-%d") BETWEEN ' . $this->db->escape($filters['start_date']) . ' AND ' . $this->db->escape($filters['end_date'])); + } + else + { + $this->db->where('cash_up.open_date BETWEEN ' . $this->db->escape(rawurldecode($filters['start_date'])) . ' AND ' . $this->db->escape(rawurldecode($filters['end_date']))); + } + + $this->db->group_by('cashup_id'); + + // get_found_rows case + if($count_only == TRUE) + { + return $this->db->get()->row_array()['count']; + } + + $this->db->order_by($sort, $order); + + if($rows > 0) + { + $this->db->limit($rows, $limit_from); + } + + return $this->db->get(); + } + + /* + Gets information about a particular cashup + */ + public function get_info($cashup_id) + { + $this->db->select(' + cash_up.cashup_id AS cashup_id, + cash_up.open_date AS open_date, + cash_up.close_date AS close_date, + cash_up.open_amount_cash AS open_amount_cash, + cash_up.transfer_amount_cash AS transfer_amount_cash, + cash_up.closed_amount_cash AS closed_amount_cash, + cash_up.closed_amount_due AS closed_amount_due, + cash_up.closed_amount_card AS closed_amount_card, + cash_up.closed_amount_check AS closed_amount_check, + cash_up.closed_amount_total AS closed_amount_total, + cash_up.description AS description, + cash_up.note AS note, + cash_up.open_employee_id AS open_employee_id, + cash_up.close_employee_id AS close_employee_id, + cash_up.deleted AS deleted, + open_employees.first_name AS open_first_name, + open_employees.last_name AS open_last_name, + close_employees.first_name AS close_first_name, + close_employees.last_name AS close_last_name + '); + $this->db->from('cash_up AS cash_up'); + $this->db->join('people AS open_employees', 'open_employees.person_id = cash_up.open_employee_id', 'LEFT'); + $this->db->join('people AS close_employees', 'close_employees.person_id = cash_up.close_employee_id', 'LEFT'); + $this->db->where('cashup_id', $cashup_id); + + $query = $this->db->get(); + if($query->num_rows() == 1) + { + return $query->row(); + } + else + { + //Get empty base parent object + $cash_up_obj = new stdClass(); + + //Get all the fields from cashup table + foreach($this->db->list_fields('cash_up') as $field) + { + $cash_up_obj->$field = ''; + } + + return $cash_up_obj; + } + } + + /* + Inserts or updates an cashup + */ + public function save(&$cash_up_data, $cashup_id = FALSE) + { + if(!$cashup_id == -1 || !$this->exists($cashup_id)) + { + if($this->db->insert('cash_up', $cash_up_data)) + { + $cash_up_data['cashup_id'] = $this->db->insert_id(); + + return TRUE; + } + + return FALSE; + } + + $this->db->where('cashup_id', $cashup_id); + + return $this->db->update('cash_up', $cash_up_data); + } + + /* + Deletes a list of cashups + */ + public function delete_list($cashup_ids) + { + $success = FALSE; + + //Run these queries as a transaction, we want to make sure we do all or nothing + $this->db->trans_start(); + $this->db->where_in('cashup_id', $cashup_ids); + $success = $this->db->update('cash_up', array('deleted'=>1)); + $this->db->trans_complete(); + + return $success; + } +} +?> diff --git a/application/models/Customer.php b/application/models/Customer.php index 7f1f647f2..983e2c6b5 100644 --- a/application/models/Customer.php +++ b/application/models/Customer.php @@ -104,7 +104,7 @@ class Customer extends Person ( SELECT sales.sale_id AS sale_id, - AVG(sales_items.discount_percent) AS avg_discount, + AVG(sales_items.discount) AS avg_discount, SUM(sales_items.quantity_purchased) AS quantity FROM ' . $this->db->dbprefix('sales') . ' AS sales INNER JOIN ' . $this->db->dbprefix('sales_items') . ' AS sales_items @@ -223,17 +223,52 @@ class Customer extends Person */ public function delete($customer_id) { + $result = TRUE; + // if privacy enforcement is selected scramble customer data if($this->config->item('enforce_privacy')) { $this->db->where('person_id', $customer_id); - return $this->db->update('people', array('first_name' => $customer_id, 'last_name' => $customer_id, 'phone_number' => '', 'email' => '')); + $result &= $this->db->update('people', array( + 'first_name' => $customer_id, + 'last_name' => $customer_id, + 'phone_number' => '', + 'email' => '', + 'gender' => NULL, + 'address_1' => '', + 'address_2' => '', + 'city' => '', + 'state' => '', + 'zip' => '', + 'country' => '', + 'comments' => '' + )); + + $this->db->where('person_id', $customer_id); + + $result &= $this->db->update('customers', array( + 'consent' => 0, + 'company_name' => NULL, + 'account_number' => NULL, + 'tax_id' => '', + 'taxable' => 0, + 'discount' => 0.00, + 'discount_type' => 0, + 'package_id' => NULL, + 'points' => NULL, + 'sales_tax_code' => 1, + 'deleted' => 1 + )); + } + else + { + $this->db->where('person_id', $customer_id); + + $result &= $this->db->update('customers', array('deleted' => 1)); } - $this->db->where('person_id', $customer_id); - - return $this->db->update('customers', array('deleted' => 1)); + return $result; } /* @@ -316,7 +351,7 @@ class Customer extends Person } //only return $limit suggestions - if(count($suggestions > $limit)) + if(count($suggestions) > $limit) { $suggestions = array_slice($suggestions, 0, $limit); } diff --git a/application/models/Employee.php b/application/models/Employee.php index 0387d951f..c9d5f5582 100644 --- a/application/models/Employee.php +++ b/application/models/Employee.php @@ -97,7 +97,7 @@ class Employee extends Person //Run these queries as a transaction, we want to make sure we do all or nothing $this->db->trans_start(); - if(parent::save($person_data, $employee_id)) + if(ENVIRONMENT != 'testing' && parent::save($person_data, $employee_id)) { if(!$employee_id || !$this->exists($employee_id)) { @@ -119,11 +119,9 @@ class Employee extends Person //Now insert the new grants if($success) { - $count = 0; foreach($grants_data as $grant) { $success = $this->db->insert('grants', array('permission_id' => $grant['permission_id'], 'person_id' => $employee_id, 'menu_group' => $grant['menu_group'])); - $count = $count+ 1; } } } @@ -246,7 +244,7 @@ class Employee extends Person } //only return $limit suggestions - if(count($suggestions > $limit)) + if(count($suggestions) > $limit) { $suggestions = array_slice($suggestions, 0, $limit); } @@ -366,7 +364,7 @@ class Employee extends Person /* Determines whether the employee has access to at least one submodule - */ + */ public function has_module_grant($permission_id, $person_id) { $this->db->from('grants'); @@ -433,8 +431,8 @@ class Employee extends Person } /* - Gets employee permission grants - */ + Gets employee permission grants + */ public function get_employee_grants($person_id) { $this->db->from('grants'); @@ -443,7 +441,6 @@ class Employee extends Person return $this->db->get()->result_array(); } - /* Attempts to login employee and set session. Returns boolean based on outcome. */ @@ -473,15 +470,18 @@ class Employee extends Person { $success = FALSE; - //Run these queries as a transaction, we want to make sure we do all or nothing - $this->db->trans_start(); + if(ENVIRONMENT != 'testing') + { + //Run these queries as a transaction, we want to make sure we do all or nothing + $this->db->trans_start(); - $this->db->where('person_id', $employee_id); - $success = $this->db->update('employees', $employee_data); + $this->db->where('person_id', $employee_id); + $success = $this->db->update('employees', $employee_data); - $this->db->trans_complete(); + $this->db->trans_complete(); - $success &= $this->db->trans_status(); + $success &= $this->db->trans_status(); + } return $success; } diff --git a/application/models/Expense.php b/application/models/Expense.php index b962f50be..035e256c5 100644 --- a/application/models/Expense.php +++ b/application/models/Expense.php @@ -71,7 +71,7 @@ class Expense extends CI_Model $this->db->select(' expenses.expense_id, MAX(expenses.date) AS date, - MAX(expenses.supplier_name) AS supplier_name, + MAX(suppliers.company_name) AS supplier_name, MAX(expenses.supplier_tax_code) AS supplier_tax_code, MAX(expenses.amount) AS amount, MAX(expenses.tax_amount) AS tax_amount, @@ -86,6 +86,7 @@ class Expense extends CI_Model $this->db->from('expenses AS expenses'); $this->db->join('people AS employees', 'employees.person_id = expenses.employee_id', 'LEFT'); $this->db->join('expense_categories AS expense_categories', 'expense_categories.expense_category_id = expenses.expense_category_id', 'LEFT'); + $this->db->join('suppliers AS suppliers', 'suppliers.person_id = expenses.supplier_id', 'LEFT'); $this->db->group_start(); $this->db->like('employees.first_name', $search); @@ -162,7 +163,8 @@ class Expense extends CI_Model $this->db->select(' expenses.expense_id AS expense_id, expenses.date AS date, - expenses.supplier_name AS supplier_name, + suppliers.company_name AS supplier_name, + expenses.supplier_id AS supplier_id, expenses.supplier_tax_code AS supplier_tax_code, expenses.amount AS amount, expenses.tax_amount AS tax_amount, @@ -178,6 +180,7 @@ class Expense extends CI_Model $this->db->from('expenses AS expenses'); $this->db->join('people AS employees', 'employees.person_id = expenses.employee_id', 'LEFT'); $this->db->join('expense_categories AS expense_categories', 'expense_categories.expense_category_id = expenses.expense_category_id', 'LEFT'); + $this->db->join('suppliers AS suppliers', 'suppliers.person_id = expenses.supplier_id', 'LEFT'); $this->db->where('expense_id', $expense_id); $query = $this->db->get(); @@ -196,6 +199,8 @@ class Expense extends CI_Model $expenses_obj->$field = ''; } + $expenses_obj->supplier_name = ''; + return $expenses_obj; } } @@ -294,31 +299,7 @@ class Expense extends CI_Model */ public function get_payment_options() { - $payments = array(); - - if($this->config->item('payment_options_order') == 'debitcreditcash') - { - $payments[$this->lang->line('sales_debit')] = $this->lang->line('sales_debit'); - $payments[$this->lang->line('sales_credit')] = $this->lang->line('sales_credit'); - $payments[$this->lang->line('sales_cash')] = $this->lang->line('sales_cash'); - } - elseif($this->config->item('payment_options_order') == 'debitcashcredit') - { - $payments[$this->lang->line('sales_debit')] = $this->lang->line('sales_debit'); - $payments[$this->lang->line('sales_cash')] = $this->lang->line('sales_cash'); - $payments[$this->lang->line('sales_credit')] = $this->lang->line('sales_credit'); - } - else // default: if($this->config->item('payment_options_order') == 'cashdebitcredit') - { - $payments[$this->lang->line('sales_cash')] = $this->lang->line('sales_cash'); - $payments[$this->lang->line('sales_debit')] = $this->lang->line('sales_debit'); - $payments[$this->lang->line('sales_credit')] = $this->lang->line('sales_credit'); - } - - $payments[$this->lang->line('sales_due')] = $this->lang->line('sales_due'); - $payments[$this->lang->line('sales_check')] = $this->lang->line('sales_check'); - - return $payments; + return get_payment_options(); } /* diff --git a/application/models/Giftcard.php b/application/models/Giftcard.php index 898bfedd6..281ed5219 100644 --- a/application/models/Giftcard.php +++ b/application/models/Giftcard.php @@ -190,7 +190,7 @@ class Giftcard extends CI_Model } //only return $limit suggestions - if(count($suggestions > $limit)) + if(count($suggestions) > $limit) { $suggestions = array_slice($suggestions, 0, $limit); } diff --git a/application/models/Item.php b/application/models/Item.php index 48589a28d..d7b469b37 100644 --- a/application/models/Item.php +++ b/application/models/Item.php @@ -6,6 +6,7 @@ define('HAS_NO_STOCK', 1); define('ITEM', 0); define('ITEM_KIT', 1); define('ITEM_AMOUNT_ENTRY', 2); +define('ITEM_TEMP', 3); define('PRINT_ALL', 0); define('PRINT_PRICED', 1); @@ -22,6 +23,8 @@ define('PRICE_OPTION_ALL', 0); define('PRICE_OPTION_KIT', 1); define('PRICE_OPTION_KIT_STOCK', 2); +define('NAME_SEPARATOR', ' | '); + /** * Item class @@ -108,64 +111,56 @@ class Item extends CI_Model // get_found_rows case if($count_only == TRUE) { - $this->db->select('COUNT(DISTINCT items.item_id) as count'); + $this->db->select('COUNT(DISTINCT items.item_id) AS count'); } else { - $this->db->select('items.item_id as item_id'); - $this->db->select('MAX(items.name) as name'); - $this->db->select('MAX(items.category) as category'); - $this->db->select('MAX(items.supplier_id) as supplier_id'); - $this->db->select('MAX(items.item_number) as item_number'); - $this->db->select('MAX(items.description) as description'); - $this->db->select('MAX(items.cost_price) as cost_price'); - $this->db->select('MAX(items.unit_price) as unit_price'); - $this->db->select('MAX(items.reorder_level) as reorder_level'); - $this->db->select('MAX(items.receiving_quantity) as receiving_quantity'); - $this->db->select('MAX(items.pic_filename) as pic_filename'); - $this->db->select('MAX(items.allow_alt_description) as allow_alt_description'); - $this->db->select('MAX(items.is_serialized) as is_serialized'); - $this->db->select('MAX(items.deleted) as deleted'); - $this->db->select('MAX(items.custom1) as custom1'); - $this->db->select('MAX(items.custom2) as custom2'); - $this->db->select('MAX(items.custom3) as custom3'); - $this->db->select('MAX(items.custom4) as custom4'); - $this->db->select('MAX(items.custom5) as custom5'); - $this->db->select('MAX(items.custom6) as custom6'); - $this->db->select('MAX(items.custom7) as custom7'); - $this->db->select('MAX(items.custom8) as custom8'); - $this->db->select('MAX(items.custom9) as custom9'); - $this->db->select('MAX(items.custom10) as custom10'); + $this->db->select('items.item_id AS item_id'); + $this->db->select('MAX(items.name) AS name'); + $this->db->select('MAX(items.category) AS category'); + $this->db->select('MAX(items.supplier_id) AS supplier_id'); + $this->db->select('MAX(items.item_number) AS item_number'); + $this->db->select('MAX(items.description) AS description'); + $this->db->select('MAX(items.cost_price) AS cost_price'); + $this->db->select('MAX(items.unit_price) AS unit_price'); + $this->db->select('MAX(items.reorder_level) AS reorder_level'); + $this->db->select('MAX(items.receiving_quantity) AS receiving_quantity'); + $this->db->select('MAX(items.pic_filename) AS pic_filename'); + $this->db->select('MAX(items.allow_alt_description) AS allow_alt_description'); + $this->db->select('MAX(items.is_serialized) AS is_serialized'); + $this->db->select('MAX(items.pack_name) AS pack_name'); + $this->db->select('MAX(items.tax_category_id) AS tax_category_id'); + $this->db->select('MAX(items.deleted) AS deleted'); - $this->db->select('MAX(suppliers.person_id) as person_id'); - $this->db->select('MAX(suppliers.company_name) as company_name'); - $this->db->select('MAX(suppliers.agency_name) as agency_name'); - $this->db->select('MAX(suppliers.account_number) as account_number'); - $this->db->select('MAX(suppliers.deleted) as deleted'); + $this->db->select('MAX(suppliers.person_id) AS person_id'); + $this->db->select('MAX(suppliers.company_name) AS company_name'); + $this->db->select('MAX(suppliers.agency_name) AS agency_name'); + $this->db->select('MAX(suppliers.account_number) AS account_number'); + $this->db->select('MAX(suppliers.deleted) AS deleted'); - $this->db->select('MAX(inventory.trans_id) as trans_id'); - $this->db->select('MAX(inventory.trans_items) as trans_items'); - $this->db->select('MAX(inventory.trans_user) as trans_user'); - $this->db->select('MAX(inventory.trans_date) as trans_date'); - $this->db->select('MAX(inventory.trans_comment) as trans_comment'); - $this->db->select('MAX(inventory.trans_location) as trans_location'); - $this->db->select('MAX(inventory.trans_inventory) as trans_inventory'); + $this->db->select('MAX(inventory.trans_id) AS trans_id'); + $this->db->select('MAX(inventory.trans_items) AS trans_items'); + $this->db->select('MAX(inventory.trans_user) AS trans_user'); + $this->db->select('MAX(inventory.trans_date) AS trans_date'); + $this->db->select('MAX(inventory.trans_comment) AS trans_comment'); + $this->db->select('MAX(inventory.trans_location) AS trans_location'); + $this->db->select('MAX(inventory.trans_inventory) AS trans_inventory'); if($filters['stock_location_id'] > -1) { - $this->db->select('MAX(item_quantities.item_id) as qty_item_id'); - $this->db->select('MAX(item_quantities.location_id) as location_id'); - $this->db->select('MAX(item_quantities.quantity) as quantity'); + $this->db->select('MAX(item_quantities.item_id) AS qty_item_id'); + $this->db->select('MAX(item_quantities.location_id) AS location_id'); + $this->db->select('MAX(item_quantities.quantity) AS quantity'); } } - $this->db->from('items as items'); - $this->db->join('suppliers as suppliers', 'suppliers.person_id = items.supplier_id', 'left'); - $this->db->join('inventory as inventory', 'inventory.trans_items = items.item_id'); + $this->db->from('items AS items'); + $this->db->join('suppliers AS suppliers', 'suppliers.person_id = items.supplier_id', 'left'); + $this->db->join('inventory AS inventory', 'inventory.trans_items = items.item_id'); if($filters['stock_location_id'] > -1) { - $this->db->join('item_quantities as item_quantities', 'item_quantities.item_id = items.item_id'); + $this->db->join('item_quantities AS item_quantities', 'item_quantities.item_id = items.item_id'); $this->db->where('location_id', $filters['stock_location_id']); } @@ -178,33 +173,28 @@ class Item extends CI_Model $this->db->where('trans_date BETWEEN ' . $this->db->escape(rawurldecode($filters['start_date'])) . ' AND ' . $this->db->escape(rawurldecode($filters['end_date']))); } + $attributes_enabled = count($filters['definition_ids']) > 0; + if(!empty($search)) { - if($filters['search_custom'] == FALSE) - { - $this->db->group_start(); - $this->db->like('name', $search); - $this->db->or_like('item_number', $search); - $this->db->or_like('items.item_id', $search); - $this->db->or_like('company_name', $search); - $this->db->or_like('category', $search); - $this->db->group_end(); - } - else - { - $this->db->group_start(); - $this->db->like('custom1', $search); - $this->db->or_like('custom2', $search); - $this->db->or_like('custom3', $search); - $this->db->or_like('custom4', $search); - $this->db->or_like('custom5', $search); - $this->db->or_like('custom6', $search); - $this->db->or_like('custom7', $search); - $this->db->or_like('custom8', $search); - $this->db->or_like('custom9', $search); - $this->db->or_like('custom10', $search); - $this->db->group_end(); - } + $this->db->group_start(); + $this->db->like('name', $search); + $this->db->or_like('item_number', $search); + $this->db->or_like('items.item_id', $search); + $this->db->or_like('company_name', $search); + $this->db->or_like('items.category', $search); + if ($filters['search_custom'] && $attributes_enabled) + { + $this->db->or_like('attribute_value', $search); + } + $this->db->group_end(); + } + + if ($attributes_enabled) + { + $this->db->select('GROUP_CONCAT(DISTINCT CONCAT_WS(\':\', definition_id, attribute_value) ORDER BY definition_id SEPARATOR \'|\') AS attribute_values'); + $this->db->join('attribute_links', 'attribute_links.item_id = items.item_id AND attribute_links.receiving_id IS NULL AND attribute_links.sale_id IS NULL AND definition_id IN (' . implode(',', $filters['definition_ids']) . ')', 'left'); + $this->db->join('attribute_values', 'attribute_values.attribute_id = attribute_links.attribute_id', 'left'); } $this->db->where('items.deleted', $filters['is_deleted']); @@ -225,6 +215,15 @@ class Item extends CI_Model { $this->db->where('items.description', ''); } + if($filters['temporary'] != FALSE) + { + $this->db->where('items.item_type', ITEM_TEMP); + } + else + { + $non_temp = array(ITEM, ITEM_KIT, ITEM_AMOUNT_ENTRY); + $this->db->where_in('items.item_type', $non_temp); + } // get_found_rows case if($count_only == TRUE) @@ -279,10 +278,13 @@ class Item extends CI_Model public function get_info($item_id) { $this->db->select('items.*'); + $this->db->select('GROUP_CONCAT(attribute_value SEPARATOR \'|\') AS attribute_values'); $this->db->select('suppliers.company_name'); $this->db->from('items'); $this->db->join('suppliers', 'suppliers.person_id = items.supplier_id', 'left'); - $this->db->where('item_id', $item_id); + $this->db->join('attribute_links', 'attribute_links.item_id = items.item_id', 'left'); + $this->db->join('attribute_values', 'attribute_links.attribute_id = attribute_values.attribute_id', 'left'); + $this->db->where('items.item_id', $item_id); $query = $this->db->get(); @@ -308,7 +310,7 @@ class Item extends CI_Model /* Gets information about a particular item by item id or number */ - public function get_info_by_id_or_number($item_id) + public function get_info_by_id_or_number($item_id, $include_deleted = TRUE) { $this->db->from('items'); @@ -325,9 +327,12 @@ class Item extends CI_Model $this->db->group_end(); - $this->db->where('items.deleted', 0); + if(!$include_deleted) + { + $this->db->where("items.deleted = '0'"); + } - // limit to only 1 so there is a result in case two are returned + // limit to only 1 so there is a result in case two are returned // due to barcode and item_id clash $this->db->limit(1); @@ -369,11 +374,19 @@ class Item extends CI_Model */ public function get_multiple_info($item_ids, $location_id) { + $this->db->select('items.*'); + $this->db->select('company_name'); + $this->db->select('GROUP_CONCAT(DISTINCT CONCAT_WS(\':\', definition_id, attribute_value) ORDER BY definition_id SEPARATOR \'|\') AS attribute_values'); + $this->db->select('items.category'); + $this->db->select('quantity'); $this->db->from('items'); $this->db->join('suppliers', 'suppliers.person_id = items.supplier_id', 'left'); $this->db->join('item_quantities', 'item_quantities.item_id = items.item_id', 'left'); + $this->db->join('attribute_links', 'attribute_links.item_id = items.item_id AND sale_id IS NULL AND receiving_id IS NULL', 'left'); + $this->db->join('attribute_values', 'attribute_links.attribute_id = attribute_values.attribute_id', 'left'); $this->db->where('location_id', $location_id); $this->db->where_in('items.item_id', $item_ids); + $this->db->group_by('items.item_id'); return $this->db->get(); } @@ -388,10 +401,13 @@ class Item extends CI_Model if($this->db->insert('items', $item_data)) { $item_data['item_id'] = $this->db->insert_id(); - + if($item_data['low_sell_item_id'] == -1) + { + $this->db->where('item_id', $item_data['item_id']); + $this->db->update('items', array('low_sell_item_id'=>$item_data['item_id'])); + } return TRUE; } - return FALSE; } @@ -469,7 +485,7 @@ class Item extends CI_Model function get_search_suggestion_format($seed = NULL) { $seed .= ',' . $this->config->item('suggestions_first_column'); - + if($this->config->item('suggestions_second_column') !== '') { $seed .= ',' . $this->config->item('suggestions_second_column'); @@ -479,37 +495,77 @@ class Item extends CI_Model { $seed .= ',' . $this->config->item('suggestions_third_column'); } - + return $seed; } function get_search_suggestion_label($result_row) { + $label = ''; $label1 = $this->config->item('suggestions_first_column'); $label2 = $this->config->item('suggestions_second_column'); $label3 = $this->config->item('suggestions_third_column'); - $label = $result_row->$label1; - - if($label2 !== '') + // If multi_pack enabled then if "name" is part of the search suggestions then append pack + if($this->config->item('multi_pack_enabled') == '1') { - $label .= ' | '. $result_row->$label2; + $this->append_label($label, $label1, $result_row); + $this->append_label($label, $label2, $result_row); + $this->append_label($label, $label3, $result_row); } - - if($label3 !== '') + else { - $label .= ' | '. $result_row->$label3; - } - + $label = $result_row->$label1; + + if($label2 !== '') + { + $label .= NAME_SEPARATOR . $result_row->$label2; + } + + if($label3 !== '') + { + $label .= NAME_SEPARATOR . $result_row->$label3; + } + } + return $label; - } + } + + private function append_label(&$label, $item_field_name, $item_info) + { + if($item_field_name !== '') + { + if($label == '') + { + if($item_field_name == 'name') + { + $label .= implode(NAME_SEPARATOR, array($item_info->name, $item_info->pack_name)); + } + else + { + $label .= $item_info->$item_field_name; + } + } + else + { + if($item_field_name == 'name') + { + $label .= implode(NAME_SEPARATOR, array('', $item_info->name, $item_info->pack_name)); + } + else + { + $label .= NAME_SEPARATOR . $item_info->$item_field_name; + } + } + } + } public function get_search_suggestions($search, $filters = array('is_deleted' => FALSE, 'search_custom' => FALSE), $unique = FALSE, $limit = 25) { $suggestions = array(); $non_kit = array(ITEM, ITEM_AMOUNT_ENTRY); - $this->db->select($this->get_search_suggestion_format('item_id, name')); + $this->db->select($this->get_search_suggestion_format('item_id, name, pack_name')); $this->db->from('items'); $this->db->where('deleted', $filters['is_deleted']); $this->db->where_in('item_type', $non_kit); // standard, exclude kit items since kits will be picked up later @@ -520,7 +576,7 @@ class Item extends CI_Model $suggestions[] = array('value' => $row->item_id, 'label' => $this->get_search_suggestion_label($row)); } - $this->db->select($this->get_search_suggestion_format('item_id, item_number')); + $this->db->select($this->get_search_suggestion_format('item_id, item_number, pack_name')); $this->db->from('items'); $this->db->where('deleted', $filters['is_deleted']); $this->db->where_in('item_type', $non_kit); // standard, exclude kit items since kits will be picked up later @@ -561,7 +617,7 @@ class Item extends CI_Model } //Search by description - $this->db->select($this->get_search_suggestion_format('item_id, name, description')); + $this->db->select($this->get_search_suggestion_format('item_id, name, pack_name, description')); $this->db->from('items'); $this->db->where('deleted', $filters['is_deleted']); $this->db->where_in('item_type', $non_kit); // standard, exclude kit items since kits will be picked up later @@ -579,19 +635,11 @@ class Item extends CI_Model //Search by custom fields if($filters['search_custom'] != FALSE) { - $this->db->from('items'); - $this->db->group_start(); - $this->db->like('custom1', $search); - $this->db->or_like('custom2', $search); - $this->db->or_like('custom3', $search); - $this->db->or_like('custom4', $search); - $this->db->or_like('custom5', $search); - $this->db->or_like('custom6', $search); - $this->db->or_like('custom7', $search); - $this->db->or_like('custom8', $search); - $this->db->or_like('custom9', $search); - $this->db->or_like('custom10', $search); - $this->db->group_end(); + $this->db->from('attribute_links'); + $this->db->join('attribute_links.attribute_id = attribute_values.attribute_id'); + $this->db->join('attribute_definitions', 'attribute_definitions.definition_id = attribute_links.definition_id'); + $this->db->like('attribute_value', $search); + $this->db->where('definition_type', TEXT); $this->db->where('deleted', $filters['is_deleted']); $this->db->where_in('item_type', $non_kit); // standard, exclude kit items since kits will be picked up later foreach($this->db->get()->result() as $row) @@ -602,12 +650,12 @@ class Item extends CI_Model } //only return $limit suggestions - if(count($suggestions > $limit)) + if(count($suggestions) > $limit) { $suggestions = array_slice($suggestions, 0, $limit); } - return $suggestions; + return array_unique($suggestions, SORT_REGULAR); } @@ -616,11 +664,11 @@ class Item extends CI_Model $suggestions = array(); $non_kit = array(ITEM, ITEM_AMOUNT_ENTRY); - $this->db->select($this->get_search_suggestion_format('item_id, name')); + $this->db->select($this->get_search_suggestion_format('item_id, name, pack_name')); $this->db->from('items'); $this->db->where('deleted', $filters['is_deleted']); $this->db->where_in('item_type', $non_kit); // standard, exclude kit items since kits will be picked up later - $this->db->where("stock_type = '0'"); // stocked items only + $this->db->where('stock_type', '0'); // stocked items only $this->db->like('name', $search); $this->db->order_by('name', 'asc'); foreach($this->db->get()->result() as $row) @@ -628,11 +676,11 @@ class Item extends CI_Model $suggestions[] = array('value' => $row->item_id, 'label' => $this->get_search_suggestion_label($row)); } - $this->db->select($this->get_search_suggestion_format('item_id, item_number')); + $this->db->select($this->get_search_suggestion_format('item_id, item_number, pack_name')); $this->db->from('items'); $this->db->where('deleted', $filters['is_deleted']); $this->db->where_in('item_type', $non_kit); // standard, exclude kit items since kits will be picked up later - $this->db->where("stock_type = '0'"); // stocked items only + $this->db->where('stock_type', '0'); // stocked items only $this->db->like('item_number', $search); $this->db->order_by('item_number', 'asc'); foreach($this->db->get()->result() as $row) @@ -647,7 +695,7 @@ class Item extends CI_Model $this->db->from('items'); $this->db->where('deleted', $filters['is_deleted']); $this->db->where_in('item_type', $non_kit); // standard, exclude kit items since kits will be picked up later - $this->db->where("stock_type = '0'"); // stocked items only + $this->db->where('stock_type', '0'); // stocked items only $this->db->distinct(); $this->db->like('category', $search); $this->db->order_by('category', 'asc'); @@ -670,11 +718,11 @@ class Item extends CI_Model } //Search by description - $this->db->select($this->get_search_suggestion_format('item_id, name, description')); + $this->db->select($this->get_search_suggestion_format('item_id, name, pack_name, description')); $this->db->from('items'); $this->db->where('deleted', $filters['is_deleted']); $this->db->where_in('item_type', $non_kit); // standard, exclude kit items since kits will be picked up later - $this->db->where("stock_type = '0'"); // stocked items only + $this->db->where('stock_type', '0'); // stocked items only $this->db->like('description', $search); $this->db->order_by('description', 'asc'); foreach($this->db->get()->result() as $row) @@ -689,21 +737,12 @@ class Item extends CI_Model //Search by custom fields if($filters['search_custom'] != FALSE) { - $this->db->from('items'); - $this->db->group_start(); - $this->db->like('custom1', $search); - $this->db->or_like('custom2', $search); - $this->db->or_like('custom3', $search); - $this->db->or_like('custom4', $search); - $this->db->or_like('custom5', $search); - $this->db->or_like('custom6', $search); - $this->db->or_like('custom7', $search); - $this->db->or_like('custom8', $search); - $this->db->or_like('custom9', $search); - $this->db->or_like('custom10', $search); - $this->db->group_end(); - $this->db->where_in('item_type', $non_kit); // standard, exclude kit items since kits will be picked up later - $this->db->where("stock_type = '0'"); // stocked items only + $this->db->from('attribute_links'); + $this->db->join('attribute_links.attribute_id = attribute_values.attribute_id'); + $this->db->join('attribute_definitions', 'attribute_definitions.definition_id = attribute_links.definition_id'); + $this->db->like('attribute_value', $search); + $this->db->where('definition_type', TEXT); + $this->db->where('stock_type', '0'); // stocked items only $this->db->where('deleted', $filters['is_deleted']); foreach($this->db->get()->result() as $row) { @@ -713,12 +752,12 @@ class Item extends CI_Model } //only return $limit suggestions - if(count($suggestions > $limit)) + if(count($suggestions) > $limit) { $suggestions = array_slice($suggestions, 0, $limit); } - return $suggestions; + return array_unique($suggestions, SORT_REGULAR); } public function get_kit_search_suggestions($search, $filters = array('is_deleted' => FALSE, 'search_custom' => FALSE), $unique = FALSE, $limit = 25) @@ -818,11 +857,29 @@ class Item extends CI_Model } //only return $limit suggestions - if(count($suggestions > $limit)) + if(count($suggestions) > $limit) { $suggestions = array_slice($suggestions, 0, $limit); } + return array_unique($suggestions, SORT_REGULAR); + } + + public function get_low_sell_suggestions($search) + { + $suggestions = array(); + + $this->db->select($this->get_search_suggestion_format('item_id, pack_name')); + $this->db->from('items'); + $this->db->where('deleted', '0'); + $this->db->where('stock_type', '0'); // stocked items only + $this->db->like('name', $search); + $this->db->order_by('name', 'asc'); + foreach($this->db->get()->result() as $row) + { + $suggestions[] = array('value' => $row->item_id, 'label' => $this->get_search_suggestion_label($row)); + } + return $suggestions; } @@ -860,24 +917,6 @@ class Item extends CI_Model return $suggestions; } - public function get_custom_suggestions($search, $field_no) - { - $suggestions = array(); - $this->db->distinct(); - $this->db->select('custom'.$field_no); - $this->db->from('items'); - $this->db->like('custom'.$field_no, $search); - $this->db->where('deleted', 0); - $this->db->order_by('custom'.$field_no, 'asc'); - foreach($this->db->get()->result() as $row) - { - $row_array = (array) $row; - $suggestions[] = array('label' => $row_array['custom'.$field_no]); - } - - return $suggestions; - } - public function get_categories() { $this->db->select('category'); @@ -923,5 +962,50 @@ class Item extends CI_Model return $this->save($data, $item_id); } + + public function update_item_number($item_id, $item_number) + { + $this->db->where('item_id', $item_id); + $this->db->update('items', array('item_number'=>$item_number)); + } + + public function update_item_name($item_id, $item_name) + { + $this->db->where('item_id', $item_id); + $this->db->update('items', array('name'=>$item_name)); + } + + public function update_item_description($item_id, $item_description) + { + $this->db->where('item_id', $item_id); + $this->db->update('items', array('description'=>$item_description)); + } + + /** + * Determine the item name to use taking into consideration that + * for a multipack environment then the item name should have the + * pack appended to it + */ + function get_item_name($as_name = NULL) + { + if($as_name == NULL) + { + $as_name = ''; + } + else + { + $as_name = ' AS ' . $as_name; + } + + if($this->config->item('multi_pack_enabled') == '1') + { + $item_name = "concat(items.name,'" . NAME_SEPARATOR . '\', items.pack_name)' . $as_name; + } + else + { + $item_name = 'items.name' . $as_name; + } + return $item_name; + } } ?> diff --git a/application/models/Item_kit.php b/application/models/Item_kit.php index 6de74b7c4..c0e76212f 100644 --- a/application/models/Item_kit.php +++ b/application/models/Item_kit.php @@ -58,7 +58,8 @@ class Item_kit extends CI_Model item_kits.description, items.description as item_description, item_kits.item_id as kit_item_id, - kit_discount_percent, + kit_discount, + kit_discount_type, price_option, print_option, category, @@ -72,16 +73,6 @@ class Item_kit extends CI_Model allow_alt_description, is_serialized, items.deleted, - custom1, - custom2, - custom3, - custom4, - custom5, - custom6, - custom7, - custom8, - custom9, - custom10, item_type, stock_type'); @@ -191,7 +182,7 @@ class Item_kit extends CI_Model } //only return $limit suggestions - if(count($suggestions > $limit)) + if(count($suggestions) > $limit) { $suggestions = array_slice($suggestions, 0, $limit); } diff --git a/application/models/Person.php b/application/models/Person.php index f5cee9260..cf5c773ae 100644 --- a/application/models/Person.php +++ b/application/models/Person.php @@ -158,7 +158,7 @@ class Person extends CI_Model } //only return $limit suggestions - if(count($suggestions > $limit)) + if(count($suggestions) > $limit) { $suggestions = array_slice($suggestions, 0, $limit); } diff --git a/application/models/Receiving.php b/application/models/Receiving.php index c875796e1..29958d52f 100644 --- a/application/models/Receiving.php +++ b/application/models/Receiving.php @@ -93,7 +93,8 @@ class Receiving extends CI_Model 'serialnumber' => $item['serialnumber'], 'quantity_purchased' => $item['quantity'], 'receiving_quantity' => $item['receiving_quantity'], - 'discount_percent' => $item['discount'], + 'discount' => $item['discount'], + 'discount_type' => $item['discount_type'], 'item_cost_price' => $cur_item_info->cost_price, 'item_unit_price' => $item['price'], 'item_location' => $item['item_location'] @@ -126,6 +127,8 @@ class Receiving extends CI_Model $this->Inventory->insert($inv_data); + $this->Attribute->copy_attribute_links($item['item_id'], 'receiving_id', $receiving_id); + $supplier = $this->Supplier->get_info($supplier_id); } @@ -221,7 +224,8 @@ class Receiving extends CI_Model $this->lang->line('sales_cash') => $this->lang->line('sales_cash'), $this->lang->line('sales_check') => $this->lang->line('sales_check'), $this->lang->line('sales_debit') => $this->lang->line('sales_debit'), - $this->lang->line('sales_credit') => $this->lang->line('sales_credit') + $this->lang->line('sales_credit') => $this->lang->line('sales_credit'), + $this->lang->line('sales_due') => $this->lang->line('sales_due') ); } @@ -264,13 +268,14 @@ class Receiving extends CI_Model MAX(receivings_items.receiving_quantity) AS receiving_quantity, MAX(item_cost_price) AS item_cost_price, MAX(item_unit_price) AS item_unit_price, - MAX(discount_percent) AS discount_percent, + MAX(discount) AS discount, + discount_type as discount_type, receivings_items.line, MAX(serialnumber) AS serialnumber, MAX(receivings_items.description) AS description, - MAX(item_unit_price * quantity_purchased * receivings_items.receiving_quantity - item_unit_price * quantity_purchased * receivings_items.receiving_quantity * discount_percent / 100) AS subtotal, - MAX(item_unit_price * quantity_purchased * receivings_items.receiving_quantity - item_unit_price * quantity_purchased * receivings_items.receiving_quantity * discount_percent / 100) AS total, - MAX((item_unit_price * quantity_purchased * receivings_items.receiving_quantity - item_unit_price * quantity_purchased * receivings_items.receiving_quantity * discount_percent / 100) - (item_cost_price * quantity_purchased)) AS profit, + MAX(CASE WHEN receivings_items.discount_type = ' . PERCENT . ' THEN item_unit_price * quantity_purchased * receivings_items.receiving_quantity - item_unit_price * quantity_purchased * receivings_items.receiving_quantity * discount / 100 ELSE item_unit_price * quantity_purchased * receivings_items.receiving_quantity - discount END) AS subtotal, + MAX(CASE WHEN receivings_items.discount_type = ' . PERCENT . ' THEN item_unit_price * quantity_purchased * receivings_items.receiving_quantity - item_unit_price * quantity_purchased * receivings_items.receiving_quantity * discount / 100 ELSE item_unit_price * quantity_purchased * receivings_items.receiving_quantity - discount END) AS total, + MAX((CASE WHEN receivings_items.discount_type = ' . PERCENT . ' THEN item_unit_price * quantity_purchased * receivings_items.receiving_quantity - item_unit_price * quantity_purchased * receivings_items.receiving_quantity * discount / 100 ELSE item_unit_price * quantity_purchased * receivings_items.receiving_quantity - discount END) - (item_cost_price * quantity_purchased)) AS profit, MAX(item_cost_price * quantity_purchased * receivings_items.receiving_quantity ) AS cost FROM ' . $this->db->dbprefix('receivings_items') . ' AS receivings_items INNER JOIN ' . $this->db->dbprefix('receivings') . ' AS receivings diff --git a/application/models/Sale.php b/application/models/Sale.php index ad02c2b83..44c20abfd 100644 --- a/application/models/Sale.php +++ b/application/models/Sale.php @@ -10,6 +10,8 @@ define('SALE_TYPE_WORK_ORDER', 2); define('SALE_TYPE_QUOTE', 3); define('SALE_TYPE_RETURN', 4); +define('PERCENT', 0); +define('FIXED', 1); /** * Sale class */ @@ -38,7 +40,7 @@ class Sale extends CI_Model $decimals = totals_decimals(); - $sale_price = 'sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100)'; + $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'; $tax = 'ROUND(IFNULL(SUM(sales_items_taxes.tax), 0), ' . $decimals . ')'; if($this->config->item('tax_included')) @@ -152,7 +154,7 @@ class Sale extends CI_Model $decimals = totals_decimals(); - $sale_price = 'sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100)'; + $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'; $sale_cost = 'SUM(sales_items.item_cost_price * sales_items.quantity_purchased)'; $tax = 'IFNULL(SUM(sales_items_taxes.tax), 0)'; @@ -174,7 +176,7 @@ class Sale extends CI_Model SELECT sales_items_taxes.sale_id AS sale_id, sales_items_taxes.item_id AS item_id, sales_items_taxes.line AS line, - SUM(sales_items_taxes.item_tax_amount) as tax + SUM(sales_items_taxes.item_tax_amount) AS tax FROM ' . $this->db->dbprefix('sales_items_taxes') . ' AS sales_items_taxes INNER JOIN ' . $this->db->dbprefix('sales') . ' AS sales ON sales.sale_id = sales_items_taxes.sale_id @@ -188,7 +190,7 @@ class Sale extends CI_Model // get_found_rows case if($count_only == TRUE) { - $this->db->select('COUNT(DISTINCT sales.sale_id) as count'); + $this->db->select('COUNT(DISTINCT sales.sale_id) AS count'); } else { @@ -384,7 +386,7 @@ class Sale extends CI_Model $gift_card_amount = 0; foreach($payments as $key=>$payment) { - if( strstr($payment['payment_type'], $this->lang->line('sales_giftcard')) != FALSE ) + if(strstr($payment['payment_type'], $this->lang->line('sales_giftcard')) != FALSE) { $gift_card_count += $payment['count']; $gift_card_amount += $payment['payment_amount']; @@ -563,7 +565,8 @@ class Sale extends CI_Model * Save the sale information after the sales is complete but before the final document is printed * The sales_taxes variable needs to be initialized to an empty array before calling */ - public function save($sale_id, &$sale_status, &$items, $customer_id, $employee_id, $comment, $invoice_number, $work_order_number, $quote_number, $sale_type, $payments, $dinner_table, &$sales_taxes) + public function save($sale_id, &$sale_status, &$items, $customer_id, $employee_id, $comment, $invoice_number, + $work_order_number, $quote_number, $sale_type, $payments, $dinner_table, &$sales_taxes) { if($sale_id != -1) { @@ -639,12 +642,15 @@ class Sale extends CI_Model $customer = $this->Customer->get_info($customer_id); - $sales_taxes = array(); - foreach($items as $line=>$item) { $cur_item_info = $this->Item->get_info($item['item_id']); + if($item['price'] == 0.00) + { + $item['discount'] = 0.00; + } + $sales_items_data = array( 'sale_id' => $sale_id, 'item_id' => $item['item_id'], @@ -652,7 +658,8 @@ class Sale extends CI_Model 'description' => character_limiter($item['description'], 255), 'serialnumber' => character_limiter($item['serialnumber'], 30), 'quantity_purchased'=> $item['quantity'], - 'discount_percent' => $item['discount'], + 'discount' => $item['discount'], + 'discount_type' => $item['discount_type'], 'item_cost_price' => $item['cost_price'], 'item_unit_price' => $item['price'], 'item_location' => $item['item_location'], @@ -689,80 +696,13 @@ class Sale extends CI_Model $this->Inventory->insert($inv_data); } - // Calculate taxes and save the tax information for the sale. Return the result for printing - - if($customer_id == -1 || $customer->taxable) - { - if($this->config->item('tax_included')) - { - $tax_type = Tax_lib::TAX_TYPE_VAT; - } - else - { - $tax_type = Tax_lib::TAX_TYPE_SALES; - } - $rounding_code = Rounding_mode::HALF_UP; // half adjust - $tax_group_sequence = 0; - $item_total = $this->sale_lib->get_item_total($item['quantity'], $item['price'], $item['discount'], TRUE); - $tax_basis = $item_total; - $item_tax_amount = 0; - - foreach($this->Item_taxes->get_info($item['item_id']) as $row) - { - - $sales_items_taxes = array( - 'sale_id' => $sale_id, - 'item_id' => $item['item_id'], - 'line' => $item['line'], - 'name' => character_limiter($row['name'], 255), - 'percent' => $row['percent'], - 'tax_type' => $tax_type, - 'rounding_code' => $rounding_code, - 'cascade_tax' => 0, - 'cascade_sequence' => 0, - 'item_tax_amount' => 0 - ); - - // This computes tax for each line item and adds it to the tax type total - $tax_group = (float)$row['percent'] . '% ' . $row['name']; - $tax_basis = $this->sale_lib->get_item_total($item['quantity'], $item['price'], $item['discount'], TRUE); - - if($this->config->item('tax_included')) - { - $tax_type = Tax_lib::TAX_TYPE_VAT; - $item_tax_amount = $this->sale_lib->get_item_tax($item['quantity'], $item['price'], $item['discount'],$row['percent']); - } - elseif($this->config->item('customer_sales_tax_support') == '0') - { - $tax_type = Tax_lib::TAX_TYPE_SALES; - $item_tax_amount = $this->tax_lib->get_sales_tax_for_amount($tax_basis, $row['percent'], '0', $tax_decimals); - } - else - { - $tax_type = Tax_lib::TAX_TYPE_SALES; - } - - $sales_items_taxes['item_tax_amount'] = $item_tax_amount; - if($item_tax_amount != 0) - { - $this->db->insert('sales_items_taxes', $sales_items_taxes); - $this->tax_lib->update_sales_taxes($sales_taxes, $tax_type, $tax_group, $row['percent'], $tax_basis, $item_tax_amount, $tax_group_sequence, $rounding_code, $sale_id, $row['name'], ''); - $tax_group_sequence += 1; - } - - } - - if($this->config->item('customer_sales_tax_support') == '1') - { - $this->save_sales_item_tax($customer, $sale_id, $item, $item_total, $sales_taxes, $sequence, $cur_item_info->tax_category_id); - } - } + $this->Attribute->copy_attribute_links($item['item_id'], 'sale_id', $sale_id); } if($customer_id == -1 || $customer->taxable) { - $this->tax_lib->round_sales_taxes($sales_taxes); - $this->save_sales_tax($sales_taxes); + $this->save_sales_tax($sale_id, $sales_taxes[0]); + $this->save_sales_items_taxes($sale_id, $sales_taxes[1]); } $dinner_table_data = array( @@ -782,6 +722,19 @@ class Sale extends CI_Model return $sale_id; } + /** + * Saves sale tax + */ + public function save_sales_tax($sale_id, $sales_taxes) + { + foreach($sales_taxes as $line=>$sales_tax) + { + $sales_tax['tax_group'] = (float)$sales_tax['tax_rate'] . '% ' . $sales_tax['tax_group']; + $sales_tax['sale_id'] = $sale_id; + $this->db->insert('sales_taxes', $sales_tax); + } + } + /** * Apply customer sales tax if the customer sales tax is enabledl * The original tax is still supported if the user configures it, @@ -789,25 +742,24 @@ class Sale extends CI_Model * of VAT tax which becomes a price component. VAT taxes must still be reported * as a separate tax entry on the invoice. */ - public function save_sales_item_tax(&$customer, &$sale_id, &$item, $tax_basis, &$sales_taxes, &$sequence, $tax_category_id) + public function save_sales_items_taxes($sale_id, $sales_item_taxes) { - // if customer sales tax is enabled then update sales_items_taxes with the - if($this->config->item('customer_sales_tax_support') == '1') + foreach($sales_item_taxes as $line => $tax_item) { - $register_mode = $this->config->item('default_register_mode'); - $tax_details = $this->tax_lib->apply_sales_tax($item, $customer->city, $customer->state, $customer->sales_tax_code, $register_mode, $sale_id, $sales_taxes); - $sales_items_taxes = array( - 'sale_id' => $sale_id, - 'item_id' => $item['item_id'], - 'line' => $item['line'], - 'name' => $tax_details['tax_name'], - 'percent' => $tax_details['tax_rate'], - 'tax_type' => Tax_lib::TAX_TYPE_SALES, - 'rounding_code' => $tax_details['rounding_code'], - 'cascade_tax' => 0, - 'cascade_sequence' => 0, - 'item_tax_amount' => $tax_details['item_tax_amount'] + 'sale_id' => $sale_id, + 'item_id' => $tax_item['item_id'], + 'line' => $tax_item['line'], + 'name' => $tax_item['name'], + 'percent' => $tax_item['percent'], + 'tax_type' => $tax_item['tax_type'], + 'rounding_code' => $tax_item['rounding_code'], + 'cascade_sequence' => $tax_item['cascade_sequence'], + 'item_tax_amount' => $tax_item['item_tax_amount'], + 'sales_tax_code_id' => $tax_item['sales_tax_code_id'], + 'tax_category_id' => $tax_item['tax_category_id'], + 'jurisdiction_id' => $tax_item['jurisdiction_id'], + 'tax_category_id' => $tax_item['tax_category_id'] ); $this->db->insert('sales_items_taxes', $sales_items_taxes); @@ -815,14 +767,17 @@ class Sale extends CI_Model } /** - * Saves sale tax + * Return the taxes that were charged */ - public function save_sales_tax(&$sales_taxes) + public function get_sales_taxes($sale_id) { - foreach($sales_taxes as $line=>$sales_tax) - { - $this->db->insert('sales_taxes', $sales_tax); - } + $this->db->from('sales_taxes'); + $this->db->where('sale_id', $sale_id); + $this->db->order_by('print_sequence', 'asc'); + + $query = $this->db->get(); + + return $query->result_array(); } /** @@ -927,15 +882,16 @@ class Sale extends CI_Model quantity_purchased, item_cost_price, item_unit_price, - discount_percent, + discount, + discount_type, item_location, print_option, - items.name as name, + ' . $this->Item->get_item_name('name') . ', category, item_type, stock_type'); - $this->db->from('sales_items as sales_items'); - $this->db->join('items as items', 'sales_items.item_id = items.item_id'); + $this->db->from('sales_items AS sales_items'); + $this->db->join('items AS items', 'sales_items.item_id = items.item_id'); $this->db->where('sales_items.sale_id', $sale_id); // Entry sequence (this will render kits in the expected sequence) @@ -949,6 +905,7 @@ class Sale extends CI_Model $this->db->order_by('stock_type', 'desc'); $this->db->order_by('sales_items.description', 'asc'); $this->db->order_by('items.name', 'asc'); + $this->db->order_by('items.qty_per_pack', 'asc'); } // Group by Item Category elseif($this->config->item('line_sequence') == '2') @@ -956,6 +913,7 @@ class Sale extends CI_Model $this->db->order_by('category', 'asc'); $this->db->order_by('sales_items.description', 'asc'); $this->db->order_by('items.name', 'asc'); + $this->db->order_by('items.qty_per_pack', 'asc'); } // Group by entry sequence in descending sequence (the Standard) else @@ -982,36 +940,14 @@ class Sale extends CI_Model */ public function get_payment_options($giftcard = TRUE, $reward_points = FALSE) { - $payments = array(); + $payments = get_payment_options(); - if($this->config->item('payment_options_order') == 'debitcreditcash') - { - $payments[$this->lang->line('sales_debit')] = $this->lang->line('sales_debit'); - $payments[$this->lang->line('sales_credit')] = $this->lang->line('sales_credit'); - $payments[$this->lang->line('sales_cash')] = $this->lang->line('sales_cash'); - } - elseif($this->config->item('payment_options_order') == 'debitcashcredit') - { - $payments[$this->lang->line('sales_debit')] = $this->lang->line('sales_debit'); - $payments[$this->lang->line('sales_cash')] = $this->lang->line('sales_cash'); - $payments[$this->lang->line('sales_credit')] = $this->lang->line('sales_credit'); - } - else // default: if($this->config->item('payment_options_order') == 'cashdebitcredit') - { - $payments[$this->lang->line('sales_cash')] = $this->lang->line('sales_cash'); - $payments[$this->lang->line('sales_debit')] = $this->lang->line('sales_debit'); - $payments[$this->lang->line('sales_credit')] = $this->lang->line('sales_credit'); - } - - $payments[$this->lang->line('sales_due')] = $this->lang->line('sales_due'); - $payments[$this->lang->line('sales_check')] = $this->lang->line('sales_check'); - - if($giftcard) + if($giftcard == TRUE) { $payments[$this->lang->line('sales_giftcard')] = $this->lang->line('sales_giftcard'); } - if($reward_points) + if($reward_points == TRUE) { $payments[$this->lang->line('sales_rewards')] = $this->lang->line('sales_rewards'); } @@ -1133,7 +1069,7 @@ class Sale extends CI_Model $decimals = totals_decimals(); - $sale_price = 'sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100)'; + $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'; $sale_cost = 'SUM(sales_items.item_cost_price * sales_items.quantity_purchased)'; $tax = 'IFNULL(SUM(sales_items_taxes.tax), 0)'; @@ -1155,7 +1091,7 @@ class Sale extends CI_Model SELECT sales_items_taxes.sale_id AS sale_id, sales_items_taxes.item_id AS item_id, sales_items_taxes.line AS line, - SUM(sales_items_taxes.item_tax_amount) as tax + SUM(sales_items_taxes.item_tax_amount) AS tax FROM ' . $this->db->dbprefix('sales_items_taxes') . ' AS sales_items_taxes INNER JOIN ' . $this->db->dbprefix('sales') . ' AS sales ON sales.sale_id = sales_items_taxes.sale_id @@ -1203,14 +1139,15 @@ class Sale extends CI_Model MAX(sales.employee_id) AS employee_id, MAX(CONCAT(employee.first_name, " ", employee.last_name)) AS employee_name, items.item_id AS item_id, - MAX(items.name) AS name, + MAX(' . $this->Item->get_item_name() . ') AS name, MAX(items.item_number) AS item_number, MAX(items.category) AS category, MAX(items.supplier_id) AS supplier_id, MAX(sales_items.quantity_purchased) AS quantity_purchased, MAX(sales_items.item_cost_price) AS item_cost_price, MAX(sales_items.item_unit_price) AS item_unit_price, - MAX(sales_items.discount_percent) AS discount_percent, + MAX(sales_items.discount) AS discount, + sales_items.discount_type AS discount_type, sales_items.line AS line, MAX(sales_items.serialnumber) AS serialnumber, MAX(sales_items.item_location) AS item_location, @@ -1314,7 +1251,6 @@ class Sale extends CI_Model $this->db->update('sales', array('sale_status'=>$sale_status)); } - /** * Gets the quote_number for the selected sale */ @@ -1329,10 +1265,8 @@ class Sale extends CI_Model { return $row->quote_number; } - else - { - return NULL; - } + + return NULL; } /** @@ -1349,10 +1283,8 @@ class Sale extends CI_Model { return $row->work_order_number; } - else - { - return NULL; - } + + return NULL; } /** @@ -1369,10 +1301,8 @@ class Sale extends CI_Model { return $row->comment; } - else - { - return NULL; - } + + return NULL; } /** @@ -1487,6 +1417,7 @@ class Sale extends CI_Model { return SUSPENDED; } + return COMPLETED; } } diff --git a/application/models/Stock_location.php b/application/models/Stock_location.php index b35b4647a..239e3937c 100644 --- a/application/models/Stock_location.php +++ b/application/models/Stock_location.php @@ -149,7 +149,7 @@ class Stock_location extends CI_Model private function _insert_new_permission($module, $location_id, $location_name) { // insert new permission for stock location - $permission_id = $module . '_' . $location_name; + $permission_id = $module . '_' . str_replace(' ', '_', $location_name); $permission_data = array('permission_id' => $permission_id, 'module_id' => $module, 'location_id' => $location_id); $this->db->insert('permissions', $permission_data); diff --git a/application/models/Supplier.php b/application/models/Supplier.php index ea08fa887..05ef4d458 100644 --- a/application/models/Supplier.php +++ b/application/models/Supplier.php @@ -5,7 +5,10 @@ */ class Supplier extends Person -{ +{ + const GOODS_SUPPLIER = 0; + const COST_SUPPLIER = 1; + /* Determines if a given person_id is a customer */ @@ -32,10 +35,11 @@ class Supplier extends Person /* Returns all the suppliers */ - public function get_all($limit_from = 0, $rows = 0) + public function get_all($category = self::GOODS_SUPPLIER, $limit_from = 0, $rows = 0) { $this->db->from('suppliers'); - $this->db->join('people', 'suppliers.person_id = people.person_id'); + $this->db->join('people', 'suppliers.person_id = people.person_id'); + $this->db->where('category', $category); $this->db->where('deleted', 0); $this->db->order_by('company_name', 'asc'); if($rows > 0) @@ -217,7 +221,7 @@ class Supplier extends Person } //only return $limit suggestions - if(count($suggestions > $limit)) + if(count($suggestions) > $limit) { $suggestions = array_slice($suggestions, 0, $limit); } @@ -273,5 +277,31 @@ class Supplier extends Person return $this->db->get(); } + + /* + Return supplier categories + */ + public function get_categories() + { + return array( + self::GOODS_SUPPLIER => $this->lang->line('suppliers_goods'), + self::COST_SUPPLIER => $this->lang->line('suppliers_cost') + ); + } + + /* + Return a category name given its id + */ + public function get_category_name($id) + { + if($id == self::GOODS_SUPPLIER) + { + return $this->lang->line('suppliers_goods'); + } + elseif($id == self::COST_SUPPLIER) + { + return $this->lang->line('suppliers_cost'); + } + } } ?> diff --git a/application/models/Tax.php b/application/models/Tax.php index ab6b7bb3b..92391aa8a 100644 --- a/application/models/Tax.php +++ b/application/models/Tax.php @@ -7,62 +7,60 @@ class Tax extends CI_Model { /** - Determines if a given tax_code is on file - */ - public function exists($tax_code) + * Determines if a given row is on file + */ + public function exists($tax_rate_id) { - $this->db->from('tax_codes'); - $this->db->where('tax_code', $tax_code); - - return ($this->db->get()->num_rows() == 1); - } - - public function tax_rate_exists($tax_code, $tax_category_id) - { - $this->db->from('tax_code_rates'); - $this->db->where('rate_tax_code', $tax_code); - $this->db->where('rate_tax_category_id', $tax_category_id); - - return ($this->db->get()->num_rows() == 1); - } - - public function tax_category_exists($tax_category_id) - { - $this->db->from('tax_categories'); - $this->db->where('tax_category_id', $tax_category_id); + $this->db->from('tax_rates'); + $this->db->where('tax_rate_id', $tax_rate_id); return ($this->db->get()->num_rows() == 1); } /** - Gets total of rows - */ + * Gets total of rows + */ public function get_total_rows() { - $this->db->from('tax_codes'); + $this->db->from('tax_rates'); return $this->db->count_all_results(); } + /** + * Gets list of tax rates that are assigned to a particular tax category + */ public function get_tax_category_usage($tax_category_id) { - $this->db->from('tax_code_rates'); + $this->db->from('tax_rates'); $this->db->where('rate_tax_category_id', $tax_category_id); return $this->db->count_all_results(); } /** - Gets information about a particular tax_code - */ - public function get_info($tax_code) + * Gets the row for a particular id + */ + public function get_info($tax_rate_id) { - $this->db->from('tax_codes'); - $this->db->join('tax_code_rates', - 'tax_code = rate_tax_code and rate_tax_category_id = 1', 'LEFT'); + $this->db->select('tax_rate_id'); + $this->db->select('rate_tax_code_id'); + $this->db->select('tax_code'); + $this->db->select('tax_code_name'); + $this->db->select('rate_jurisdiction_id'); + $this->db->select('jurisdiction_name'); + $this->db->select('rate_tax_category_id'); + $this->db->select('tax_category'); + $this->db->select('tax_rate'); + $this->db->select('tax_rounding_code'); + $this->db->from('tax_rates'); + $this->db->join('tax_codes', + 'rate_tax_code_id = tax_code_id', 'LEFT'); $this->db->join('tax_categories', - 'rate_tax_category_id = tax_category_id'); - $this->db->where('tax_code', $tax_code); + 'rate_tax_category_id = tax_category_id', 'LEFT'); + $this->db->join('tax_jurisdictions', + 'rate_jurisdiction_id = jurisdiction_id', 'LEFT'); + $this->db->where('tax_rate_id', $tax_rate_id); $query = $this->db->get(); @@ -73,30 +71,49 @@ class Tax extends CI_Model else { //Get empty base parent object - $tax_code_obj = new stdClass(); + $tax_rate_obj = new stdClass(); + $tax_rate_obj->tax_rate_id = NULL; + $tax_rate_obj->rate_tax_code_id = NULL; + $tax_rate_obj->tax_code = ''; + $tax_rate_obj->tax_code_name = ''; + $tax_rate_obj->rate_tax_category_id = NULL; + $tax_rate_obj->tax_category = ''; + $tax_rate_obj->tax_rate = 0.0; + $tax_rate_obj->tax_rounding_code = '0'; + $tax_rate_obj->rate_jurisdiction_id = NULL; + $tax_rate_obj->jurisdiction_name = ''; //Get all the fields from tax_codes table - foreach($this->db->list_fields('tax_codes') as $field) - { - $tax_code_obj->$field = ''; - } - foreach($this->db->list_fields('tax_code_rates') as $field) - { - $tax_code_obj->$field = ''; - } - return $tax_code_obj; + return $tax_rate_obj; } } /** - Gets information about a particular tax_code - */ - public function get_rate_info($tax_code, $tax_category_id) + * Get taxes to be collected for a given tax code + */ + public function get_taxes($tax_code_id, $tax_category_id) { - $this->db->from('tax_code_rates'); + $query = $this->db->query('select tax_rate_id, rate_tax_code_id, tax_code, tax_code_name, tax_type, cascade_sequence, rate_tax_category_id, tax_category, + rate_jurisdiction_id, jurisdiction_name, tax_group, tax_rate, tax_rounding_code,tax_categories.tax_group_sequence + tax_jurisdictions.tax_group_sequence as tax_group_sequence + from ' . $this->db->dbprefix('tax_rates') . ' + left outer join ' . $this->db->dbprefix('tax_codes') . ' on rate_tax_code_id = tax_code_id + left outer join ' . $this->db->dbprefix('tax_categories') . ' as tax_categories on rate_tax_category_id = tax_category_id + left outer join ' . $this->db->dbprefix('tax_jurisdictions') . ' as tax_jurisdictions on rate_jurisdiction_id = jurisdiction_id + where rate_tax_code_id = ' . $this->db->escape($tax_code_id) . ' and rate_tax_category_id = ' . $this->db->escape($tax_category_id) . ' + order by cascade_sequence, tax_group, jurisdiction_name, tax_jurisdictions.tax_group_sequence + tax_categories.tax_group_sequence'); + + return $query->result_array(); + } + + /** + * Gets information about a particular tax_code + */ + public function get_rate_info($tax_code_id, $tax_category_id) + { + $this->db->from('tax_rates'); $this->db->join('tax_categories', 'rate_tax_category_id = tax_category_id'); - $this->db->where('rate_tax_code', $tax_code); + $this->db->where('rate_tax_code_id', $tax_code_id); $this->db->where('rate_tax_category_id', $tax_category_id); $query = $this->db->get(); @@ -111,11 +128,11 @@ class Tax extends CI_Model $tax_rate_obj = new stdClass(); //Get all the fields from tax_codes table - foreach($this->db->list_fields('tax_code_rates') as $field) + foreach($this->db->list_fields('tax_rates') as $field) { $tax_rate_obj->$field = ''; } - //Get all the fields from tax_code_rates table + //Get all the fields from tax_rates table foreach($this->db->list_fields('tax_categories') as $field) { $tax_rate_obj->$field = ''; @@ -126,67 +143,25 @@ class Tax extends CI_Model } /** - Gets the tax code to use for a given customer + Inserts or updates a tax_rates entry */ - public function get_sales_tax_code($city = '', $state = '') + public function save(&$tax_rate_data, $tax_rate_id = -1) { - // if tax code using both city and state cannot be found then try again using just the state - // if the state tax code cannot be found then try again using blanks for both - $this->db->from('tax_codes'); - $this->db->where('city', $city); - $this->db->where('state', $state); - $this->db->where('tax_code_type', '0'); // sales tax - - $query = $this->db->get(); - - if($query->num_rows() == 1) + if(!$this->exists($tax_rate_id)) { - return $query->row()->tax_code; - } - else - { - $this->db->from('tax_codes'); - $this->db->where('city', ''); - $this->db->where('state', $state); - $this->db->where('tax_code_type', '0'); // sales tax - - $query = $this->db->get(); - - if($query->num_rows() == 1) + if($this->db->insert('tax_rates', $tax_rate_data)) { - return $query->row()->tax_code; - } - else - { - return $this->config->item('default_origin_tax_code'); - } - } - - return FALSE; - } - - /** - Inserts or updates a tax_codes entry - */ - public function save(&$tax_code_data, $tax_rate_data, $tax_code = -1) - { - if(!$this->exists($tax_code)) - { - if($this->db->insert('tax_codes', $tax_code_data)) - { - $this->save_tax_rates($tax_rate_data, $tax_code); + $tax_rate_data['tax_rate_id'] = $this->db->insert_id(); return TRUE; } } else { - $this->db->where('tax_code', $tax_code); + $this->db->where('tax_rate_id', $tax_rate_id); - if($this->db->update('tax_codes', $tax_code_data)) + if($this->db->update('tax_rates', $tax_rate_data)) { - $this->save_tax_rates($tax_rate_data, $tax_code); - return TRUE; } } @@ -195,132 +170,65 @@ class Tax extends CI_Model } /** - * Inserts or updates a tax_category + * Deletes a single tax rate entry */ - public function save_tax_category(&$tax_category_data, &$tax_category_id) + public function delete($tax_rate_id) { - if(!$this->tax_category_exists($tax_category_id)) - { - $result = $this->db->insert('tax_categories', $tax_category_data); - - $tax_category_id = $this->db->insert_id(); - - return $result; - } - - $this->db->where('tax_category_id', $tax_category_id); - - return $this->db->update('tax_categories', $tax_category_data); + return $this->db->delete('tax_tax_rates', array('tax_rate_id' => $tax_rate_id)); } /** - * Inserts or updates a tax_rate + * Deletes a list of tax rates */ - public function save_tax_rates(&$tax_rate_data, $tax_code) + public function delete_list($tax_rate_ids) { - if(!$this->tax_rate_exists($tax_code, $tax_rate_data['rate_tax_category_id'])) - { - return $this->db->insert('tax_code_rates', $tax_rate_data); - } + $this->db->where_in('tax_rate_id', $tax_rate_ids); - $this->db->where('rate_tax_code', $tax_code); - $this->db->where('rate_tax_category_id', $tax_rate_data['rate_tax_category_id']); - - return $this->db->update('tax_code_rates', $tax_rate_data); + return $this->db->delete('tax_rates'); } /** - Inserts or updates an item kit's items - */ - public function save_tax_rate_exceptions(&$tax_rate_data, $tax_code) - { - $success = TRUE; - - //Run these queries as a transaction, we want to make sure we do all or nothing - - $this->db->trans_start(); - - // Delete all exceptions for the given tax_code - $this->delete_tax_rate_exceptions($tax_code); - - if($tax_rate_data != NULL) - { - foreach($tax_rate_data as $row) - { - $row['rate_tax_code'] = $tax_code; - $success &= $this->db->insert('tax_code_rates', $row); - } - } - - $this->db->trans_complete(); - - $success &= $this->db->trans_status(); - - return $success; - } - - /** - Deletes one tax_codes entry - */ - public function delete($tax_code) - { - return $this->db->delete('tax_codes', array('tax_code' => $tax_code)); - } - - /** - Deletes one tax_codes entry - */ - public function delete_tax_category($tax_category_id) - { - return $this->db->delete('tax_categories', array('tax_category_id' => $tax_category_id)); - } - - /** - Deletes a list of tax codes - */ - public function delete_list($tax_codes) - { - $this->db->where_in('tax_code', $tax_codes); - - return $this->db->delete('tax_codes'); - } - - /** - Deletes all tax_rate_exceptions for given tax codes - */ - public function delete_tax_rate_exceptions($tax_code) - { - $this->db->where('rate_tax_code', $tax_code); - $this->db->where('rate_tax_category_id !=', 1); - - return $this->db->delete('tax_code_rates'); - } - - /** - Gets tax_codes - */ + * Gets tax_codes + */ public function get_found_rows($search) { - return $this->search($search, 0, 0, 'tax_code', 'asc', TRUE); + return $this->search($search, 0, 0, 'tax_code_name', 'asc', TRUE); } /** - Performs a search on tax_codes - */ - public function search($search, $rows = 0, $limit_from = 0, $sort = 'tax_code', $order = 'asc', $count_only = FALSE) + * Performs a search on tax_rates + */ + public function search($search, $rows = 0, $limit_from = 0, $sort = 'tax_code_name', $order = 'asc', $count_only = FALSE) { // get_found_rows case + if($count_only == TRUE) { - $this->db->select('COUNT(tax_code) as count'); + $this->db->select('COUNT(tax_rate_id) as count'); + } else + { + $this->db->select('tax_rate_id'); + $this->db->select('tax_code'); + $this->db->select('rate_tax_code_id'); + $this->db->select('tax_code_name'); + $this->db->select('rate_jurisdiction_id'); + $this->db->select('jurisdiction_name'); + $this->db->select('rate_tax_category_id'); + $this->db->select('tax_category'); + $this->db->select('tax_rate'); + $this->db->select('tax_rounding_code'); } - - $this->db->from('tax_codes'); - $this->db->join('tax_code_rates', 'tax_code = rate_tax_code AND rate_tax_category_id = 1', 'LEFT'); + $this->db->from('tax_rates'); + $this->db->join('tax_codes', + 'rate_tax_code_id = tax_code_id', 'LEFT'); + $this->db->join('tax_categories', + 'rate_tax_category_id = tax_category_id', 'LEFT'); + $this->db->join('tax_jurisdictions', + 'rate_jurisdiction_id = jurisdiction_id', 'LEFT'); if(!empty($search)) { - $this->db->like('tax_code', $search); + $this->db->like('rate_tax_code', $search); $this->db->or_like('tax_code_name', $search); } @@ -344,60 +252,14 @@ class Tax extends CI_Model { if($tax_code_type == '0') { - return $this->lang->line('taxes_sales_tax'); + return $this->lang->line('taxes_tax_included'); } else { - return $this->lang->line('taxes_vat_tax'); + return $this->lang->line('taxes_tax_excluded'); } } - public function get_sales_tax_codes_search_suggestions($search, $limit = 25) - { - $suggestions = array(); - - $this->db->from('tax_codes'); - if(!empty($search)) - { - $this->db->like('tax_code', $search); - $this->db->or_like('tax_code_name', $search); - } - $this->db->order_by('tax_code_name', 'asc'); - - foreach($this->db->get()->result() as $row) - { - $suggestions[] = array('value' => $row->tax_code, 'label' => ($row->tax_code . ' ' . $row->tax_code_name)); - } - - //only return $limit suggestions - if(count($suggestions > $limit)) - { - $suggestions = array_slice($suggestions, 0,$limit); - } - - return $suggestions; - } - - public function get_tax_category_suggestions($search) - { - $suggestions = array(); - - $this->db->from('tax_categories'); - $this->db->where('tax_category_id !=', 1); - if(!empty($search)) - { - $this->db->like('tax_category', '%'.$search.'%'); - } - $this->db->order_by('tax_category', 'asc'); - - foreach($this->db->get()->result() as $row) - { - $suggestions[] = array('value' => $row->tax_category_id, 'label' => $row->tax_category); - } - - return $suggestions; - } - public function get_tax_category($tax_category_id) { $this->db->select('tax_category'); @@ -415,15 +277,6 @@ class Tax extends CI_Model return $this->db->get(); } - public function get_all_tax_codes() - { - $this->db->select('tax_code, tax_code_name'); - $this->db->from('tax_codes'); - $this->db->order_by('tax_code_name'); - - return $this->db->get(); - } - public function get_tax_category_id($tax_category) { $this->db->select('tax_category_id'); @@ -432,15 +285,5 @@ class Tax extends CI_Model return $this->db->get()->row()->tax_category_id; } - public function get_tax_code_rate_exceptions($tax_code) - { - $this->db->from('tax_code_rates'); - $this->db->join('tax_categories', 'rate_tax_category_id = tax_category_id'); - $this->db->where('rate_tax_code', $tax_code); - $this->db->where('rate_tax_category_id !=', 1); - $this->db->order_by('tax_category', 'asc'); - - return $this->db->get()->result_array(); - } } ?> diff --git a/application/models/Tax_category.php b/application/models/Tax_category.php new file mode 100644 index 000000000..76939609b --- /dev/null +++ b/application/models/Tax_category.php @@ -0,0 +1,243 @@ +db->from('tax_categories'); + $this->db->where('tax_category_id', $tax_category_id); + + return ($this->db->get()->num_rows() == 1); + } + + /** + * Gets total of rows + */ + public function get_total_rows() + { + $this->db->from('tax_categories'); + $this->db->where('deleted', 0); + + return $this->db->count_all_results(); + } + + /** + * Gets information about the particular record + */ + public function get_info($tax_category_id) + { + $this->db->from('tax_categories'); + $this->db->where('tax_category_id', $tax_category_id); + $this->db->where('deleted', 0); + $query = $this->db->get(); + + if($query->num_rows()==1) + { + return $query->row(); + } + else + { + //Get empty base parent object + $tax_category_obj = new stdClass(); + + //Get all the fields from the table + foreach($this->db->list_fields('tax_categories') as $field) + { + $tax_category_obj->$field = ''; + } + return $tax_category_obj; + } + } + + /** + * Returns all rows from the table + */ + public function get_all($rows = 0, $limit_from = 0, $no_deleted = TRUE) + { + $this->db->from('tax_categories'); + if($no_deleted == TRUE) + { + $this->db->where('deleted', 0); + } + + $this->db->order_by('tax_group_sequence', 'asc'); + $this->db->order_by('tax_category', 'asc'); + + if($rows > 0) + { + $this->db->limit($rows, $limit_from); + } + + return $this->db->get(); + } + + /** + * Returns multiple rows + */ + public function get_multiple_info($tax_category_ids) + { + $this->db->from('tax_categories'); + $this->db->where_in('tax_category_id', $tax_category_ids); + $this->db->order_by('tax_category', 'asc'); + + return $this->db->get(); + } + + /** + * Inserts or updates a row + */ + public function save(&$tax_category_data, $tax_category_id = FALSE) + { + if(!$tax_category_id || !$this->exists($tax_category_id)) + { + if($this->db->insert('tax_categories', $tax_category_data)) + { + $tax_category_data['tax_category_id'] = $this->db->insert_id(); + + return TRUE; + } + + return FALSE; + } + + $this->db->where('tax_category_id', $tax_category_id); + + return $this->db->update('tax_categories', $tax_category_data); + } + + /** + * Saves changes to the tax categories table + */ + public function save_categories($array_save) + { + $this->db->trans_start(); + + $not_to_delete = array(); + + foreach($array_save as $key => $value) + { + // save or update + $tax_category_data = array('tax_category' => $value['tax_category'], 'tax_group_sequence' => $value['tax_group_sequence'], 'deleted' => '0'); + $this->save($tax_category_data, $value['tax_category_id']); + if($value['tax_category_id'] == -1) + { + $not_to_delete[] = $tax_category_data['tax_category_id']; + } + else + { + $not_to_delete[] = $value['tax_category_id']; + } + } + + // all entries not available in post will be deleted now + $deleted_tax_categories = $this->get_all()->result_array(); + + foreach($deleted_tax_categories as $key => $tax_category_data) + { + if(!in_array($tax_category_data['tax_category_id'], $not_to_delete)) + { + $this->delete($tax_category_data['tax_category_id']); + } + } + + $this->db->trans_complete(); + return $this->db->trans_status(); + } + + /** + * Soft delete a specific row + */ + public function delete($tax_category_id) + { + $this->db->where('tax_category_id', $tax_category_id); + + return $this->db->update('tax_categories', array('deleted' => 1)); + } + + /** + * Deletes a list of rows + */ + public function delete_list($tax_category_ids) + { + $this->db->where_in('tax_category_id', $tax_category_ids); + + return $this->db->update('tax_categories', array('deleted' => 1)); + } + + /** + * Gets rows + */ + public function get_found_rows($search) + { + return $this->search($search, 0, 0, 'tax_category', 'asc', TRUE); + } + + /** + * Perform a search for a set of rows + */ + public function search($search, $rows = 0, $limit_from = 0, $sort = 'tax_category', $order='asc', $count_only = FALSE) + { + // get_found_rows case + if($count_only == TRUE) + { + $this->db->select('COUNT(tax_categories.tax_category_id) as count'); + } + + $this->db->from('tax_categories AS tax_categories'); + $this->db->like('tax_category', $search); + $this->db->where('deleted', 0); + + // get_found_rows case + if($count_only == TRUE) + { + return $this->db->get()->row()->count; + } + + $this->db->order_by($sort, $order); + + if($rows > 0) + { + $this->db->limit($rows, $limit_from); + } + + return $this->db->get(); + } + + public function get_tax_category_suggestions($search) + { + $suggestions = array(); + + $this->db->from('tax_categories'); + $this->db->where('deleted', 0); + if(!empty($search)) + { + $this->db->like('tax_category', '%'.$search.'%'); + } + $this->db->order_by('tax_category', 'asc'); + + foreach($this->db->get()->result() as $row) + { + $suggestions[] = array('value' => $row->tax_category_id, 'label' => $row->tax_category); + } + + return $suggestions; + } + + public function get_empty_row() + { + return array('0' => array( + 'tax_category_id' => -1, + 'tax_category' => '', + 'tax_group_sequence' => 0, + 'deleted' => 0)); + } + +} +?> diff --git a/application/models/Tax_code.php b/application/models/Tax_code.php new file mode 100644 index 000000000..16b09b025 --- /dev/null +++ b/application/models/Tax_code.php @@ -0,0 +1,283 @@ +db->from('tax_codes'); + $this->db->where('tax_code', $tax_code); + + return ($this->db->get()->num_rows() == 1); + } + + /** + * Gets total of rows + */ + public function get_total_rows() + { + $this->db->from('tax_codes'); + $this->db->where('deleted', 0); + + return $this->db->count_all_results(); + } + + /** + * Gets information about the particular record + */ + public function get_info($tax_code_id) + { + $this->db->from('tax_codes'); + $this->db->where('tax_code_id', $tax_code_id); + $this->db->where('deleted', 0); + $query = $this->db->get(); + + if($query->num_rows()==1) + { + return $query->row(); + } + else + { + //Get empty base parent object + $tax_code_obj = new stdClass(); + + //Get all the fields from the table + foreach($this->db->list_fields('tax_codes') as $field) + { + $tax_code_obj->$field = ''; + } + return $tax_code_obj; + } + } + + /** + * Returns all rows from the table + */ + public function get_all($rows = 0, $limit_from = 0, $no_deleted = TRUE) + { + $this->db->from('tax_codes'); + if($no_deleted == TRUE) + { + $this->db->where('deleted', 0); + } + + $this->db->order_by('tax_code_name', 'asc'); + + if($rows > 0) + { + $this->db->limit($rows, $limit_from); + } + + return $this->db->get(); + } + + /** + * Returns multiple rows + */ + public function get_multiple_info($tax_codes) + { + $this->db->from('tax_codes'); + $this->db->where_in('tax_code', $tax_codes); + $this->db->order_by('tax_code_name', 'asc'); + + return $this->db->get(); + } + + /** + * Inserts or updates a row + */ + public function save(&$tax_code_data) + { + if(!$this->exists($tax_code_data['tax_code'])) + { + if($this->db->insert('tax_codes', $tax_code_data)) + { + return TRUE; + } + return FALSE; + } + + $this->db->where('tax_code', $tax_code_data['tax_code']); + + return $this->db->update('tax_codes', $tax_code_data); + } + + /** + * Saves changes to the tax codes table + */ + public function save_tax_codes($array_save) + { + $this->db->trans_start(); + + $not_to_delete = array(); + + foreach($array_save as $key => $value) + { + // save or update + $tax_code_data = array('tax_code' => $value['tax_code'], 'tax_code_name' => $value['tax_code_name'], 'city' => $value['city'], 'state' => $value['state'], 'deleted' => '0'); + $this->save($tax_code_data); + $not_to_delete[] = $tax_code_data['tax_code']; + } + + // all entries not available in post will be deleted now + $deleted_tax_codes = $this->get_all()->result_array(); + + foreach($deleted_tax_codes as $key => $tax_code_data) + { + if(!in_array($tax_code_data['tax_code'], $not_to_delete)) + { + $this->delete($tax_code_data['tax_code']); + } + } + + $this->db->trans_complete(); + return $this->db->trans_status(); + } + + /** + * Deletes a specific tax code + */ + public function delete($tax_code) + { + $this->db->where('tax_code', $tax_code); + + return $this->db->update('tax_codes', array('deleted' => 1)); + } + + /** + * Deletes a list of rows + */ + public function delete_list($tax_codes) + { + $this->db->where_in('tax_code', $tax_codes); + + return $this->db->update('tax_codes', array('deleted' => 1)); + } + + /** + * Gets rows + */ + public function get_found_rows($search) + { + return $this->search($search, 0, 0, 'tax_code_name', 'asc', TRUE); + } + + /** + * Perform a search for a set of rows + */ + public function search($search, $rows = 0, $limit_from = 0, $sort = 'tax_code_name', $order='asc', $count_only = FALSE) + { + // get_found_rows case + if($count_only == TRUE) + { + $this->db->select('COUNT(tax_codes.tax_code) as count'); + } + + $this->db->from('tax_codes AS tax_codes'); + $this->db->group_start(); + $this->db->like('tax_code_name', $search); + $this->db->or_like('tax_code', $search); + $this->db->group_end(); + $this->db->where('deleted', 0); + + // get_found_rows case + if($count_only == TRUE) + { + return $this->db->get()->row()->count; + } + + $this->db->order_by($sort, $order); + + if($rows > 0) + { + $this->db->limit($rows, $limit_from); + } + + return $this->db->get(); + } + + /** + * Gets the tax code to use for a given customer + */ + public function get_sales_tax_code($city = '', $state = '') + { + // if tax code using both city and state cannot be found then try again using just the state + // if the state tax code cannot be found then try again using blanks for both + $this->db->from('tax_codes'); + $this->db->where('city', $city); + $this->db->where('state', $state); + $this->db->where('deleted', 0); + + + $query = $this->db->get(); + + if($query->num_rows() == 1) + { + return $query->row()->tax_code_id; + } + else + { + $this->db->from('tax_codes'); + $this->db->where('city', ''); + $this->db->where('state', $state); + $this->db->where('deleted', 0); + + $query = $this->db->get(); + + if($query->num_rows() == 1) + { + return $query->row()->tax_code_id; + } + else + { + return $this->config->item('default_tax_code'); + } + } + return FALSE; + } + + public function get_tax_codes_search_suggestions($search, $limit = 25) + { + $suggestions = array(); + + $this->db->from('tax_codes'); + if(!empty($search)) + { + $this->db->like('tax_code', $search); + $this->db->or_like('tax_code_name', $search); + } + $this->db->where('deleted', 0); + $this->db->order_by('tax_code_name', 'asc'); + + foreach($this->db->get()->result() as $row) + { + $suggestions[] = array('value' => $row->tax_code_id, 'label' => ($row->tax_code . ' ' . $row->tax_code_name)); + } + + //only return $limit suggestions + if(count($suggestions) > $limit) + { + $suggestions = array_slice($suggestions, 0,$limit); + } + + return $suggestions; + } + + public function get_empty_row() + { + return array('0' => array( + 'tax_code_id' => -1, + 'tax_code' => '', + 'tax_code_name' => '', + 'city' => '', + 'state' => '', + 'deleted' => 0)); + } +} +?> diff --git a/application/models/Tax_jurisdiction.php b/application/models/Tax_jurisdiction.php new file mode 100644 index 000000000..e27ba46b4 --- /dev/null +++ b/application/models/Tax_jurisdiction.php @@ -0,0 +1,228 @@ +db->from('tax_jurisdictions'); + $this->db->where('jurisdiction_id', $jurisdiction_id); + + return ($this->db->get()->num_rows() == 1); + } + + /** + * Gets total of rows + */ + public function get_total_rows() + { + $this->db->from('tax_jurisdictions'); + $this->db->where('deleted', 0); + + return $this->db->count_all_results(); + } + + /** + * Gets information about the particular record + */ + public function get_info($jurisdiction_id) + { + $this->db->from('tax_jurisdictions'); + $this->db->where('jurisdiction_id', $jurisdiction_id); + $this->db->where('deleted', 0); + $query = $this->db->get(); + + if($query->num_rows()==1) + { + return $query->row(); + } + else + { + //Get empty base parent object + $tax_jurisdiction_obj = new stdClass(); + + //Get all the fields from the table + foreach($this->db->list_fields('tax_jurisdictions') as $field) + { + $tax_jurisdiction_obj->$field = ''; + } + return $tax_jurisdiction_obj; + } + } + + /** + * Returns all rows from the table + */ + public function get_all($rows = 0, $limit_from = 0, $no_deleted = TRUE) + { + $this->db->from('tax_jurisdictions'); + if($no_deleted == TRUE) + { + $this->db->where('deleted', 0); + } + + $this->db->order_by('jurisdiction_name', 'asc'); + + if($rows > 0) + { + $this->db->limit($rows, $limit_from); + } + + return $this->db->get(); + } + + /** + * Returns multiple rows + */ + public function get_multiple_info($jurisdiction_ids) + { + $this->db->from('tax_jurisdictions'); + $this->db->where_in('jurisdiction_id', $jurisdiction_ids); + $this->db->order_by('jurisdiction_name', 'asc'); + + return $this->db->get(); + } + + /** + * Inserts or updates a row + */ + public function save(&$jurisdiction_data, $jurisdiction_id = FALSE) + { + if(!$jurisdiction_id || !$this->exists($jurisdiction_id)) + { + if($this->db->insert('tax_jurisdictions', $jurisdiction_data)) + { + $jurisdiction_data['jurisdiction_id'] = $this->db->insert_id(); + return TRUE; + } + + return FALSE; + } + + $this->db->where('jurisdiction_id', $jurisdiction_id); + + return $this->db->update('tax_jurisdictions', $jurisdiction_data); + } + + /** + * Saves changes to the tax jurisdictions table + */ + public function save_jurisdictions($array_save) + { + $this->db->trans_start(); + + $not_to_delete = array(); + + foreach($array_save as $key => $value) + { + // save or update + $tax_jurisdiction_data = array('jurisdiction_name' => $value['jurisdiction_name'], 'tax_group' => $value['tax_group'], 'tax_type' => $value['tax_type'], 'reporting_authority' => $value['reporting_authority'], 'tax_group_sequence' => $value['tax_group_sequence'], 'cascade_sequence' => $value['cascade_sequence'], 'deleted' => '0'); + $this->save($tax_jurisdiction_data, $value['jurisdiction_id']); + if($value['jurisdiction_id'] == -1) + { + $not_to_delete[] = $tax_jurisdiction_data['jurisdiction_id']; + } + else + { + $not_to_delete[] = $value['jurisdiction_id']; + } + } + + // all entries not available in post will be deleted now + $deleted_tax_jurisdictions = $this->get_all()->result_array(); + + foreach($deleted_tax_jurisdictions as $key => $tax_jurisdiction_data) + { + if(!in_array($tax_jurisdiction_data['jurisdiction_id'], $not_to_delete)) + { + $this->delete($tax_jurisdiction_data['jurisdiction_id']); + } + } + + $this->db->trans_complete(); + return $this->db->trans_status(); + } + + /** + * Soft deletes a specific tax jurisdiction + */ + public function delete($jurisdiction_id) + { + $this->db->where('jurisdiction_id', $jurisdiction_id); + + return $this->db->update('tax_jurisdictions', array('deleted' => 1)); + } + + /** + * Soft deletes a list of rows + */ + public function delete_list($jurisdiction_ids) + { + $this->db->where_in('jurisdiction_id', $jurisdiction_ids); + + return $this->db->update('tax_jurisdictions', array('deleted' => 1)); + } + + /** + * Gets rows + */ + public function get_found_rows($search) + { + return $this->search($search, 0, 0, 'jurisdiction_name', 'asc', TRUE); + } + + /** + * Perform a search for a set of rows + */ + public function search($search, $rows = 0, $limit_from = 0, $sort = 'jurisdiction_name', $order='asc', $count_only = FALSE) + { + // get_found_rows case + if($count_only == TRUE) + { + $this->db->select('COUNT(tax_jurisdictions.jurisdiction_id) as count'); + } + + $this->db->from('tax_jurisdictions AS tax_jurisdictions'); + $this->db->group_start(); + $this->db->like('jurisdiction_name', $search); + $this->db->or_like('reporting_authority', $search); + $this->db->group_end(); + $this->db->where('deleted', 0); + + // get_found_rows case + if($count_only == TRUE) + { + return $this->db->get()->row()->count; + } + + $this->db->order_by($sort, $order); + + if($rows > 0) + { + $this->db->limit($rows, $limit_from); + } + + return $this->db->get(); + } + + public function get_empty_row() + { + return array('0' => array( + 'jurisdiction_id' => -1, + 'jurisdiction_name' => '', + 'tax_group' => '', + 'tax_type' => '1', + 'reporting_authority' => '', + 'tax_group_sequence' => 0, + 'cascade_sequence' => 0, + 'deleted' => 0)); + } + +} +?> diff --git a/application/models/reports/Detailed_receivings.php b/application/models/reports/Detailed_receivings.php index 3941da25b..c2f3f795b 100644 --- a/application/models/reports/Detailed_receivings.php +++ b/application/models/reports/Detailed_receivings.php @@ -12,7 +12,7 @@ class Detailed_receivings extends Report public function getDataColumns() { - $columns = array( + return array( 'summary' => array( array('id' => $this->lang->line('reports_receiving_id')), array('receiving_date' => $this->lang->line('reports_date'), 'sortable' => FALSE), @@ -31,8 +31,6 @@ class Detailed_receivings extends Report $this->lang->line('reports_total'), $this->lang->line('reports_discount')) ); - - return $columns; } public function getDataByReceivingId($receiving_id) @@ -99,10 +97,17 @@ class Detailed_receivings extends Report foreach($data['summary'] as $key=>$value) { - $this->db->select('name, item_number, category, quantity_purchased, serialnumber,total, discount_percent, item_location, receivings_items_temp.receiving_quantity'); + $this->db->select('name, item_number, category, quantity_purchased, serialnumber, total, discount, discount_type, item_location, receivings_items_temp.receiving_quantity'); $this->db->from('receivings_items_temp'); $this->db->join('items', 'receivings_items_temp.item_id = items.item_id'); - $this->db->where('receiving_id = '.$value['receiving_id']); + if (count($inputs['definition_ids']) > 0) + { + $this->db->select('GROUP_CONCAT(DISTINCT CONCAT_WS(\':\', definition_id, attribute_value) ORDER BY definition_id SEPARATOR \'|\') AS attribute_values'); + $this->db->join('attribute_links', 'attribute_links.item_id = items.item_id AND attribute_links.receiving_id = receivings_items_temp.receiving_id AND definition_id IN (' . implode(',', $inputs['definition_ids']) . ')', 'left'); + $this->db->join('attribute_values', 'attribute_values.attribute_id = attribute_links.attribute_id', 'left'); + $this->db->group_by('receivings_items_temp.receiving_id, receivings_items_temp.item_id'); + } + $this->db->where('receivings_items_temp.receiving_id', $value['receiving_id']); $data['details'][$key] = $this->db->get()->result_array(); } diff --git a/application/models/reports/Detailed_sales.php b/application/models/reports/Detailed_sales.php index 79645af2c..a63655fa1 100644 --- a/application/models/reports/Detailed_sales.php +++ b/application/models/reports/Detailed_sales.php @@ -144,10 +144,18 @@ class Detailed_sales extends Report foreach($data['summary'] as $key=>$value) { - $this->db->select('name, category, quantity_purchased, item_location, serialnumber, description, subtotal, tax, total, cost, profit, discount_percent, sale_status'); + $this->db->select('name, category, quantity_purchased, item_location, serialnumber, description, subtotal, tax, total, cost, profit, discount, discount_type, sale_status'); $this->db->from('sales_items_temp'); - $this->db->where('sale_id', $value['sale_id']); + if (count($inputs['definition_ids']) > 0) + { + $this->db->select('GROUP_CONCAT(DISTINCT CONCAT_WS(\':\', definition_id, attribute_value) ORDER BY definition_id SEPARATOR \'|\') AS attribute_values'); + $this->db->join('attribute_links', 'attribute_links.item_id = sales_items_temp.item_id AND attribute_links.sale_id = sales_items_temp.sale_id AND definition_id IN (' . implode(',', $inputs['definition_ids']) . ')', 'left'); + $this->db->join('attribute_values', 'attribute_values.attribute_id = attribute_links.attribute_id', 'left'); + $this->db->group_by('sales_items_temp.sale_id, sales_items_temp.item_id'); + } + $this->db->where('sales_items_temp.sale_id', $value['sale_id']); $data['details'][$key] = $this->db->get()->result_array(); + $this->db->select('used, earned'); $this->db->from('sales_reward_points'); $this->db->where('sale_id', $value['sale_id']); diff --git a/application/models/reports/Inventory_low.php b/application/models/reports/Inventory_low.php index 2cca4519f..35842f700 100644 --- a/application/models/reports/Inventory_low.php +++ b/application/models/reports/Inventory_low.php @@ -16,17 +16,20 @@ class Inventory_low extends Report public function getData(array $inputs) { - $this->db->select('items.name, items.item_number, item_quantities.quantity, items.reorder_level, stock_locations.location_name'); - $this->db->from('items'); - $this->db->join('item_quantities', 'items.item_id = item_quantities.item_id'); - $this->db->join('stock_locations', 'item_quantities.location_id = stock_locations.location_id'); - $this->db->where('items.deleted', 0); - $this->db->where('stock_locations.deleted', 0); - $this->db->where('items.stock_type', 0); - $this->db->where('item_quantities.quantity <= items.reorder_level'); - $this->db->order_by('items.name'); + $query = $this->db->query("SELECT " . $this->Item->get_item_name('name') . ", + items.item_number, + item_quantities.quantity, + items.reorder_level, + stock_locations.location_name + FROM " . $this->db->dbprefix('items') . " AS items + JOIN " . $this->db->dbprefix('item_quantities') . " AS item_quantities ON items.item_id = item_quantities.item_id + JOIN " . $this->db->dbprefix('stock_locations') . " AS stock_locations ON item_quantities.location_id = stock_locations.location_id + WHERE items.deleted = 0 + AND items.stock_type = 0 + AND item_quantities.quantity <= items.reorder_level + ORDER BY items.name"); - return $this->db->get()->result_array(); + return $query->result_array(); } public function getSummaryData(array $inputs) diff --git a/application/models/reports/Inventory_summary.php b/application/models/reports/Inventory_summary.php index 51c4e2004..37736df9d 100644 --- a/application/models/reports/Inventory_summary.php +++ b/application/models/reports/Inventory_summary.php @@ -9,6 +9,7 @@ class Inventory_summary extends Report return array(array('item_name' => $this->lang->line('reports_item_name')), array('item_number' => $this->lang->line('reports_item_number')), array('quantity' => $this->lang->line('reports_quantity')), + array('low_sell_quantity' => $this->lang->line('reports_low_sell_quantity')), array('reorder_level' => $this->lang->line('reports_reorder_level')), array('location_name' => $this->lang->line('reports_stock_location')), array('cost_price' => $this->lang->line('reports_cost_price'), 'sorter' => 'number_sorter'), @@ -18,7 +19,7 @@ class Inventory_summary extends Report public function getData(array $inputs) { - $this->db->select('items.name, items.item_number, item_quantities.quantity, items.reorder_level, stock_locations.location_name, items.cost_price, items.unit_price, (items.cost_price * item_quantities.quantity) AS sub_total_value'); + $this->db->select($this->Item->get_item_name('name') . ', items.item_number, item_quantities.quantity, (item_quantities.quantity * items.qty_per_pack) as low_sell_quantity, items.reorder_level, stock_locations.location_name, items.cost_price, items.unit_price, (items.cost_price * item_quantities.quantity) AS sub_total_value'); $this->db->from('items AS items'); $this->db->join('item_quantities AS item_quantities', 'items.item_id = item_quantities.item_id'); $this->db->join('stock_locations AS stock_locations', 'item_quantities.location_id = stock_locations.location_id'); @@ -42,6 +43,7 @@ class Inventory_summary extends Report } $this->db->order_by('items.name'); + $this->db->order_by('items.qty_per_pack'); return $this->db->get()->result_array(); } @@ -54,12 +56,13 @@ class Inventory_summary extends Report */ public function getSummaryData(array $inputs) { - $return = array('total_inventory_value' => 0, 'total_quantity' => 0, 'total_retail' => 0); + $return = array('total_inventory_value' => 0, 'total_quantity' => 0, 'total_low_sell_quantity' => 0, 'total_retail' => 0); foreach($inputs as $input) { $return['total_inventory_value'] += $input['sub_total_value']; $return['total_quantity'] += $input['quantity']; + $return['total_low_sell_quantity'] += $input['low_sell_quantity']; $return['total_retail'] += $input['unit_price'] * $input['quantity']; } diff --git a/application/models/reports/Specific_customer.php b/application/models/reports/Specific_customer.php index 54623489c..1184f351c 100644 --- a/application/models/reports/Specific_customer.php +++ b/application/models/reports/Specific_customer.php @@ -127,7 +127,7 @@ class Specific_customer extends Report foreach($data['summary'] as $key=>$value) { - $this->db->select('name, category, serialnumber, description, quantity_purchased, subtotal, tax, total, cost, profit, discount_percent'); + $this->db->select('name, category, serialnumber, description, quantity_purchased, subtotal, tax, total, cost, profit, discount, discount_type'); $this->db->from('sales_items_temp'); $this->db->where('sale_id', $value['sale_id']); $data['details'][$key] = $this->db->get()->result_array(); diff --git a/application/models/reports/Specific_discount.php b/application/models/reports/Specific_discount.php index 91bc2be3c..03f05623d 100755 --- a/application/models/reports/Specific_discount.php +++ b/application/models/reports/Specific_discount.php @@ -70,7 +70,9 @@ class Specific_discount extends Report MAX(payment_type) AS payment_type, MAX(comment) AS comment'); $this->db->from('sales_items_temp'); - $this->db->where('discount_percent >=', $inputs['discount']); + + $this->db->where('discount >=', $inputs['discount']); + $this->db->where('discount_type',$inputs['discount_type']); if($inputs['sale_type'] == 'complete') { @@ -119,7 +121,7 @@ class Specific_discount extends Report foreach($data['summary'] as $key=>$value) { - $this->db->select('name, category, serialnumber, description, quantity_purchased, subtotal, tax, total, cost, profit, discount_percent'); + $this->db->select('name, category, serialnumber, description, quantity_purchased, subtotal, tax, total, cost, profit, discount, discount_type'); $this->db->from('sales_items_temp'); $this->db->where('sale_id', $value['sale_id']); $data['details'][$key] = $this->db->get()->result_array(); @@ -136,7 +138,9 @@ class Specific_discount extends Report { $this->db->select('SUM(subtotal) AS subtotal, SUM(tax) AS tax, SUM(total) AS total, SUM(cost) AS cost, SUM(profit) AS profit'); $this->db->from('sales_items_temp'); - $this->db->where('discount_percent >=', $inputs['discount']); + + $this->db->where('discount >=', $inputs['discount']); + $this->db->where('discount_type',$inputs['discount_type']); if($inputs['sale_type'] == 'complete') { diff --git a/application/models/reports/Specific_employee.php b/application/models/reports/Specific_employee.php index 1b4484227..36bdc0957 100644 --- a/application/models/reports/Specific_employee.php +++ b/application/models/reports/Specific_employee.php @@ -117,7 +117,7 @@ class Specific_employee extends Report foreach($data['summary'] as $key=>$value) { - $this->db->select('name, category, serialnumber, description, quantity_purchased, subtotal, tax, total, cost, profit, discount_percent'); + $this->db->select('name, category, serialnumber, description, quantity_purchased, subtotal, tax, total, cost, profit, discount, discount_type'); $this->db->from('sales_items_temp'); $this->db->where('sale_id', $value['sale_id']); $data['details'][$key] = $this->db->get()->result_array(); diff --git a/application/models/reports/Summary_discounts.php b/application/models/reports/Summary_discounts.php index e94119f6d..02b3970ff 100644 --- a/application/models/reports/Summary_discounts.php +++ b/application/models/reports/Summary_discounts.php @@ -7,23 +7,34 @@ class Summary_discounts extends Summary_report protected function _get_data_columns() { return array( - array('discount' => $this->lang->line('reports_discount_percent'), 'sorter' => 'number_sorter'), - array('count' => $this->lang->line('reports_count'))); + array('discount' => $this->lang->line('reports_discount'), 'sorter' => 'number_sorter'), + array('count' => $this->lang->line('reports_count')), + array('total' => $this->lang->line('reports_total'))); } public function getData(array $inputs) { - $this->db->select('MAX(CONCAT(sales_items.discount_percent, "%")) AS discount_percent, count(*) AS count'); + if($inputs['discount_type'] == FIXED) + { + $this->db->select('SUM(sales_items.discount) AS total, MAX(CONCAT("'.$this->config->item('currency_symbol').'",sales_items.discount)) AS discount, count(*) AS count'); + $this->db->where('discount_type',FIXED); + } + elseif($inputs['discount_type'] == PERCENT) + { + $this->db->select('SUM(item_unit_price) * sales_items.discount / 100.0 AS total, MAX(CONCAT(sales_items.discount, "%")) AS discount, count(*) AS count'); + $this->db->where('discount_type',PERCENT); + } + + $this->db->where('discount > 0'); + $this->db->group_by('sales_items.discount'); + $this->db->order_by('sales_items.discount'); + + $this->db->from('sales_items AS sales_items'); $this->db->join('sales AS sales', 'sales_items.sale_id = sales.sale_id', 'inner'); - $this->db->where('discount_percent > 0'); - $this->_where($inputs); - $this->db->group_by('sales_items.discount_percent'); - $this->db->order_by('sales_items.discount_percent'); - return $this->db->get()->result_array(); } } diff --git a/application/models/reports/Summary_payments.php b/application/models/reports/Summary_payments.php index 043cc8a43..79e82a03c 100644 --- a/application/models/reports/Summary_payments.php +++ b/application/models/reports/Summary_payments.php @@ -9,15 +9,15 @@ class Summary_payments extends Summary_report return array( array('payment_type' => $this->lang->line('reports_payment_type')), array('report_count' => $this->lang->line('reports_count')), - array('amount_tendered' => $this->lang->line('sales_amount_tendered'), 'sorter' => 'number_sorter')); + array('amount_due' => $this->lang->line('sales_amount_due'), 'sorter' => 'number_sorter')); } public function getData(array $inputs) { - $this->db->select('sales_payments.payment_type, COUNT(sales_payments.sale_id) AS count, SUM(sales_payments.payment_amount) AS payment_amount'); + $this->db->select('sales_payments.payment_type, COUNT(DISTINCT sales_payments.sale_id) AS count, SUM(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) AS payment_amount'); $this->db->from('sales_payments AS sales_payments'); $this->db->join('sales AS sales', 'sales.sale_id = sales_payments.sale_id'); - $this->db->join('sales_items AS sales_items', 'sales_items.sale_id = sales_payments.sale_id AND sales_items.line = 1'); + $this->db->join('sales_items AS sales_items', 'sales_items.sale_id = sales_payments.sale_id', 'left'); $this->_where($inputs); diff --git a/application/models/reports/Summary_report.php b/application/models/reports/Summary_report.php index a974336e4..2511f85bd 100644 --- a/application/models/reports/Summary_report.php +++ b/application/models/reports/Summary_report.php @@ -23,7 +23,7 @@ abstract class Summary_report extends Report $decimals = totals_decimals(); - $sale_price = 'sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100)'; + $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'; $sale_cost = 'SUM(sales_items.item_cost_price * sales_items.quantity_purchased)'; $tax = 'IFNULL(SUM(sales_items_taxes.tax), 0)'; diff --git a/application/models/reports/Summary_sales_taxes.php b/application/models/reports/Summary_sales_taxes.php new file mode 100644 index 000000000..84087ee86 --- /dev/null +++ b/application/models/reports/Summary_sales_taxes.php @@ -0,0 +1,56 @@ + $this->lang->line('reports_authority')), + array('jurisdiction_name' => $this->lang->line('reports_jurisdiction')), + array('tax_category' => $this->lang->line('reports_tax_category')), + array('tax_rate' => $this->lang->line('reports_tax_rate'), 'sorter' => 'number_sorter'), + array('tax' => $this->lang->line('reports_tax'), 'sorter' => 'number_sorter')); + } + + protected function _where(array $inputs) + { + if(empty($this->config->item('date_or_time_format'))) + { + $this->db->where('DATE(sales.sale_time) BETWEEN ' . $this->db->escape($inputs['start_date']) . ' AND ' . $this->db->escape($inputs['end_date'])); + } + else + { + $this->db->where('sales.sale_time BETWEEN ' . $this->db->escape(rawurldecode($inputs['start_date'])) . ' AND ' . $this->db->escape(rawurldecode($inputs['end_date']))); + } + } + + public function getData(array $inputs) + { + $where = ''; + + + + if(empty($this->config->item('date_or_time_format'))) + { + $where .= 'WHERE DATE(sale_time) BETWEEN ' . $this->db->escape($inputs['start_date']) . ' AND ' . $this->db->escape($inputs['end_date']); + } + else + { + $where .= 'WHERE sale_time BETWEEN ' . $this->db->escape(rawurldecode($inputs['start_date'])) . ' AND ' . $this->db->escape(rawurldecode($inputs['end_date'])); + } + + $query = $this->db->query("SELECT reporting_authority, jurisdiction_name, tax_category, tax_rate, + SUM(sale_tax_amount) AS tax + FROM " . $this->db->dbprefix('sales_taxes') . " AS sales_taxes + JOIN " . $this->db->dbprefix('sales') . " AS sales ON sales_taxes.sale_id = sales.sale_id + JOIN " . $this->db->dbprefix('tax_categories') . " AS tax_categories ON sales_taxes.tax_category_id = tax_categories.tax_category_id + JOIN " . $this->db->dbprefix('tax_jurisdictions') . " AS tax_jurisdictions ON sales_taxes.jurisdiction_id = tax_jurisdictions.jurisdiction_id " + . $where . + "GROUP BY reporting_authority, jurisdiction_name, tax_category; "); + + return $query->result_array(); + } +} +?> diff --git a/application/models/reports/Summary_taxes.php b/application/models/reports/Summary_taxes.php index d57a61510..11ab9f343 100644 --- a/application/models/reports/Summary_taxes.php +++ b/application/models/reports/Summary_taxes.php @@ -41,13 +41,13 @@ class Summary_taxes extends Summary_report if($this->config->item('tax_included')) { - $sale_total = '(sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100))'; - $sale_subtotal = '(sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100) * (100 / (100 + sales_items_taxes.percent)))'; + $sale_total = '(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)'; + $sale_subtotal = '(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 * (100 / (100 + sales_items_taxes.percent)))'; } else { - $sale_total = '(sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100) * (1 + (sales_items_taxes.percent / 100)))'; - $sale_subtotal = '(sales_items.item_unit_price * sales_items.quantity_purchased * (1 - sales_items.discount_percent / 100))'; + $sale_total = '(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 * (1 + (sales_items_taxes.percent / 100)))'; + $sale_subtotal = '(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)'; } $decimals = totals_decimals(); diff --git a/application/views/attributes/form.php b/application/views/attributes/form.php new file mode 100644 index 000000000..8cda67c92 --- /dev/null +++ b/application/views/attributes/form.php @@ -0,0 +1,187 @@ +