Merge pull request #2431 from opensourcepos/attributes_csv_import_integration

Attributes csv import integration
This commit is contained in:
FrancescoUK
2019-06-04 21:13:54 +01:00
committed by GitHub
8 changed files with 527 additions and 304 deletions

View File

@@ -89,7 +89,7 @@ $autoload['drivers'] = array();
|
| $autoload['helper'] = array('url', 'file');
*/
$autoload['helper'] = array('form', 'url', 'tabular', 'text', 'locale', 'html', 'download', 'directory', 'migration');
$autoload['helper'] = array('form', 'url', 'tabular', 'text', 'locale', 'html', 'download', 'directory', 'migration', 'importfile');
/*
| -------------------------------------------------------------------

View File

@@ -10,13 +10,13 @@ class Items extends Secure_Controller
$this->load->library('item_lib');
}
public function index()
{
$this->session->set_userdata('allow_temp_items', 0);
$data['table_headers'] = $this->xss_clean(get_items_manage_table_headers());
$data['stock_location'] = $this->xss_clean($this->item_lib->get_item_location());
$data['stock_locations'] = $this->xss_clean($this->Stock_location->get_allowed_locations());
@@ -33,8 +33,8 @@ class Items extends Secure_Controller
}
/*
Returns Items table data rows. This will be called with AJAX.
*/
Returns Items table data rows. This will be called with AJAX.
*/
public function search()
{
$search = $this->input->get('search');
@@ -48,17 +48,17 @@ class Items extends Secure_Controller
$definition_names = $this->Attribute->get_definitions_by_flags(Attribute::SHOW_IN_ITEMS);
$filters = array('start_date' => $this->input->get('start_date'),
'end_date' => $this->input->get('end_date'),
'stock_location_id' => $this->item_lib->get_item_location(),
'empty_upc' => FALSE,
'low_inventory' => FALSE,
'is_serialized' => FALSE,
'no_description' => FALSE,
'search_custom' => FALSE,
'is_deleted' => FALSE,
'temporary' => FALSE,
'definition_ids' => array_keys($definition_names));
'end_date' => $this->input->get('end_date'),
'stock_location_id' => $this->item_lib->get_item_location(),
'empty_upc' => FALSE,
'low_inventory' => FALSE,
'is_serialized' => FALSE,
'no_description' => FALSE,
'search_custom' => FALSE,
'is_deleted' => FALSE,
'temporary' => FALSE,
'definition_ids' => array_keys($definition_names));
// check if any filter is set in the multiselect dropdown
$filledup = array_fill_keys($this->input->get('filters'), TRUE);
$filters = array_merge($filters, $filledup);
@@ -79,7 +79,7 @@ class Items extends Secure_Controller
echo json_encode(array('total' => $total_rows, 'rows' => $data_rows));
}
public function pic_thumb($pic_filename)
{
$this->load->helper('file');
@@ -113,8 +113,8 @@ class Items extends Secure_Controller
}
/*
Gives search suggestions based on what is being searched for
*/
Gives search suggestions based on what is being searched for
*/
public function suggest_search()
{
$suggestions = $this->xss_clean($this->Item->get_search_suggestions($this->input->post_get('term'),
@@ -149,8 +149,8 @@ class Items extends Secure_Controller
}
/*
Gives search suggestions based on what is being searched for
*/
Gives search suggestions based on what is being searched for
*/
public function suggest_category()
{
$suggestions = $this->xss_clean($this->Item->get_category_suggestions($this->input->get('term')));
@@ -160,7 +160,7 @@ class Items extends Secure_Controller
/*
Gives search suggestions based on what is being searched for
*/
*/
public function suggest_location()
{
$suggestions = $this->xss_clean($this->Item->get_location_suggestions($this->input->get('term')));
@@ -348,14 +348,14 @@ class Items extends Secure_Controller
{
$location = $this->xss_clean($location);
$quantity = $this->xss_clean($this->Item_quantity->get_item_quantity($item_id, $location['location_id'])->quantity);
$data['stock_locations'][$location['location_id']] = $location['location_name'];
$data['item_quantities'][$location['location_id']] = $quantity;
}
$this->load->view('items/form_inventory', $data);
}
public function count_details($item_id = -1)
{
$item_info = $this->Item->get_info($item_id);
@@ -371,7 +371,7 @@ class Items extends Secure_Controller
{
$location = $this->xss_clean($location);
$quantity = $this->xss_clean($this->Item_quantity->get_item_quantity($item_id, $location['location_id'])->quantity);
$data['stock_locations'][$location['location_id']] = $location['location_name'];
$data['item_quantities'][$location['location_id']] = $quantity;
}
@@ -393,14 +393,14 @@ class Items extends Secure_Controller
foreach($result as &$item)
{
$item = $this->xss_clean($item);
// update the barcode field if empty / NULL with the newly generated barcode
if(empty($item['item_number']) && $this->config->item('barcode_generate_if_empty'))
{
// get the newly generated barcode
$barcode_instance = Barcode_lib::barcode_instance($item, $config);
$item['item_number'] = $barcode_instance->getData();
$save_item = array('item_number' => $item['item_number']);
// update the item in the database in order to save the barcode field
@@ -428,7 +428,7 @@ class Items extends Secure_Controller
$values['attribute_id'] = $attribute_id;
$values['attribute_value'] = $attribute_value;
$values['selected_value'] = '';
if ($definition_value['definition_type'] == DROPDOWN)
{
$values['values'] = $this->Attribute->get_definition_values($definition_id);
@@ -444,7 +444,7 @@ class Items extends Secure_Controller
unset($data['definition_names'][$definition_id]);
}
$this->load->view('attributes/item', $data);
$this->load->view('attributes/item', $data);
}
public function bulk_edit()
@@ -458,12 +458,12 @@ class Items extends Secure_Controller
}
$data['suppliers'] = $suppliers;
$data['allow_alt_description_choices'] = array(
'' => $this->lang->line('items_do_nothing'),
'' => $this->lang->line('items_do_nothing'),
1 => $this->lang->line('items_change_all_to_allow_alt_desc'),
0 => $this->lang->line('items_change_all_to_not_allow_allow_desc'));
$data['serialization_choices'] = array(
'' => $this->lang->line('items_do_nothing'),
'' => $this->lang->line('items_do_nothing'),
1 => $this->lang->line('items_change_all_to_serialized'),
0 => $this->lang->line('items_change_all_to_unserialized'));
@@ -535,7 +535,7 @@ class Items extends Secure_Controller
{
$item_data['tax_category_id'] = $this->input->post('tax_category_id') == '' ? NULL : $this->input->post('tax_category_id');
}
if(!empty($upload_data['orig_name']))
{
// XSS file image sanity check
@@ -544,7 +544,7 @@ class Items extends Secure_Controller
$item_data['pic_filename'] = $upload_data['raw_name'];
}
}
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
if($this->Item->save($item_data, $item_id))
@@ -587,8 +587,8 @@ class Items extends Secure_Controller
$updated_quantity = 0;
}
$location_detail = array('item_id' => $item_id,
'location_id' => $location['location_id'],
'quantity' => $updated_quantity);
'location_id' => $location['location_id'],
'quantity' => $updated_quantity);
$item_quantity = $this->Item_quantity->get_item_quantity($item_id, $location['location_id']);
@@ -639,11 +639,11 @@ class Items extends Secure_Controller
else // failure
{
$message = $this->xss_clean($this->lang->line('items_error_adding_updating') . ' ' . $item_data['name']);
echo json_encode(array('success' => FALSE, 'message' => $message, 'id' => -1));
}
}
public function check_item_number()
{
$exists = $this->Item->item_number_exists($this->input->post('item_number'), $this->input->post('item_id'));
@@ -651,8 +651,8 @@ class Items extends Secure_Controller
}
/*
If adding a new item check to see if an item kit with the same name as the item already exists.
*/
If adding a new item check to see if an item kit with the same name as the item already exists.
*/
public function check_kit_exists()
{
if($this->input->post('item_number') === -1)
@@ -669,7 +669,7 @@ class Items extends Secure_Controller
private function _handle_image_upload()
{
/* Let files be uploaded with their original name */
// load upload library
$config = array('upload_path' => './uploads/item_pics/',
'allowed_types' => 'gif|jpg|png',
@@ -679,7 +679,7 @@ class Items extends Secure_Controller
);
$this->load->library('upload', $config);
$this->upload->do_upload('item_image');
return strlen($this->upload->display_errors()) == 0 || !strcmp($this->upload->display_errors(), '<p>'.$this->lang->line('upload_no_file_selected').'</p>');
}
@@ -692,7 +692,7 @@ class Items extends Secure_Controller
}
public function save_inventory($item_id = -1)
{
{
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
$cur_item_info = $this->Item->get_info($item_id);
$location_id = $this->input->post('stock_location');
@@ -704,9 +704,9 @@ class Items extends Secure_Controller
'trans_comment' => $this->input->post('trans_comment'),
'trans_inventory' => parse_decimals($this->input->post('newquantity'))
);
$this->Inventory->insert($inv_data);
//Update stock quantity
$item_quantity = $this->Item_quantity->get_item_quantity($item_id, $location_id);
$item_quantity_data = array(
@@ -718,13 +718,13 @@ class Items extends Secure_Controller
if($this->Item_quantity->save($item_quantity_data, $item_id, $location_id))
{
$message = $this->xss_clean($this->lang->line('items_successful_updating') . ' ' . $cur_item_info->name);
echo json_encode(array('success' => TRUE, 'message' => $message, 'id' => $item_id));
}
else//failure
{
$message = $this->xss_clean($this->lang->line('items_error_adding_updating') . ' ' . $cur_item_info->name);
echo json_encode(array('success' => FALSE, 'message' => $message, 'id' => -1));
}
}
@@ -735,10 +735,10 @@ class Items extends Secure_Controller
$item_data = array();
foreach($_POST as $key => $value)
{
{
//This field is nullable, so treat it differently
if($key == 'supplier_id' && $value != '')
{
{
$item_data["$key"] = $value;
}
elseif($value != '' && !(in_array($key, array('item_ids', 'tax_names', 'tax_percents'))))
@@ -756,15 +756,15 @@ class Items extends Secure_Controller
$tax_updated = FALSE;
$count = count($tax_percents);
for($k = 0; $k < $count; ++$k)
{
{
if(!empty($tax_names[$k]) && is_numeric($tax_percents[$k]))
{
$tax_updated = TRUE;
$items_taxes_data[] = array('name' => $tax_names[$k], 'percent' => $tax_percents[$k]);
}
}
if($tax_updated)
{
$this->Item_taxes->save_multiple($items_taxes_data, $items_to_update);
@@ -794,183 +794,108 @@ class Items extends Secure_Controller
}
/*
Items import from excel spreadsheet
*/
Items import from excel spreadsheet
*/
public function excel()
{
$name = 'import_items.csv';
$data = file_get_contents('../' . $name);
force_download($name, $data);
$allowed_locations = $this->Stock_location->get_allowed_locations();
$allowed_attributes = $this->Attribute->get_definition_names(FALSE);
$data = generate_import_items_csv($allowed_locations,$allowed_attributes);
force_download($name, $data, TRUE);
}
public function excel_import()
{
$this->load->view('items/form_excel_import', NULL);
}
/**
* Imports items from CSV formatted file.
*/
public function do_excel_import()
{
$non_repeating_element_count = 18;
if($_FILES['file_path']['error'] != UPLOAD_ERR_OK)
{
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('items_excel_import_failed')));
}
else
{
if(($handle = fopen($_FILES['file_path']['tmp_name'], 'r')) !== FALSE)
if(file_exists($_FILES['file_path']['tmp_name']))
{
// Skip the first row as it's the table description
fgetcsv($handle);
$i = 1;
$failCodes = array();
$line_array = get_csv_file($_FILES['file_path']['tmp_name']);
$failCodes = array();
$keys = $line_array[0];
while(($data = fgetcsv($handle)) !== FALSE)
$this->db->trans_begin();
for($i = 1; $i < count($line_array); $i++)
{
// XSS file data sanity check
$data = $this->xss_clean($data);
if(sizeof($data) >= $non_repeating_element_count)
$invalidated = FALSE;
$line = array_combine($keys,$this->xss_clean($line_array[$i])); //Build a XSS-cleaned associative array with the row to use to assign values
if(!empty($line))
{
$item_data = array(
'name' => $data[1],
'description' => $data[11],
'category' => $data[2],
'cost_price' => $data[4],
'unit_price' => $data[5],
'reorder_level' => $data[10],
'supplier_id' => $this->Supplier->exists($data[3]) ? $data[3] : NULL,
'allow_alt_description' => $data[12] != '' ? '1' : '0',
'is_serialized' => $data[13] != '' ? '1' : '0',
'hsn_code' => $data[15]
'name' => $line['Item Name'],
'description' => $line['Description'],
'category' => $line['Category'],
'cost_price' => $line['Cost Price'],
'unit_price' => $line['Unit Price'],
'reorder_level' => $line['Reorder Level'],
'supplier_id' => $this->Supplier->exists($line['Supplier ID']) ? $line['Supplier ID'] : NULL,
'allow_alt_description' => $line['Allow Alt Description'] != '' ? '1' : '0',
'is_serialized' => $line['Item has Serial Number'] != '' ? '1' : '0',
'hsn_code' => $line['HSN'],
'pic_filename' => $line['item_image']
);
/* we could do something like this, however, the effectiveness of
this is rather limited, since for now, you have to upload files manually
into that directory, so you really can do whatever you want, this probably
needs further discussion */
$item_number = $line['Barcode'];
$pic_file = $data[14];
/*if(strcmp('.htaccess', $pic_file)==0)
{
$pic_file='';
}*/
$item_data['pic_filename'] = $pic_file;
$item_number = $data[0];
$invalidated = FALSE;
if($item_number != '')
{
$item_data['item_number'] = $item_number;
$invalidated = $this->Item->item_number_exists($item_number);
}
//Sanity check of data
if(!$invalidated)
{
$invalidated = $this->data_error_check($line, $item_data);
}
}
else
else
{
$invalidated = TRUE;
}
//Save to database
if(!$invalidated && $this->Item->save($item_data))
{
$items_taxes_data = NULL;
//tax 1
if(is_numeric($data[7]) && $data[6] != '')
{
$items_taxes_data[] = array('name' => $data[6], 'percent' => $data[7] );
}
//tax 2
if(is_numeric($data[9]) && $data[8] != '')
{
$items_taxes_data[] = array('name' => $data[8], 'percent' => $data[9] );
}
// save tax values
if(count($items_taxes_data) > 0)
{
$this->Item_taxes->save($items_taxes_data, $item_data['item_id']);
}
// quantities & inventory Info
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
$emp_info = $this->Employee->get_info($employee_id);
$comment ='Qty CSV Imported';
$cols = count($data);
// array to store information if location got a quantity
$allowed_locations = $this->Stock_location->get_allowed_locations();
for($col = 16; $col < $cols; $col = $col + 2)
{
$location_id = $data[$col];
if(array_key_exists($location_id, $allowed_locations))
{
$item_quantity_data = array(
'item_id' => $item_data['item_id'],
'location_id' => $location_id,
'quantity' => $data[$col + 1],
);
$this->Item_quantity->save($item_quantity_data, $item_data['item_id'], $location_id);
$excel_data = array(
'trans_items' => $item_data['item_id'],
'trans_user' => $employee_id,
'trans_comment' => $comment,
'trans_location' => $data[$col],
'trans_inventory' => $data[$col + 1]
);
$this->Inventory->insert($excel_data);
unset($allowed_locations[$location_id]);
}
}
/*
* now iterate through the array and check for which location_id no entry into item_quantities was made yet
* those get an entry with quantity as 0.
* unfortunately a bit duplicate code from above...
*/
foreach($allowed_locations as $location_id => $location_name)
{
$item_quantity_data = array(
'item_id' => $item_data['item_id'],
'location_id' => $location_id,
'quantity' => 0,
);
$this->Item_quantity->save($item_quantity_data, $item_data['item_id'], $data[$col]);
$excel_data = array(
'trans_items' => $item_data['item_id'],
'trans_user' => $employee_id,
'trans_comment' => $comment,
'trans_location' => $location_id,
'trans_inventory' => 0
);
$this->Inventory->insert($excel_data);
}
$this->save_tax_data($line, $item_data);
$this->save_inventory_quantities($line, $item_data);
$this->save_attribute_data($line, $item_data);
}
else //insert or update item failure
{
$failCodes[] = $i;
$failed_row = $i+1;
$failCodes[] = $failed_row;
log_message("ERROR","CSV Item import failed on line ". $failed_row .". This item was not imported.");
}
++$i;
}
if(count($failCodes) > 0)
{
$message = $this->lang->line('items_excel_import_partially_failed') . ' (' . count($failCodes) . '): ' . implode(', ', $failCodes);
$message = $this->lang->line('items_excel_import_partially_failed', count($failCodes), implode(', ', $failCodes));
$this->db->trans_rollback();
echo json_encode(array('success' => FALSE, 'message' => $message));
}
else
{
$this->db->trans_commit();
echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('items_excel_import_success')));
}
}
else
else
{
echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('items_excel_import_nodata_wrongformat')));
}
@@ -978,9 +903,202 @@ class Items extends Secure_Controller
}
/**
* Guess whether file extension is not in the table field,
* if it isn't, then it's an old-format (formerly pic_id) field,
* so we guess the right filename and update the table
* Checks the entire line of data for errors
*
* @param array $line
* @param array $item_data
*
* @return bool Returns FALSE if all data checks out and TRUE when there is an error in the data
*/
private function data_error_check($line, $item_data)
{
//Check for empty required fields
$check_for_empty = array(
$item_data['name'],
$item_data['category'],
$item_data['cost_price'],
$item_data['unit_price']
);
if(in_array('',$check_for_empty,true))
{
log_message("ERROR","Empty required value");
return TRUE; //Return fail on empty required fields
}
//Build array of fields to check for numerics
$check_for_numeric_values = array(
$item_data['cost_price'],
$item_data['unit_price'],
$item_data['reorder_level'],
$item_data['supplier_id'],
$line['Tax 1 Percent'],
$line['Tax 2 Percent']
);
//Add in Stock Location values to check for numeric
$allowed_locations = $this->Stock_location->get_allowed_locations();
foreach($allowed_locations as $location_id => $location_name)
{
$check_for_numeric_values[] = $line['location_'. $location_name];
}
//Check for non-numeric values which require numeric
foreach($check_for_numeric_values as $value)
{
if(!is_numeric($value) && $value != '')
{
log_message("ERROR","non-numeric: '$value' when numeric is required");
return TRUE;
}
}
//Check Attribute Data
$definition_names = $this->Attribute->get_definition_names();
foreach($definition_names as $definition_name)
{
if(!empty($line['attribute_' . $definition_name]))
{
$attribute_data = $this->Attribute->get_definition_by_name($definition_name)[0];
$attribute_type = $attribute_data['definition_type'];
$attribute_value = $line['attribute_' . $definition_name];
if($attribute_type == 'DROPDOWN')
{
$dropdown_values = $this->Attribute->get_definition_values($attribute_data['definition_id']);
$dropdown_values[] = '';
if(in_array($attribute_value, $dropdown_values) === FALSE)
{
log_message("ERROR","Value: '$attribute_value' is not an acceptable DROPDOWN value");
return TRUE;
}
}
else if($attribute_type == 'DECIMAL')
{
if(!is_numeric($attribute_value) && $attribute_value != '')
{
log_message("ERROR","Decimal required: '$attribute_value' is not an acceptable DECIMAL value");
return TRUE;
}
}
else if($attribute_type == 'DATETIME')
{
if(strtotime($attribute_value) === FALSE)
{
log_message("ERROR","Datetime required: '$attribute_value' is not an acceptable DATETIME value");
return TRUE;
}
}
}
}
return FALSE;
}
/**
* @param line
* @param failCodes
* @param attribute_data
*/
private function save_attribute_data($line, $item_data)
{
$definition_names = $this->Attribute->get_definition_names();
foreach($definition_names as $definition_name)
{
if(!empty($line['attribute_' . $definition_name]))
{
//Create attribute value
$attribute_data = $this->Attribute->get_definition_by_name($definition_name)[0];
$status = $this->Attribute->save_value($line['attribute_' . $definition_name], $attribute_data['definition_id'], $item_data['item_id'], FALSE, $attribute_data['definition_type']);
if($status === FALSE)
{
return FALSE;
}
}
}
}
/**
* Saves inventory quantities for the row in the appropriate stock locations.
*
* @param array line
* @param item_data
*/
private function save_inventory_quantities($line, $item_data)
{
//Quantities & Inventory Section
$employee_id = $this->Employee->get_logged_in_employee_info()->person_id;
$emp_info = $this->Employee->get_info($employee_id);
$comment = $this->lang->line('items_inventory_CSV_import_quantity');
$allowed_locations = $this->Stock_location->get_allowed_locations();
foreach($allowed_locations as $location_id => $location_name)
{
$item_quantity_data = array(
'item_id' => $item_data['item_id'],
'location_id' => $location_id
);
$excel_data = array(
'trans_items' => $item_data['item_id'],
'trans_user' => $employee_id,
'trans_comment' => $comment,
'trans_location' => $location_id,
);
if(!empty($line['location_' . $location_name]))
{
$item_quantity_data['quantity'] = $line['location_' . $location_name];
$this->Item_quantity->save($item_quantity_data, $item_data['item_id'], $location_id);
$excel_data['trans_inventory'] = $line['location_' . $location_name];
$this->Inventory->insert($excel_data);
}
else
{
$item_quantity_data['quantity'] = 0;
$this->Item_quantity->save($item_quantity_data, $item_data['item_id'], $line[$col]);
$excel_data['trans_inventory'] = 0;
$this->Inventory->insert($excel_data);
}
}
}
/**
* Saves the tax data found in the line of the CSV items import file
*
* @param array line
*/
private function save_tax_data($line, $item_data)
{
$items_taxes_data = array();
if(is_numeric($line['Tax 1 Percent']) && $line['Tax 1 Name'] != '')
{
$items_taxes_data[] = array('name' => $line['Tax 1 Name'], 'percent' => $line['Tax 1 Percent'] );
}
if(is_numeric($line['Tax 2 Percent']) && $line['Tax 2 Name'] != '')
{
$items_taxes_data[] = array('name' => $line['Tax 2 Name'], 'percent' => $line['Tax 2 Percent'] );
}
if(count($items_taxes_data) > 0)
{
$this->Item_taxes->save($items_taxes_data, $item_data['item_id']);
}
}
/**
* Guess whether file extension is not in the table field, if it isn't, then it's an old-format (formerly pic_id) field, so we guess the right filename and update the table
*
* @param $item the item to update
*/
private function _update_pic_filename($item)

View File

@@ -0,0 +1,80 @@
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
* Generates the header content for the import_items.csv file
*
* @return string Comma separated headers for the CSV file
*/
function generate_import_items_csv($stock_locations,$attributes)
{
$csv_headers = pack("CCC",0xef,0xbb,0xbf); //Encode the Byte-Order Mark (BOM) so that UTF-8 File headers display properly in Microsoft Excel
$csv_headers .= 'Barcode,"Item Name",Category,"Supplier ID","Cost Price","Unit Price","Tax 1 Name","Tax 1 Percent","Tax 2 Name","Tax 2 Percent","Reorder Level",Description,"Allow Alt Description","Item has Serial Number",item_image,HSN';
$csv_headers .= generate_stock_location_headers($stock_locations);
$csv_headers .= generate_attribute_headers($attributes);
return $csv_headers;
}
/**
* Generates a list of stock location names as a string
*
* @return string Comma-separated list of stock location names
*/
function generate_stock_location_headers($locations)
{
$location_headers = "";
foreach($locations as $location_id => $location_name)
{
$location_headers .= ',"location_' . $location_name . '"';
}
return $location_headers;
}
/**
* Generates a list of attribute names as a string
*
* @return string Comma-separated list of attribute names
*/
function generate_attribute_headers($attribute_names)
{
$attribute_headers = "";
unset($attribute_names[-1]);
foreach($attribute_names as $attribute_name)
{
$attribute_headers .= ',"attribute_' . $attribute_name . '"';
}
return $attribute_headers;
}
/**
* Read the contents of a given CSV formatted file into a two-dimensional array
*
* @param string $file_name Name of the file to read.
* @return boolean|array[][] two-dimensional array with the file contents or FALSE on failure.
*/
function get_csv_file($file_name)
{
ini_set("auto_detect_line_endings", true);
if(($csv_file = fopen($file_name,'r')) !== FALSE)
{
//Skip Byte-Order Mark
fseek($csv_file, 3);
while (($data = fgetcsv($csv_file)) !== FALSE)
{
$line_array[] = $data;
}
}
else
{
return FALSE;
}
return $line_array;
}
?>

View File

@@ -34,7 +34,7 @@ $lang["items_error_adding_updating"] = "Error adding/updating item";
$lang["items_error_updating_multiple"] = "Error updating items";
$lang["items_excel_import_failed"] = "The excel import failed";
$lang["items_excel_import_nodata_wrongformat"] = "The uploaded file has no data or is formatted incorrectly";
$lang["items_excel_import_partially_failed"] = "Item import successful with some failures:";
$lang["items_excel_import_partially_failed"] = "There were %1 item import failure(s) on line(s): %2. No rows were imported";
$lang["items_excel_import_success"] = "Item import successful";
$lang["items_generate_barcodes"] = "Generate Barcodes";
$lang["items_hsn_code"] = "Harmonised System Nomenclature";
@@ -43,6 +43,7 @@ $lang["items_import_items_excel"] = "Item Import from Excel";
$lang["items_info_provided_by"] = "Information provided by";
$lang["items_inventory"] = "Inventory";
$lang["items_inventory_comments"] = "Comments";
$lang["items_inventory_CSV_import_quantity"] = "Quantity Imported from CSV";
$lang["items_inventory_data_tracking"] = "Inventory Data Tracking";
$lang["items_inventory_date"] = "Date";
$lang["items_inventory_employee"] = "Employee";

View File

@@ -1,4 +1,4 @@
<?php
<?php
$lang["items_add_minus"] = "Inventory to add or subtract.";
$lang["items_allow_alt_description"] = "Allow Alternate Description";
@@ -34,7 +34,7 @@ $lang["items_error_adding_updating"] = "Error adding/updating item";
$lang["items_error_updating_multiple"] = "Error updating items";
$lang["items_excel_import_failed"] = "Excel import failed";
$lang["items_excel_import_nodata_wrongformat"] = "The uploaded file has no data or is formatted incorrectly.";
$lang["items_excel_import_partially_failed"] = "Item import successful with some failures:";
$lang["items_excel_import_partially_failed"] = "There were %1 item import failure(s) on line(s): %2. No rows were imported.";
$lang["items_excel_import_success"] = "Item import successful.";
$lang["items_generate_barcodes"] = "Generate Barcodes";
$lang["items_hsn_code"] = "Harmonized System Nomenclature";
@@ -43,6 +43,7 @@ $lang["items_import_items_excel"] = "Item Import from Excel";
$lang["items_info_provided_by"] = "Information provided by";
$lang["items_inventory"] = "Inventory";
$lang["items_inventory_comments"] = "Comments";
$lang["items_inventory_CSV_import_quantity"] = "Quantity Imported from CSV";
$lang["items_inventory_data_tracking"] = "Inventory Data Tracking";
$lang["items_inventory_date"] = "Date";
$lang["items_inventory_employee"] = "Employee";

View File

@@ -17,14 +17,14 @@ class Attribute extends CI_Model
const SHOW_IN_ITEMS = 1;
const SHOW_IN_SALES = 2;
const SHOW_IN_RECEIVINGS = 4;
public static function get_definition_flags()
{
$class = new ReflectionClass(__CLASS__);
return array_flip($class->getConstants());
}
/*
Determines if a given definition_id is an attribute
*/
@@ -33,10 +33,10 @@ class Attribute extends CI_Model
$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');
@@ -50,13 +50,13 @@ class Attribute extends CI_Model
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
*/
@@ -65,10 +65,10 @@ class Attribute extends CI_Model
$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
*/
@@ -78,9 +78,9 @@ class Attribute extends CI_Model
$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();
@@ -89,17 +89,17 @@ class Attribute extends CI_Model
{
//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
*/
@@ -108,22 +108,22 @@ class Attribute extends CI_Model
$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');
@@ -132,51 +132,51 @@ class Attribute extends CI_Model
$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');
@@ -185,47 +185,60 @@ class Attribute extends CI_Model
$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()
/**
* Returns an array of attribute definition names and IDs
*
* @param boolean $groups If FALSE does not return GROUP type attributes in the array
* @return array Array containing definition IDs, attribute names and -1 index with the local language '[SELECT]' line.
*/
public function get_definition_names($groups = TRUE)
{
$this->db->from('attribute_definitions');
$this->db->where('deleted', 0);
if($groups === FALSE)
{
$this->db->where_not_in('definition_type',GROUP);
}
$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
*/
@@ -233,10 +246,10 @@ class Attribute extends CI_Model
{
$this->db->from('attribute_definitions');
$this->db->where('deleted', 0);
return $this->db->count_all_results();
}
/*
Get number of rows
*/
@@ -244,11 +257,11 @@ class Attribute extends CI_Model
{
return $this->search($search)->num_rows();
}
private function check_data_validity($definition, $from, $to)
{
$success = FALSE;
if($from === TEXT)
{
$this->db->select('item_id,attribute_value');
@@ -256,7 +269,7 @@ class Attribute extends CI_Model
$this->db->join('attribute_links', 'attribute_values.attribute_id = attribute_links.attribute_id');
$this->db->where('definition_id',$definition);
$success = TRUE;
if($to === DATETIME)
{
foreach($this->db->get()->result_array() as $row)
@@ -282,22 +295,22 @@ class Attribute extends CI_Model
}
return $success;
}
private function convert_definition_type($definition_id, $from_type, $to_type)
{
$success = FALSE;
//From TEXT to DATETIME
if($from_type === TEXT)
{
if($to_type === DATETIME || $to_type === DECIMAL)
{
$field = ($to_type === DATETIME ? 'attribute_datetime' : 'attribute_decimal');
if($this->check_data_validity($definition_id, $from_type, $to_type))
{
$this->db->trans_start();
$query = 'UPDATE ospos_attribute_values ';
$query .= 'INNER JOIN ospos_attribute_links ';
$query .= 'ON ospos_attribute_values.attribute_id = ospos_attribute_links.attribute_id ';
@@ -305,7 +318,7 @@ class Attribute extends CI_Model
$query .= 'attribute_value = NULL ';
$query .= 'WHERE definition_id = ' . $this->db->escape($definition_id);
$success = $this->db->query($query);
$this->db->trans_complete();
}
}
@@ -314,30 +327,30 @@ class Attribute extends CI_Model
$success = TRUE;
}
}
//From DROPDOWN to TEXT
else if($from_type === DROPDOWN)
{
//From DROPDOWN to TEXT
$this->db->trans_start();
$this->db->from('ospos_attribute_links');
$this->db->where('definition_id',$definition_id);
$this->db->where('item_id',NULL);
$success = $this->db->delete();
$this->db->trans_complete();
}
//Any other allowed conversion does not get checked here
else
{
$success = TRUE;
}
return $success;
}
/*
Inserts or updates a definition
*/
@@ -345,23 +358,26 @@ class Attribute extends CI_Model
{
//Run these queries as a transaction, we want to make sure we do all or nothing
$this->db->trans_start();
//Definition doesn't exist
if($definition_id === -1 || !$this->exists($definition_id))
{
$success = $this->db->insert('attribute_definitions', $definition_data);
$definition_data['definition_id'] = $this->db->insert_id();
}
//Definition already exists
else
{
$this->db->select('definition_type');
$this->db->select('definition_type, definition_name');
$this->db->from('attribute_definitions');
$this->db->where('definition_id',$definition_id);
$from_definition_type = $this->db->get()->row()->definition_type;
$row = $this->db->get()->row();
$from_definition_type = $row->definition_type;
$from_definition_name = $row->definition_name;
$to_definition_type = $definition_data['definition_type'];
if($from_definition_type !== $to_definition_type)
{
if(!$this->convert_definition_type($definition_id,$from_definition_type,$to_definition_type))
@@ -369,32 +385,35 @@ class Attribute extends CI_Model
return FALSE;
}
}
$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)
public function get_definition_by_name($definition_name, $definition_type = FALSE)
{
$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();
if($definition_type != FALSE)
{
$this->db->where('definition_type', $definition_type);
}
return $this->db->get()->result_array();
}
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);
@@ -407,30 +426,30 @@ class Attribute extends CI_Model
{
$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');
@@ -439,7 +458,7 @@ class Attribute extends CI_Model
$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);
@@ -451,26 +470,26 @@ class Attribute extends CI_Model
}
$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');
@@ -479,10 +498,10 @@ class Attribute extends CI_Model
$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(
@@ -492,7 +511,7 @@ class Attribute extends CI_Model
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();
@@ -510,14 +529,14 @@ class Attribute extends CI_Model
$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 == TEXT || $definition_type == DROPDOWN)
@@ -543,7 +562,7 @@ class Attribute extends CI_Model
$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,
@@ -554,29 +573,35 @@ class Attribute extends CI_Model
$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));
}
/**
* Deletes an Attribute definition from the database and associated column in the items_import.csv
*
* @param unknown $definition_id Attribute definition ID to remove.
* @return boolean TRUE if successful and FALSE if there is a failure
*/
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));
}
}

View File

@@ -109,27 +109,27 @@ class Stock_location extends CI_Model
{
$this->db->trans_start();
$this->db->insert('stock_locations', $location_data_to_save);
$location_id = $this->db->insert_id();
$this->db->insert('stock_locations', $location_data_to_save);
$location_id = $this->db->insert_id();
$this->_insert_new_permission('items', $location_id, $location_name);
$this->_insert_new_permission('sales', $location_id, $location_name);
$this->_insert_new_permission('receivings', $location_id, $location_name);
$this->_insert_new_permission('items', $location_id, $location_name);
$this->_insert_new_permission('sales', $location_id, $location_name);
$this->_insert_new_permission('receivings', $location_id, $location_name);
// insert quantities for existing items
$items = $this->Item->get_all();
foreach($items->result_array() as $item)
{
$quantity_data = array('item_id' => $item['item_id'], 'location_id' => $location_id, 'quantity' => 0);
$this->db->insert('item_quantities', $quantity_data);
}
// insert quantities for existing items
$items = $this->Item->get_all();
foreach($items->result_array() as $item)
{
$quantity_data = array('item_id' => $item['item_id'], 'location_id' => $location_id, 'quantity' => 0);
$this->db->insert('item_quantities', $quantity_data);
}
$this->db->trans_complete();
$this->db->trans_complete();
return $this->db->trans_status();
}
}
$original_location_name = $this->get_location_name($location_id);
$original_location_name = $this->get_location_name($location_id);
if($original_location_name != $location_name)
{
@@ -167,7 +167,7 @@ class Stock_location extends CI_Model
/*
Deletes one item
*/
*/
public function delete($location_id)
{
$this->db->trans_start();

View File

@@ -1,2 +0,0 @@
Barcode,Item Name,Category,Supplier ID,Cost Price,Unit Price,Tax 1 Name,Tax 1 Percent,Tax 2 Name ,Tax 2 Percent,Reorder Level,Description,Allow Alt Description,Item has Serial Number,item_image,HSN,location_id,quantity
33333334,Apple iMac,Computers,,800,1200,Tax 1,8,Tax 2,10,1,Best Computer ever,y,,item.jpg,,1,100
1 Barcode Item Name Category Supplier ID Cost Price Unit Price Tax 1 Name Tax 1 Percent Tax 2 Name Tax 2 Percent Reorder Level Description Allow Alt Description Item has Serial Number item_image HSN location_id quantity
2 33333334 Apple iMac Computers 800 1200 Tax 1 8 Tax 2 10 1 Best Computer ever y item.jpg 1 100