From eb63f49db72e78e64187776ebd6d35a280b216f0 Mon Sep 17 00:00:00 2001 From: jekkos Date: Thu, 24 Dec 2015 12:40:02 +0100 Subject: [PATCH] Add barcode generation for EAN8 and EAN13 (#111, #246) Add config option to use generation when generating barcodes for an Generate propery barcode with configured format if item_number empty --- application/controllers/config.php | 1 + application/controllers/items.php | 17 ++- application/language/en/config_lang.php | 1 + application/language/es/config_lang.php | 1 + application/language/fr/config_lang.php | 1 + application/language/id/config_lang.php | 1 + application/language/nl-BE/config_lang.php | 1 + application/language/ru/config_lang.php | 1 + application/language/th/config_lang.php | 1 + application/language/tr/config_lang.php | 1 + application/language/zh/config_lang.php | 1 + application/libraries/Barcode_lib.php | 118 +++++++++---------- application/views/barcode_sheet.php | 4 +- application/views/barcodes/BarcodeBase.php | 12 ++ application/views/barcodes/Ean13.php | 26 ++++ application/views/barcodes/Ean8.php | 66 ++++++++++- application/views/configs/barcode_config.php | 5 + database/2.3.2_to_2.3.3.sql | 3 +- database/database.sql | 1 + translations/config_lang.csv | 1 + 20 files changed, 188 insertions(+), 75 deletions(-) diff --git a/application/controllers/config.php b/application/controllers/config.php index b0a447a47..a4dd320cd 100644 --- a/application/controllers/config.php +++ b/application/controllers/config.php @@ -145,6 +145,7 @@ class Config extends Secure_area 'barcode_num_in_row'=>$this->input->post('barcode_num_in_row'), 'barcode_page_width'=>$this->input->post('barcode_page_width'), 'barcode_page_cellspacing'=>$this->input->post('barcode_page_cellspacing'), + 'barcode_generate_if_empty'=>$this->input->post('barcode_generate_if_empty'), 'barcode_content'=>$this->input->post('barcode_content') ); diff --git a/application/controllers/items.php b/application/controllers/items.php index 98424e4c3..4113db5c7 100644 --- a/application/controllers/items.php +++ b/application/controllers/items.php @@ -332,21 +332,17 @@ class Items extends Secure_area implements iData_controller $result = $this->Item->get_multiple_info($item_ids)->result_array(); $config = $this->barcode_lib->get_barcode_config(); - $data['items'] = $result; $data['barcode_config'] = $config; - // display barcodes - $this->load->view("barcode_sheet", $data); - // check the list of items to see if any item_number field is empty - foreach($result as $item) + foreach($result as &$item) { // update the UPC/EAN/ISBN field if empty / null with the newly generated barcode - if ($item['item_number'] == '' || $item['item_number'] == null) + if (empty($item['item_number']) && $this->Appconfig->get('barcode_generate_if_empty')) { // get the newly generated barcode - $item['item_number'] = $this->barcode_lib->get_new_barcode($item, $config); - + $barcode_instance = Barcode_lib::barcode_instance($item, $config); + $item['item_number'] = $barcode_instance->getData(); // remove from item any suppliers table info to avoid save failure because of unknown fields // WARNING: if suppliers table is changed this list needs to be upgraded, which makes the matter a bit tricky to maintain unset($item['person_id']); @@ -358,6 +354,10 @@ class Items extends Secure_area implements iData_controller $this->Item->save($item, $item['item_id']); } } + $data['items'] = $result; + // display barcodes + $this->load->view("barcode_sheet", $data); + } function bulk_edit() @@ -497,7 +497,6 @@ class Items extends Secure_area implements iData_controller function check_item_number() { $exists = $this->Item->item_number_exists($this->input->post('item_number'),$this->input->post('item_id')); - echo json_encode(array('success'=>!$exists,'message'=>$this->lang->line('items_item_number_duplicate'))); } diff --git a/application/language/en/config_lang.php b/application/language/en/config_lang.php index 63b5c8238..2ea009519 100644 --- a/application/language/en/config_lang.php +++ b/application/language/en/config_lang.php @@ -24,6 +24,7 @@ $lang["config_barcode_second_row"] = "Row 2"; $lang["config_barcode_third_row"] = "Row 3"; $lang["config_barcode_type"] = "Barcode Type"; $lang["config_barcode_width"] = "Width (px)"; +$lang["config_barcode_generate_if_empty"] = "Generate if empty"; $lang["config_company"] = "Company Name"; $lang["config_company_logo"] = "Company Logo"; $lang["config_company_required"] = "Company name is a required field"; diff --git a/application/language/es/config_lang.php b/application/language/es/config_lang.php index 61a1a468c..2e892f6bc 100644 --- a/application/language/es/config_lang.php +++ b/application/language/es/config_lang.php @@ -24,6 +24,7 @@ $lang["config_barcode_second_row"] = "Fila 1"; $lang["config_barcode_third_row"] = "Fila 2"; $lang["config_barcode_type"] = "Tipo de Código de Barra"; $lang["config_barcode_width"] = "Ancho (px)"; +$lang["config_barcode_generate_if_empty"] = "Generate if empty"; $lang["config_company"] = "Nombre del Comercio"; $lang["config_company_logo"] = "Logotipo del Comercio"; $lang["config_company_required"] = "Nombre del Comercio es requerido"; diff --git a/application/language/fr/config_lang.php b/application/language/fr/config_lang.php index f8c9549f6..d62a1ad3a 100644 --- a/application/language/fr/config_lang.php +++ b/application/language/fr/config_lang.php @@ -24,6 +24,7 @@ $lang["config_barcode_second_row"] = "Row 3"; $lang["config_barcode_third_row"] = "Row 4"; $lang["config_barcode_type"] = "Barcode Type"; $lang["config_barcode_width"] = "Width (px)"; +$lang["config_barcode_generate_if_empty"] = "Generate if empty"; $lang["config_company"] = "Nome de l\'Entreprise"; $lang["config_company_logo"] = "Company Logo"; $lang["config_company_required"] = "Le nom d\'entreprise est requis"; diff --git a/application/language/id/config_lang.php b/application/language/id/config_lang.php index 7809a905a..4cccf147d 100644 --- a/application/language/id/config_lang.php +++ b/application/language/id/config_lang.php @@ -24,6 +24,7 @@ $lang["config_barcode_second_row"] = "Row 8"; $lang["config_barcode_third_row"] = "Row 9"; $lang["config_barcode_type"] = "Barcode Type"; $lang["config_barcode_width"] = "Width (px)"; +$lang["config_barcode_generate_if_empty"] = "Generate if empty"; $lang["config_company"] = "Nama Perusahaan"; $lang["config_company_logo"] = "Company Logo"; $lang["config_company_required"] = "Nama Perusahaan wajib diisi"; diff --git a/application/language/nl-BE/config_lang.php b/application/language/nl-BE/config_lang.php index 8ea68a158..ea7601940 100755 --- a/application/language/nl-BE/config_lang.php +++ b/application/language/nl-BE/config_lang.php @@ -24,6 +24,7 @@ $lang["config_barcode_second_row"] = "Rij 2"; $lang["config_barcode_third_row"] = "Rij 3"; $lang["config_barcode_type"] = "Barcode Type"; $lang["config_barcode_width"] = "Breedte (px)"; +$lang["config_barcode_generate_if_empty"] = "Genereer indien leeg"; $lang["config_company"] = "Bedrijfsnaam"; $lang["config_company_logo"] = "Logo"; $lang["config_company_required"] = "De bedrijfsnaam moet ingevuld worden"; diff --git a/application/language/ru/config_lang.php b/application/language/ru/config_lang.php index a864ecddb..fcb9c2ef2 100644 --- a/application/language/ru/config_lang.php +++ b/application/language/ru/config_lang.php @@ -24,6 +24,7 @@ $lang["config_barcode_second_row"] = "Row 5"; $lang["config_barcode_third_row"] = "Row 6"; $lang["config_barcode_type"] = "Barcode Type"; $lang["config_barcode_width"] = "Width (px)"; +$lang["config_barcode_generate_if_empty"] = "Generate if empty"; $lang["config_company"] = "Название Компании"; $lang["config_company_logo"] = "Company Logo"; $lang["config_company_required"] = "Имя Компании обязательный пробел"; diff --git a/application/language/th/config_lang.php b/application/language/th/config_lang.php index aacfc5e57..e858e3e27 100644 --- a/application/language/th/config_lang.php +++ b/application/language/th/config_lang.php @@ -24,6 +24,7 @@ $lang["config_barcode_second_row"] = "แถว 2"; $lang["config_barcode_third_row"] = "แถว 3"; $lang["config_barcode_type"] = "ประเภทบาร์โค้ด"; $lang["config_barcode_width"] = "กว้าง (px)"; +$lang["config_barcode_generate_if_empty"] = "Generate if empty"; $lang["config_company"] = "ชื่อร้านค้า"; $lang["config_company_logo"] = "ภาพโลโก้"; $lang["config_company_required"] = "ชื่อร้านค้าต้องกรอก"; diff --git a/application/language/tr/config_lang.php b/application/language/tr/config_lang.php index 5461becb2..dcd1ce2ba 100644 --- a/application/language/tr/config_lang.php +++ b/application/language/tr/config_lang.php @@ -24,6 +24,7 @@ $lang["config_barcode_second_row"] = "Row 7"; $lang["config_barcode_third_row"] = "Row 8"; $lang["config_barcode_type"] = "Barcode Type"; $lang["config_barcode_width"] = "Width (px)"; +$lang["config_barcode_generate_if_empty"] = "Generate if empty"; $lang["config_company"] = "Şirket Adı"; $lang["config_company_logo"] = "Company Logo"; $lang["config_company_required"] = "Şirket Adı zorunlu alandır"; diff --git a/application/language/zh/config_lang.php b/application/language/zh/config_lang.php index bf69c26b5..08fa0d31f 100755 --- a/application/language/zh/config_lang.php +++ b/application/language/zh/config_lang.php @@ -24,6 +24,7 @@ $lang["config_barcode_second_row"] = "Row 4"; $lang["config_barcode_third_row"] = "Row 5"; $lang["config_barcode_type"] = "Barcode Type"; $lang["config_barcode_width"] = "Width (px)"; +$lang["config_barcode_generate_if_empty"] = "Generate if empty"; $lang["config_company"] = "公司名稱"; $lang["config_company_logo"] = "Company Logo"; $lang["config_company_required"] = "公司名稱為必填"; diff --git a/application/libraries/Barcode_lib.php b/application/libraries/Barcode_lib.php index e0e5dcacc..8f60e29f9 100644 --- a/application/libraries/Barcode_lib.php +++ b/application/libraries/Barcode_lib.php @@ -38,11 +38,35 @@ class Barcode_lib $data['barcode_num_in_row'] = $this->CI->Appconfig->get('barcode_num_in_row'); $data['barcode_page_width'] = $this->CI->Appconfig->get('barcode_page_width'); $data['barcode_page_cellspacing'] = $this->CI->Appconfig->get('barcode_page_cellspacing'); + $data['barcode_generate_if_empty'] = $this->CI->Appconfig->get('barcode_generate_if_empty'); return $data; } - - private function get_barcode_instance($barcode_type='Code128') + + public function validate_barcode($barcode) + { + $barcode_type = $this->CI->Appconfig->get('barcode_type'); + $barcode_instance = $this->get_barcode_instance($barcode_type); + return $barcode_instance->validate($barcode); + } + + public static function barcode_instance($item, $barcode_config) + { + $barcode_instance = Barcode_lib::get_barcode_instance($barcode_config['barcode_type']); + $is_valid = empty($item['item_number']) && $barcode_config['barcode_generate_if_empty'] || $barcode_instance->validate($item['item_number']); + + // if barcode validation does not succeed, + if (!$is_valid) + { + $barcode_instance = Barcode_lib::get_barcode_instance(); + } + $seed = Barcode_lib::barcode_seed($item, $barcode_instance, $barcode_config); + $barcode_instance->setData($seed); + + return $barcode_instance; + } + + private static function get_barcode_instance($barcode_type='Code128') { switch($barcode_type) { @@ -64,36 +88,41 @@ class Barcode_lib break; } } - + + private static function barcode_seed($item, $barcode_instance, $barcode_config) + { + $seed = $barcode_config['barcode_content'] !== "id" && isset($item['item_number']) ? $item['item_number'] : $item['item_id']; + + if( $barcode_config['barcode_content'] !== "id" && !empty($item['item_number'])) + { + $seed = $item['item_number']; + } + else + { + if ($barcode_config['barcode_generate_if_empty']) + { + // generate barcode with the correct instance + $seed = $barcode_instance->generate($seed); + } + else + { + $seed = $item['item_id']; + } + } + return $seed; + } + private function generate_barcode($item, $barcode_config) { try { - $barcode = $this->get_barcode_instance($barcode_config['barcode_type']); - $is_valid = $barcode->validate($item['item_number']); + $barcode_instance = Barcode_lib::barcode_instance($item, $barcode_config); + $barcode_instance->setQuality($barcode_config['barcode_quality']); + $barcode_instance->setDimensions($barcode_config['barcode_width'], $barcode_config['barcode_height']); - // if barcdoe validation does not succeed, then fallback to the default, which should not require specific formatting - if (!$is_valid) - { - $barcode = $this->get_barcode_instance(); - } - - $barcode_content = $barcode_config['barcode_content'] !== "id" && isset($item['item_number']) ? $item['item_number'] : $item['item_id']; - $barcode->setData($barcode_content); - if( $barcode_config['barcode_content'] !== "id" && isset($item['item_number'])) - { - $barcode->setData($item['item_number']); - } - else - { - $barcode->setData($item['item_id']); - } - $barcode->setQuality($barcode_config['barcode_quality']); - $barcode->setDimensions($barcode_config['barcode_width'], $barcode_config['barcode_height']); - - $barcode->draw(); + $barcode_instance->draw(); - return $barcode->base64(); + return $barcode_instance->base64(); } catch(Exception $e) { @@ -128,42 +157,7 @@ class Barcode_lib } } - public function get_new_barcode($item, $barcode_config) - { - try - { - $barcode = $this->get_barcode_instance($barcode_config['barcode_type']); - - // generate a barcode only if one is not already available and we use the item_id as seed. - // This to avoid generating Barcodes out of existing Barcodes - if( $barcode_config['barcode_content'] !== "id" && isset($item['item_number']) ) - { - $barcode->setData($item['item_number'], false); - - return null; - } - else - { - $barcode->setData($item['item_id'], true); - - $code = $barcode->getData(); - - // in case no new code is generated like in Code39 and Code128 return an empty string because we don't want to override it with a pure item_id - if( $code == $item['item_id'] ) - { - $code = null; - } - - return $code; - } - } - catch(Exception $e) - { - echo 'Caught exception: ', $e->getMessage(), "\n"; - } - } - - public function create_display_barcode($item, $barcode_config) + public function display_barcode($item, $barcode_config) { $display_table = ""; $display_table .= ""; diff --git a/application/views/barcode_sheet.php b/application/views/barcode_sheet.php index 7c636e627..6f4cb4cbc 100644 --- a/application/views/barcode_sheet.php +++ b/application/views/barcode_sheet.php @@ -16,7 +16,7 @@ barcode_lib->get_font_name($barcode_config['barcode_font']); ?> style="font-size:px"> -
" . $this->manage_display_layout($barcode_config['barcode_first_row'], $item, $barcode_config) . "
width= > +
width='' > '; } - echo ""; + echo ""; $count++; } ?> diff --git a/application/views/barcodes/BarcodeBase.php b/application/views/barcodes/BarcodeBase.php index 93f7bbc21..b96b565aa 100644 --- a/application/views/barcodes/BarcodeBase.php +++ b/application/views/barcodes/BarcodeBase.php @@ -84,6 +84,18 @@ abstract class BarcodeBase { return TRUE; } + + /** + * Generate a barcode for this implementation using the given seed. + * Default implementation returns just the seed + * @param $number The seed to generate a barcode for + * @return mixed The generated barcode + */ + public function generate($number) + { + return $number; + } + /* * (Abstract) Draw the image * diff --git a/application/views/barcodes/Ean13.php b/application/views/barcodes/Ean13.php index 9b30f8dd6..e2e52d5cb 100644 --- a/application/views/barcodes/Ean13.php +++ b/application/views/barcodes/Ean13.php @@ -127,6 +127,32 @@ class Ean13 extends BarcodeBase $this->data = $data; } + /* + * Generate EAN13 code out of a provided number + * Code taken from http://stackoverflow.com/questions/19890144/generate-valid-ean13-in-php (unknown copyright / license claims) + * + * @param number is the internal code you want to have EANed. The prefix, zero-padding and checksum are added by the function. + * @return string with complete EAN13 code + */ + public function generate($number) + { + $number = '200' . str_pad($number, 9, '0'); + $weightflag = true; + $sum = 0; + + // Weight for a digit in the checksum is 3, 1, 3.. starting from the last digit. + // loop backwards to make the loop length-agnostic. The same basic functionality + // will work for codes of different lengths. + for ($i = strlen($number) - 1; $i >= 0; $i--) + { + $sum += (int)$number[$i] * ($weightflag?3:1); + $weightflag = !$weightflag; + } + $number .= (10 - ($sum % 10)) % 10; + + return $number; + } + public function validate($barcode) { // check to see if barcode is 13 digits long diff --git a/application/views/barcodes/Ean8.php b/application/views/barcodes/Ean8.php index c3cdf542c..2f129d567 100644 --- a/application/views/barcodes/Ean8.php +++ b/application/views/barcodes/Ean8.php @@ -89,12 +89,76 @@ class Ean8 extends BarcodeBase ) ); + /* + * Calculate EAN8 or EAN13 automatically + * set $len = 8 for EAN8, $len = 13 for EAN13 + * + * @param number is the internal code you want to have EANed. The prefix, zero-padding and checksum are added by the function. + * @return string with complete EAN code + */ + public function generate($number, $len = 8) + { + $barcode = $number; + + if($number > -1) + { + $data_len = $len - 1; + + //Padding + $barcode = str_pad($barcode, $data_len, '0', STR_PAD_LEFT); + $barcode_len = strlen($barcode); + + // calculate check digit + $sum_a = 0; + for ($i = 1; $i < $data_len; $i += 2) + { + $sum_a += $barcode{$i}; + } + + if ($len > 12) + { + $sum_a *= 3; + } + + $sum_b = 0; + for ($i = 0; $i < $data_len; $i += 2) + { + $sum_b += ($barcode{$i}); + } + + if ($len < 13) + { + $sum_b *= 3; + } + + $r = ($sum_a + $sum_b) % 10; + + if($r > 0) + { + $r = (10 - $r); + } + + if (strlen($barcode) == $data_len) + { + // add check digit + $barcode .= $r; + } + elseif ($r !== intval($barcode{$data_len})) + { + // wrong checkdigit + $barcode = null; + } + } + + return $barcode; + } + public function validate($barcode) { $ean = str_replace(array("-","/"," ","\t","\n"), "", $barcode); // make a clean ean $len = strlen($ean); - if( !is_numeric($ean) ) + if( !is_numeric($ean) || strlen($barcode) != 8 ) { return false; } diff --git a/application/views/configs/barcode_config.php b/application/views/configs/barcode_config.php index f80738966..a6337b8d6 100644 --- a/application/views/configs/barcode_config.php +++ b/application/views/configs/barcode_config.php @@ -86,6 +86,11 @@ echo form_open('config/save_barcode/',array('id'=>'barcode_config_form')); 'value'=>'number', 'checked'=>$this->config->item('barcode_content') === "number")); ?> lang->line('config_barcode_number'); ?> + 'barcode_generate_if_empty', + 'value'=>'barcode_generate_if_empty', + 'checked'=>$this->config->item('barcode_generate_if_empty'))); ?> + lang->line('config_barcode_generate_if_empty'); ?> diff --git a/database/2.3.2_to_2.3.3.sql b/database/2.3.2_to_2.3.3.sql index 5d6fa1109..045f35359 100644 --- a/database/2.3.2_to_2.3.3.sql +++ b/database/2.3.2_to_2.3.3.sql @@ -3,7 +3,8 @@ ALTER TABLE `ospos_suppliers` INSERT INTO `ospos_app_config` (`key`, `value`) VALUES ('dateformat', 'm/d/Y'), - ('timeformat', 'H:i:s'); + ('timeformat', 'H:i:s'), + ('barcode_generate_if_empty', '0'); ALTER TABLE `ospos_sales_suspended` DROP KEY `invoice_number`; diff --git a/database/database.sql b/database/database.sql index 8b86e43e4..e528b2fca 100644 --- a/database/database.sql +++ b/database/database.sql @@ -55,6 +55,7 @@ INSERT INTO `ospos_app_config` (`key`, `value`) VALUES ('barcode_num_in_row', '2'), ('barcode_page_width', '100'), ('barcode_page_cellspacing', '20'), +('barcode_generate_if_empty', '0'), ('receipt_show_taxes', '0'), ('use_invoice_template', '1'), ('invoice_email_message', 'Dear $CU, In attachment the receipt for sale $INV'), diff --git a/translations/config_lang.csv b/translations/config_lang.csv index fa82950a8..b712e53eb 100644 --- a/translations/config_lang.csv +++ b/translations/config_lang.csv @@ -23,6 +23,7 @@ config_barcode_second_row,Rij 2,Fila 1,Row 2,Row 3,Row 4,Row 5,แถว 2,Row 7 config_barcode_third_row,Rij 3,Fila 2,Row 3,Row 4,Row 5,Row 6,แถว 3,Row 8,Row 9 config_barcode_type,Barcode Type,Tipo de Código de Barra,Barcode Type,Barcode Type,Barcode Type,Barcode Type,ประเภทบาร์โค้ด,Barcode Type,Barcode Type config_barcode_width,Breedte (px),Ancho (px),Width (px),Width (px),Width (px),Width (px),กว้าง (px),Width (px),Width (px) +config_barcode_generate_if_empty,Genereer indien leeg,Generate if empty,Generate if empty,Generate if empty,Generate if empty,Generate if empty,Generate if empty,Generate if empty,Generate if empty config_company,Bedrijfsnaam,Nombre del Comercio,Company Name,Nome de l\'Entreprise,公司名稱,Название Компании,ชื่อร้านค้า,Şirket Adı,Nama Perusahaan config_company_logo,Logo,Logotipo del Comercio,Company Logo,Company Logo,Company Logo,Company Logo,ภาพโลโก้,Company Logo,Company Logo config_company_required,De bedrijfsnaam moet ingevuld worden,Nombre del Comercio es requerido,Company name is a required field,Le nom d\'entreprise est requis,公司名稱為必填,Имя Компании обязательный пробел,ชื่อร้านค้าต้องกรอก,Şirket Adı zorunlu alandır,Nama Perusahaan wajib diisi
" . $this->barcode_lib->create_display_barcode($item, $barcode_config) . "" . $this->barcode_lib->display_barcode($item, $barcode_config) . "