Add support for multi-pack items.

This commit is contained in:
jekkos
2018-06-12 14:06:39 +02:00
committed by Steve Ireland
parent 1026ebb0eb
commit a4e135eb92
29 changed files with 431 additions and 149 deletions

View File

@@ -299,6 +299,7 @@ class Config extends Secure_Controller
'suggestions_third_column' => $this->input->post('suggestions_third_column'),
'giftcard_number' => $this->input->post('giftcard_number'),
'derive_sale_quantity' => $this->input->post('derive_sale_quantity') != NULL,
'multi_pack_enabled' => $this->input->post('multi_pack_enabled') != NULL,
'custom1_name' => $this->input->post('custom1_name'),
'custom2_name' => $this->input->post('custom2_name'),
'custom3_name' => $this->input->post('custom3_name'),

View File

@@ -128,6 +128,15 @@ class Items extends Secure_Controller
echo json_encode($suggestions);
}
public function suggest_low_sell()
{
$suggestions = $this->xss_clean($this->Item->get_low_sell_suggestions($this->input->post_get('name')));
echo json_encode($suggestions);
}
public function suggest_kits()
{
$suggestions = $this->xss_clean($this->Item->get_kit_search_suggestions($this->input->post_get('term'),
@@ -181,9 +190,13 @@ class Items extends Secure_Controller
public function view($item_id = -1)
{
if($item_id == -1)
{
$data = array();
}
// allow_temp_items is set in the index function of items.php or sales.php
$data['allow_temp_item'] = $this->session->userdata('allow_temp_items');
$data['item_tax_info'] = $this->xss_clean($this->Item_taxes->get_info($item_id));
$data['default_tax_1_rate'] = '';
$data['default_tax_2_rate'] = '';
@@ -224,6 +237,8 @@ class Items extends Secure_Controller
$item_info->item_type = ITEM; // standard
$item_info->stock_type = HAS_STOCK;
$item_info->tax_category_id = 1; // Standard
$item_info->qty_per_pack = 1;
$item_info->pack_name = $this->lang->line('items_default_pack_name');
}
$data['item_info'] = $item_info;
@@ -279,6 +294,18 @@ class Items extends Secure_Controller
$data['stock_locations'] = $location_array;
}
$data['selected_low_sell_item_id'] = $item_info->low_sell_item_id;
if($item_id != -1 && $item_info->item_id != $item_info->low_sell_item_id)
{
$low_sell_item_info = $this->Item->get_info($item_info->low_sell_item_id);
$data['selected_low_sell_item'] = implode(NAME_SEPARATOR, array($low_sell_item_info->name, $low_sell_item_info->pack_name));
}
else
{
$data['selected_low_sell_item'] = '';
}
$this->load->view('items/form', $data);
}
@@ -397,6 +424,8 @@ class Items extends Secure_Controller
{
$receiving_quantity = '1';
}
$default_pack_name = $this->lang->line('items_default_pack_name');
//Save item data
$item_data = array(
'name' => $this->input->post('name'),
@@ -412,6 +441,9 @@ class Items extends Secure_Controller
'receiving_quantity' => $receiving_quantity,
'allow_alt_description' => $this->input->post('allow_alt_description') != NULL,
'is_serialized' => $this->input->post('is_serialized') != NULL,
'qty_per_pack' => $this->input->post('qty_per_pack') == NULL ? 1 : $this->input->post('qty_per_pack'),
'pack_name' => $this->input->post('pack_name') == NULL ? $default_pack_name : $this->input->post('pack_name'),
'low_sell_item_id' => $this->input->post('low_sell_item_id') == NULL ? -1 : $this->input->post('low_sell_item_id'),
'deleted' => $this->input->post('is_deleted') != NULL,
'custom1' => $this->input->post('custom1') == NULL ? '' : $this->input->post('custom1'),
'custom2' => $this->input->post('custom2') == NULL ? '' : $this->input->post('custom2'),

View File

@@ -124,10 +124,11 @@ class Receivings extends Secure_Controller
$quantity = parse_decimals($this->input->post('quantity'));
$discount = parse_decimals($this->input->post('discount'));
$item_location = $this->input->post('location');
$receiving_quantity = $this->input->post('receiving_quantity');
if($this->form_validation->run() != FALSE)
{
$this->receiving_lib->edit_item($item_id, $description, $serialnumber, $quantity, $discount, $price);
$this->receiving_lib->edit_item($item_id, $description, $serialnumber, $quantity, $discount, $price, $receiving_quantity);
}
else
{

View File

@@ -1420,6 +1420,7 @@ class Reports extends Secure_Controller
'item_name' => $row['name'],
'item_number' => $row['item_number'],
'quantity' => to_quantity_decimals($row['quantity']),
'low_sell_quantity' => to_quantity_decimals($row['low_sell_quantity']),
'reorder_level' => to_quantity_decimals($row['reorder_level']),
'location_name' => $row['location_name'],
'cost_price' => to_currency($row['cost_price']),

View File

@@ -374,6 +374,11 @@ function get_item_data_row($item)
}
}
if ($CI->config->item('multi_pack_enabled') == '1')
{
$item->name .= NAME_SEPARATOR . $item->pack_name;
}
return array (
'items.item_id' => $item->item_id,
'item_number' => $item->item_number,

View File

@@ -181,6 +181,7 @@ $lang["config_msg_src"] = "SMS-API Sender ID";
$lang["config_msg_src_required"] = "SMS-API Sender ID is a required field";
$lang["config_msg_uid"] = "SMS-API Username";
$lang["config_msg_uid_required"] = "SMS-API Username is a required field";
$lang["config_multi_pack_enabled"] = "Multiple Packages per Item";
$lang["config_none"] = "none";
$lang["config_notify_alignment"] = "Notification Popup Position";
$lang["config_number_format"] = "Number Format";

View File

@@ -24,6 +24,7 @@ $lang["items_cost_price_number"] = "Cost Price must be a number";
$lang["items_cost_price_required"] = "Cost Price is a required field";
$lang["items_count"] = "Update Inventory";
$lang["items_current_quantity"] = "Current Quantity";
$lang["items_default_pack_name"] = "Each";
$lang["items_description"] = "Description";
$lang["items_details_count"] = "Inventory count details";
$lang["items_do_nothing"] = "Do nothing";
@@ -55,6 +56,7 @@ $lang["items_item_number_duplicate"] = "Item Number is already present in the da
$lang["items_kit"] = "Kit";
$lang["items_location"] = "Location";
$lang["items_low_inventory_items"] = "Out Of Stock Items";
$lang["items_low_sell_item"] = "Low sell item";
$lang["items_manually_editing_of_quantity"] = "Manual Edit of Quantity";
$lang["items_name"] = "Item Name";
$lang["items_name_required"] = "Item Name is a required field";
@@ -67,6 +69,8 @@ $lang["items_nonstock"] = "Non-stocked";
$lang["items_number_information"] = "Item Number";
$lang["items_number_required"] = "Barcode is a required field";
$lang["items_one_or_multiple"] = "item(s)";
$lang["items_pack_name"] = "Pack Name";
$lang["items_qty_per_pack"] = "Quantity per pack";
$lang["items_quantity"] = "Quantity";
$lang["items_quantity_number"] = "Quantity must be a number";
$lang["items_quantity_required"] = "Quantity is a required field";

View File

@@ -35,6 +35,7 @@ $lang["receivings_register"] = "Items Receiving";
$lang["receivings_requisition"] = "Requisition";
$lang["receivings_return"] = "Return";
$lang["receivings_select_supplier"] = "Select Supplier (Optional)";
$lang["receivings_ship_pack"] = "Ship Pack";
$lang["receivings_start_typing_supplier_name"] = "Start Typing Supplier's name...";
$lang["receivings_stock"] = "Stock";
$lang["receivings_stock_destination"] = "Stock Destination";

View File

@@ -63,6 +63,7 @@ $lang["reports_items_received"] = "Items Received";
$lang["reports_items_summary_report"] = "Items Summary Report";
$lang["reports_low_inventory"] = "";
$lang["reports_low_inventory_report"] = "";
$lang["reports_low_sell_quantity"] = "Low Sell Qty";
$lang["reports_more_than_zero"] = "More than zero";
$lang["reports_name"] = "Name";
$lang["reports_no_reports_to_display"] = "No Items to display";
@@ -114,6 +115,7 @@ $lang["reports_taxes"] = "Taxes";
$lang["reports_taxes_summary_report"] = "Taxes Summary Report";
$lang["reports_total"] = "Total";
$lang["reports_total_inventory_value"] = "Total Inventory Value";
$lang["reports_total_low_sell_quantity"] = "Total Low Sell Quantity";
$lang["reports_total_quantity"] = "Total Quantity";
$lang["reports_total_retail"] = "Total Inv. Retail Value";
$lang["reports_type"] = "Type";

View File

@@ -84,7 +84,7 @@ $lang["sales_must_enter_numeric"] = "Amount Tendered must be a number";
$lang["sales_must_enter_numeric_giftcard"] = "Gift Card Number must be a number";
$lang["sales_new_customer"] = "New Customer";
$lang["sales_new_item"] = "New Item";
$lang["sales_no_description"] = "None";
$lang["sales_no_description"] = "No description";
$lang["sales_no_filter"] = "All";
$lang["sales_no_items_in_cart"] = "There are no Items in the cart";
$lang["sales_no_sales_to_display"] = "No Sales to display";

View File

@@ -181,6 +181,7 @@ $lang["config_msg_src"] = "SMS-API Sender ID";
$lang["config_msg_src_required"] = "SMS-API Sender ID is a required field";
$lang["config_msg_uid"] = "SMS-API Username";
$lang["config_msg_uid_required"] = "SMS-API Username is a required field";
$lang["config_multi_pack_enabled"] = "Multiple Packages per Item";
$lang["config_none"] = "none";
$lang["config_notify_alignment"] = "Notification Popup Position";
$lang["config_number_format"] = "Number Format";

View File

@@ -24,6 +24,7 @@ $lang["items_cost_price_number"] = "Wholesale Price must be a number.";
$lang["items_cost_price_required"] = "Wholesale Price is a required field.";
$lang["items_count"] = "Update Inventory";
$lang["items_current_quantity"] = "Current Quantity";
$lang["items_default_pack_name"] = "Each";
$lang["items_description"] = "Description";
$lang["items_details_count"] = "Inventory Count Details";
$lang["items_do_nothing"] = "Do Nothing";
@@ -55,6 +56,7 @@ $lang["items_item_number_duplicate"] = "Item Number is already present in the da
$lang["items_kit"] = "Kit";
$lang["items_location"] = "Location";
$lang["items_low_inventory_items"] = "Out Of Stock Items";
$lang["items_low_sell_item"] = "Low sell item";
$lang["items_manually_editing_of_quantity"] = "Manual Edit of Quantity";
$lang["items_name"] = "Item Name";
$lang["items_name_required"] = "Item Name is a required field.";
@@ -67,6 +69,8 @@ $lang["items_nonstock"] = "Non-stocked";
$lang["items_number_information"] = "Item Number";
$lang["items_number_required"] = "Barcode is a required field.";
$lang["items_one_or_multiple"] = "item(s)";
$lang["items_pack_name"] = "Pack Name";
$lang["items_qty_per_pack"] = "Quantity per pack";
$lang["items_quantity"] = "Quantity";
$lang["items_quantity_number"] = "Quantity must be a number.";
$lang["items_quantity_required"] = "Quantity is a required field.";

View File

@@ -35,6 +35,7 @@ $lang["receivings_register"] = "Items Receiving";
$lang["receivings_requisition"] = "Requisition";
$lang["receivings_return"] = "Return";
$lang["receivings_select_supplier"] = "Select Supplier (Optional)";
$lang["receivings_ship_pack"] = "Ship Pack";
$lang["receivings_start_typing_supplier_name"] = "Start Typing Supplier's name...";
$lang["receivings_stock"] = "Stock";
$lang["receivings_stock_destination"] = "Stock Destination";

View File

@@ -63,6 +63,7 @@ $lang["reports_items_received"] = "Items Received";
$lang["reports_items_summary_report"] = "Items Summary Report";
$lang["reports_low_inventory"] = "";
$lang["reports_low_inventory_report"] = "";
$lang["reports_low_sell_quantity"] = "Low Sell Qty";
$lang["reports_more_than_zero"] = "More than zero";
$lang["reports_name"] = "Name";
$lang["reports_no_reports_to_display"] = "No Items to display.";
@@ -114,6 +115,7 @@ $lang["reports_taxes"] = "Taxes";
$lang["reports_taxes_summary_report"] = "Taxes Summary Report";
$lang["reports_total"] = "Total";
$lang["reports_total_inventory_value"] = "Total Inventory Value";
$lang["reports_total_low_sell_quantity"] = "Total Low Sell Quantity";
$lang["reports_total_quantity"] = "Total Quantity";
$lang["reports_total_retail"] = "Total Inv. Retail Value";
$lang["reports_type"] = "Type";

View File

@@ -84,7 +84,7 @@ $lang["sales_must_enter_numeric"] = "Amount Tendered must be a number.";
$lang["sales_must_enter_numeric_giftcard"] = "Gift Card Number must be a number.";
$lang["sales_new_customer"] = "New Customer";
$lang["sales_new_item"] = "New Item";
$lang["sales_no_description"] = "None";
$lang["sales_no_description"] = "No description";
$lang["sales_no_filter"] = "All";
$lang["sales_no_items_in_cart"] = "There are no Items in the cart.";
$lang["sales_no_sales_to_display"] = "No Sales to display.";

View File

@@ -209,6 +209,23 @@ 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');
}
$item = array($insertkey => array(
'item_id' => $item_id,
'item_location' => $item_location,
@@ -223,8 +240,9 @@ class Receiving_lib
'discount' => $discount,
'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' => $item_info->receiving_quantity,
'receiving_quantity_choices' => $receiving_quantity_choices,
'total' => $this->get_item_total($quantity, $price, $discount, $item_info->receiving_quantity)
)
);
@@ -232,7 +250,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, $items[$updatekey]['receiving_quantity']);
}
else
{
@@ -245,7 +263,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, $price, $receiving_quantity)
{
$items = $this->get_cart();
if(isset($items[$line]))
@@ -254,9 +272,10 @@ class Receiving_lib
$line['description'] = $description;
$line['serialnumber'] = $serialnumber;
$line['quantity'] = $quantity;
$line['receiving_quantity'] = $receiving_quantity;
$line['discount'] = $discount;
$line['price'] = $price;
$line['total'] = $this->get_item_total($quantity, $price, $discount);
$line['total'] = $this->get_item_total($quantity, $price, $discount, $receiving_quantity);
$this->set_cart($items);
}
@@ -330,9 +349,10 @@ class Receiving_lib
$this->clear_reference();
}
public function get_item_total($quantity, $price, $discount_percentage)
public function get_item_total($quantity, $price, $discount_percentage, $receiving_quantity)
{
$total = bcmul($quantity, $price);
$extended_quantity = bcmul($quantity, $receiving_quantity);
$total = bcmul($extended_quantity, $price);
$discount_fraction = bcdiv($discount_percentage, 100);
$discount_amount = bcmul($total, $discount_fraction);
@@ -344,7 +364,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['receiving_quantity']));
}
return $total;

View File

@@ -827,7 +827,14 @@ 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
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(

View File

@@ -0,0 +1,20 @@
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class Migration_Upgrade_To_3_3_0 extends CI_Migration
{
public function __construct()
{
parent::__construct();
}
public function up()
{
execute_script(APPPATH . 'migrations/sqlscripts/3.2.1_to_3.3.0.sql');
}
public function down()
{
}
}
?>

View File

@@ -0,0 +1,15 @@
--
-- 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,
ADD COLUMN `pack_name` varchar(8) DEFAULT 'Each',
ADD COLUMN `low_sell_item_id` int(10) DEFAULT 0;
UPDATE `ospos_items`
SET `low_sell_item_id` = `item_id`
WHERE `low_sell_item_id` = 0;

View File

@@ -23,6 +23,8 @@ define('PRICE_OPTION_ALL', 0);
define('PRICE_OPTION_KIT', 1);
define('PRICE_OPTION_KIT_STOCK', 2);
define('NAME_SEPARATOR', ' | ');
/**
* Item class
@@ -109,64 +111,65 @@ 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.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('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']);
}
@@ -398,6 +401,11 @@ 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;
@@ -477,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');
@@ -487,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
@@ -528,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
@@ -569,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
@@ -624,7 +672,7 @@ 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
@@ -636,7 +684,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
@@ -678,7 +726,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
@@ -834,6 +882,24 @@ class Item extends CI_Model
return $suggestions;
}
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;
}
public function get_category_suggestions($search)
{
$suggestions = array();
@@ -950,5 +1016,31 @@ class Item extends CI_Model
$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;
}
}
?>

View File

@@ -174,7 +174,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 +188,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
{
@@ -930,12 +930,12 @@ class Sale extends CI_Model
discount_percent,
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 +949,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 +957,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
@@ -1133,7 +1135,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
@@ -1181,7 +1183,7 @@ 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,

View File

@@ -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)

View File

@@ -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('item_name') . ', items.item_number, item_quantities.quantity, 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'];
}

View File

@@ -160,7 +160,7 @@
'name' => $this->lang->line('items_name'),
'item_number' => $this->lang->line('items_number_information'),
'unit_price' => $this->lang->line('items_unit_price')
),
),
$this->config->item('suggestions_third_column'), array('class' => 'form-control input-sm')); ?>
</div>
</div>
@@ -213,6 +213,17 @@
</div>
</div>
<div class="form-group form-group-sm">
<?php echo form_label($this->lang->line('config_multi_pack_enabled'), 'multi_pack_enabled', array('class' => 'control-label col-xs-2')); ?>
<div class='col-xs-1'>
<?php echo form_checkbox(array(
'name' => 'multi_pack_enabled',
'id' => 'multi_pack_enabled',
'value' => 'multi_pack_enabled',
'checked' => $this->config->item('multi_pack_enabled'))); ?>
</div>
</div>
<div class="form-group form-group-sm">
<?php echo form_label($this->lang->line('config_custom1'), 'config_custom1', array('class' => 'control-label col-xs-2')); ?>
<div class='col-xs-2'>

View File

@@ -93,7 +93,8 @@
); ?> <?php echo $this->lang->line('items_kit'); ?>
</label>
<?php
if($this->config->item('derive_sale_quantity') == '1') {
if($this->config->item('derive_sale_quantity') == '1')
{
?>
<label class="radio-inline">
<?php echo form_radio(array(
@@ -329,6 +330,51 @@
</div>
</div>
<?php
if($this->config->item('multi_pack_enabled') == '1')
{
?>
<div class="form-group form-group-sm">
<?php echo form_label($this->lang->line('items_qty_per_pack'), 'qty_per_pack', array('class'=>'control-label col-xs-3')); ?>
<div class='col-xs-4'>
<?php echo form_input(array(
'name'=>'qty_per_pack',
'id'=>'qty_per_pack',
'class'=>'form-control input-sm',
'value'=>isset($item_info->item_id) ? to_quantity_decimals($item_info->qty_per_pack) : to_quantity_decimals(0))
);?>
</div>
</div>
<div class="form-group form-group-sm">
<?php echo form_label($this->lang->line('items_pack_name'), 'name', array('class'=>'control-label col-xs-3')); ?>
<div class='col-xs-8'>
<?php echo form_input(array(
'name'=>'pack_name',
'id'=>'pack_name',
'class'=>'form-control input-sm',
'value'=>$item_info->pack_name)
);?>
</div>
</div>
<div class="form-group form-group-sm">
<?php echo form_label($this->lang->line('items_low_sell_item'), 'low_sell_item_name', array('class'=>'control-label col-xs-3')); ?>
<div class='col-xs-8'>
<div class="input-group input-group-sm">
<?php echo form_input(array(
'name'=>'low_sell_item_name',
'id'=>'low_sell_item_name',
'class'=>'form-control input-sm',
'size'=>'50',
'value'=>$selected_low_sell_item)
); ?>
<?php echo form_hidden('low_sell_item_id', $selected_low_sell_item_id);?>
</div>
</div>
</div>
<?php
}
?>
<div class="form-group form-group-sm">
<?php echo form_label($this->lang->line('items_is_deleted'), 'is_deleted', array('class'=>'control-label col-xs-3')); ?>
<div class='col-xs-1'>
@@ -381,6 +427,22 @@ $(document).ready(function()
stay_open = false;
});
var fill_value = function(event, ui) {
event.preventDefault();
$("input[name='low_sell_item_id']").val(ui.item.value);
$("input[name='low_sell_item_name']").val(ui.item.label);
};
$("#low_sell_item_name").autocomplete({
source: '<?php echo site_url("items/suggest_low_sell"); ?>',
minChars: 0,
delay: 15,
cacheLength: 1,
appendTo: '.modal-content',
select: fill_value,
focus: fill_value
});
var no_op = function(event, data, formatted){};
$('#category').autocomplete({
source: "<?php echo site_url('items/suggest_category'); ?>",

View File

@@ -82,7 +82,7 @@
<td><?php echo to_currency($item['price']); ?></td>
<td><?php echo to_quantity_decimals($item['quantity']) . " " . ($show_stock_locations ? " [" . $item['stock_name'] . "]" : "");
?>&nbsp;&nbsp;&nbsp;x <?php echo $item['receiving_quantity'] != 0 ? to_quantity_decimals($item['receiving_quantity']) : 1; ?></td>
<td><div class="total-value"><?php echo to_currency($item['total']*$item['receiving_quantity']); ?></div></td>
<td><div class="total-value"><?php echo to_currency($item['total']); ?></div></td>
</tr>
<tr>
<td ><?php echo $item['serialnumber']; ?></td>

View File

@@ -103,10 +103,10 @@ if (isset($success))
<thead>
<tr>
<th style="width:5%;"><?php echo $this->lang->line('common_delete'); ?></th>
<th style="width:45%;"><?php echo $this->lang->line('receivings_item_name'); ?></th>
<th style="width:40%;"><?php echo $this->lang->line('receivings_item_name'); ?></th>
<th style="width:10%;"><?php echo $this->lang->line('receivings_cost'); ?></th>
<th style="width:10%;"><?php echo $this->lang->line('receivings_quantity'); ?></th>
<th style="width:5%;"></th>
<th style="width:10%;"><?php echo $this->lang->line('receivings_ship_pack'); ?></th>
<th style="width:10%;"><?php echo $this->lang->line('receivings_discount'); ?></th>
<th style="width:10%;"><?php echo $this->lang->line('receivings_total'); ?></th>
<th style="width:5%;"><?php echo $this->lang->line('receivings_update'); ?></th>
@@ -157,21 +157,8 @@ if (isset($success))
?>
<td><?php echo form_input(array('name'=>'quantity', 'class'=>'form-control input-sm', 'value'=>to_quantity_decimals($item['quantity']))); ?></td>
<?php
if ($item['receiving_quantity'] > 1)
{
?>
<td><?php echo 'x'.to_quantity_decimals($item['receiving_quantity']); ?></td>
<?php
}
else
{
?>
<td></td>
<?php
}
?>
<td><?php echo form_dropdown('receiving_quantity', $item['receiving_quantity_choices'], $item['receiving_quantity'], array('class'=>'form-control input-sm'));?></td>
<?php
if ($items_module_allowed && $mode!='requisition')
{
@@ -214,7 +201,7 @@ if (isset($success))
}
else
{
echo $this->lang->line('sales_no_description');
echo "<i>".$this->lang->line('sales_no_description')."</i>";
echo form_hidden('description','');
}
}
@@ -351,42 +338,41 @@ if (isset($success))
<div class="form-group form-group-sm">
<label id="comment_label" for="comment"><?php echo $this->lang->line('common_comments'); ?></label>
<?php echo form_textarea(array('name'=>'comment', 'id'=>'comment', 'class'=>'form-control input-sm', 'value'=>$comment, 'rows'=>'4'));?>
<table class="sales_table_100" id="payment_details">
<tr>
<td><?php echo $this->lang->line('receivings_print_after_sale'); ?></td>
<td>
<?php echo form_checkbox(array('name'=>'recv_print_after_sale', 'id'=>'recv_print_after_sale', 'class'=>'checkbox', 'value'=>1, 'checked'=>$print_after_sale)); ?>
</td>
</tr>
<?php
if ($mode == "receive")
{
?>
<div id="payment_details" >
<table class="sales_table_100" >
<tr>
<td><?php echo $this->lang->line('receivings_reference');?></td>
<td><?php echo $this->lang->line('receivings_print_after_sale'); ?></td>
<td>
<?php echo form_input(array('name'=>'recv_reference', 'id'=>'recv_reference', 'class'=>'form-control input-sm', 'value'=>$reference, 'size'=>5));?>
<?php echo form_checkbox(array('name'=>'recv_print_after_sale', 'id'=>'recv_print_after_sale', 'class'=>'checkbox', 'value'=>1, 'checked'=>$print_after_sale)); ?>
</td>
</tr>
<?php
}
?>
<tr>
<td><?php echo $this->lang->line('sales_payment'); ?></td>
<td>
<?php echo form_dropdown('payment_type', $payment_options, array(), array('id'=>'payment_types', 'class'=>'selectpicker show-menu-arrow', 'data-style'=>'btn-default btn-sm', 'data-width'=>'auto')); ?>
</td>
</tr>
<tr>
<td><?php echo $this->lang->line('sales_amount_tendered'); ?></td>
<td>
<?php echo form_input(array('name'=>'amount_tendered', 'value'=>'', 'class'=>'form-control input-sm', 'size'=>'5')); ?>
</td>
</tr>
</table>
<?php
if ($mode == "receive")
{
?>
<tr>
<td><?php echo $this->lang->line('receivings_reference');?></td>
<td>
<?php echo form_input(array('name'=>'recv_reference', 'id'=>'recv_reference', 'class'=>'form-control input-sm', 'value'=>$reference, 'size'=>5));?>
</td>
</tr>
<?php
}
?>
<tr>
<td><?php echo $this->lang->line('sales_payment'); ?></td>
<td>
<?php echo form_dropdown('payment_type', $payment_options, array(), array('id'=>'payment_types', 'class'=>'selectpicker show-menu-arrow', 'data-style'=>'btn-default btn-sm', 'data-width'=>'auto')); ?>
</td>
</tr>
<tr>
<td><?php echo $this->lang->line('sales_amount_tendered'); ?></td>
<td>
<?php echo form_input(array('name'=>'amount_tendered', 'value'=>'', 'class'=>'form-control input-sm', 'size'=>'5')); ?>
</td>
</tr>
</table>
</div>
<div class='btn btn-sm btn-danger pull-left' id='cancel_receiving_button'><span class="glyphicon glyphicon-remove">&nbsp</span><?php echo $this->lang->line('receivings_cancel_receiving') ?></div>
@@ -517,7 +503,7 @@ $(document).ready(function()
}
}
$('[name="price"],[name="quantity"],[name="discount"],[name="description"],[name="serialnumber"]').change(function() {
$('[name="price"],[name="quantity"],[name="receiving_quantity"],[name="discount"],[name="description"],[name="serialnumber"]').change(function() {
$(this).parents("tr").prevAll("form:first").submit()
});

View File

@@ -133,6 +133,7 @@ if(isset($success))
foreach(array_reverse($cart, TRUE) as $line=>$item)
{
?>
<?php echo form_open($controller_name."/edit_item/$line", array('class'=>'form-horizontal', 'id'=>'cart_'.$line)); ?>
<tr>
<td>

View File

@@ -118,7 +118,8 @@ INSERT INTO `ospos_app_config` (`key`, `value`) VALUES
('suggestions_second_column', ''),
('suggestions_third_column', ''),
('allow_duplicate_barcodes', '0'),
('quote_default_comments', 'This is a default quote comment');
('quote_default_comments', 'This is a default quote comment'),
('multi_pack_enabled', '0');
-- --------------------------------------------------------
@@ -243,6 +244,9 @@ CREATE TABLE `ospos_items` (
`stock_type` TINYINT(2) NOT NULL DEFAULT 0,
`item_type` TINYINT(2) NOT NULL DEFAULT 0,
`tax_category_id` int(10) NOT NULL DEFAULT 1,
`qty_per_pack` decimal(15,3) NOT NULL DEFAULT 1,
`pack_name` varchar(8) DEFAULT '',
`low_sell_item_id` int(10) DEFAULT 0,
`deleted` int(1) NOT NULL DEFAULT '0',
`custom1` VARCHAR(255) DEFAULT NULL,
`custom2` VARCHAR(255) DEFAULT NULL,