POS - Reading variable measure barcode
Code snippetName: POS - Reading variable measure barcode
|
Purpose
Currently OpenBravo POS cannot read a variable EAN code that includes information about Item in different format according to GTIN specification. For example an EAN barcode might include product price product has been weighted on a scale in the shop. A barcode might also include the weight of the product. In both cases OpenBravo POS should be able to find the correct product from the product registry and apply the correct price on the sales line.
This solution has to be adapted for local/country specific rules. This example is an implementation for Finnish GS1 rules, where a variable measure bar code can consist of:
0 2 R R R R R R R R R R C
2 0 I I I I M M P P, P P C
2 1 I I I I M M P P P, P C
2 2 I I I I M M P P P P C
2 3 I I I I M M W, W W W C
2 4 I I I I M M W W, W W C
2 5 I I I I M M W W W, W C
2 8 R R R R R P P P, P P C
Where:
R = Retailer assigned,
M = Manuf. assigned,
V = Verifier digit,
W = Weight,
I = MO assigned,
IM = Manuf. ID for special product ass. by MO,
P = Price N = Pieces/Other
References
Information about GTIN-13 barcodes: [1]
Use of 02 and 20 to 29 Codes in GS1 Europe Countries: [2]
1. In DataLogicSales.java (com.openbravo.pos.forms) add method:
public final ProductInfoExt getProductInfoByShortCode(String sCode) throws BasicException { return (ProductInfoExt) new PreparedSentence(s , "SELECT ID, REFERENCE, CODE, NAME, ISCOM, ISSCALE, PRICEBUY, PRICESELL, TAXCAT, CATEGORY, ATTRIBUTESET_ID, IMAGE, ATTRIBUTES " + "FROM PRODUCTS WHERE SUBSTRING( CODE, 3, 6 ) = ?" , SerializerWriteString.INSTANCE , ProductInfoExt.getSerializerRead()).find(sCode.substring(2, 8)); }
Change method getProductInfoByCode so that it looks like this:
public final ProductInfoExt getProductInfoByCode(String sCode) throws BasicException { if (sCode.length() == 13 && (sCode.startsWith("2") || sCode.startsWith("02"))) return getProductInfoByShortCode(sCode); else { return (ProductInfoExt) new PreparedSentence(s , "SELECT ID, REFERENCE, CODE, NAME, ISCOM, ISSCALE, PRICEBUY, PRICESELL, TAXCAT, CATEGORY, ATTRIBUTESET_ID, IMAGE, ATTRIBUTES " + "FROM PRODUCTS WHERE CODE = ?" , SerializerWriteString.INSTANCE , ProductInfoExt.getSerializerRead()).find(sCode); } }
2. In JPanelTicket.java (com.openbravo.pos.sales) uncomment part of code and add the part that extracts information from the barcode. This functionality will have to be adapted according to country specific rules (see references).
/* START uncommenting part for implementation of variable measure barcode else if (sCode.length() == 13 && sCode.startsWith("250")) { // barcode of the other machine ProductInfoExt oProduct = new ProductInfoExt(); // Es un ticket oProduct.setReference(null); // para que no se grabe oProduct.setCode(sCode); oProduct.setName("Ticket " + sCode.substring(3, 7)); oProduct.setPriceSell(Double.parseDouble(sCode.substring(7, 12)) / 100); oProduct.setTaxCategoryID(((TaxCategoryInfo) taxcategoriesmodel.getSelectedItem()).getID()); // Se anade directamente una unidad con el precio y todo addTicketLine(oProduct, 1.0, includeTaxes(oProduct.getTaxCategoryID(), oProduct.getPriceSell())); } else if (sCode.length() == 13 && sCode.startsWith("210")) { // barcode of a weigth product incProductByCodePrice(sCode.substring(0, 7), Double.parseDouble(sCode.substring(7, 12)) / 100); } * STOP uncommenting part for implementation of variable measure barcode */ //start implementation of variable barcode else if ((sCode.length() == 13) && sCode.startsWith("2") || sCode.startsWith("02")) { try { ProductInfoExt oProduct = dlSales.getProductInfoByCode(sCode); if (oProduct == null) { Toolkit.getDefaultToolkit().beep(); new MessageInf(MessageInf.SGN_WARNING, AppLocal.getIntString("message.noproduct")).show(this); stateToZero(); } else { //set properties for product so we can use them directly using scripts in OpenBravo POS resources oProduct.setProperty("product.barcode", sCode); //get the price based on the barcode double dPriceSell = oProduct.getPriceSell(); //default price for product double weight = 0; //used if barcode includes weight of product String sVariableTypePrefix = sCode.substring(0, 2); String sVariableNum = sCode.substring(8, 12); if (sVariableTypePrefix.equals("20")) dPriceSell = Double.parseDouble(sVariableNum) / 100; //Price with two decimals else if (sVariableTypePrefix.equals("21")) dPriceSell = Double.parseDouble(sVariableNum) / 10; //Price with one decimals else if (sVariableTypePrefix.equals("22")) dPriceSell = Double.parseDouble(sVariableNum); //Price with no decimals else if (sVariableTypePrefix.equals("23")) weight = Double.parseDouble(sVariableNum) / 1000; //Weight in kg with three decimals (e.g. 1,234 kg) else if (sVariableTypePrefix.equals("24")) weight = Double.parseDouble(sVariableNum) / 100; //Weight in kg with two decimals (e.g. 12,34 kg) else if (sVariableTypePrefix.equals("25")) weight = Double.parseDouble(sVariableNum) / 10; //Weight in kg with one decimals (e.g. 123,4 kg) if ((sVariableTypePrefix.equals("20")) || (sVariableTypePrefix.equals("21")) || (sVariableTypePrefix.equals("22"))) { //price in barcode already includes taxes so remove tax from dPriceSell to avoid doublication of tax TaxInfo tax = taxeslogic.getTaxInfo(oProduct.getTaxCategoryID(), m_oTicket.getDate(), m_oTicket.getCustomer()); dPriceSell = dPriceSell / (1.0 + tax.getRate()); oProduct.setProperty("product.price", Double.toString(dPriceSell)); } else if ((sVariableTypePrefix.equals("23")) || (sVariableTypePrefix.equals("24")) || (sVariableTypePrefix.equals("25"))) { dPriceSell = weight * oProduct.getPriceSell(); oProduct.setProperty("product.weight", Double.toString(weight)); } if (m_jaddtax.isSelected()) { TaxInfo tax = taxeslogic.getTaxInfo(oProduct.getTaxCategoryID(), m_oTicket.getDate(), m_oTicket.getCustomer()); addTicketLine(oProduct, 1.0, dPriceSell / (1.0 + tax.getRate())); } else { addTicketLine(oProduct, 1.0, dPriceSell); } } } catch (BasicException eData) { stateToZero(); new MessageInf(eData).show(this); } }
3. In JPanelTicket.java you probably noticed that product properties were added to the product. These properties can easily be used in dynamic scripts in OpenBravo POS, e.g. if you want to print the weight of the product on the ticket after the name follow these steps:
In Resources => Ticket.Buttons add:
<event key="ticket.change" code="event.change"/>
In Resources => add resource "event.change" with following code:
import com.openbravo.pos.ticket.TicketLineInfo; index = sales.getSelectedIndex(); if (index != -1) { line = ticket.getLine(index); String barcode = line.getProperty("product.barcode",""); String price = line.getProperty("product.price",""); String weight = line.getProperty("product.weight",""); if (weight != "") line.setProperty("product.name", "" + line.getProperty("product.name") + " (" + weight + " kg)"); }
This will print the name of the product and its weight on the receipt e.g. "Banana (1,75 kg)"
Assumptions
The product with a variable measure barcode must exist in the product registry (database) with a barcode of length 13 (EAN 13).
The correct way to store variable measure barcodes would normally be 2 3 I I I I M M 0 0 0 0 C (example: 2378215700007). I.e. 0 (zero) in place of weight, and I I I I M M representing the six characters of the product code.
This solution however does also allow storing specific item barcodes in registry (with weight or price).
For example: 2378215712802 (weight 1,28 kg)
When another item with the same product number but different weight is scanned through the POS, the product will be correctly identified and the correct weight will be extracted from the barcode. The six characters representing product number in this example is “782157”. So the barcode 2378215715001 will be identified against either 2378215700007 or 2378215712802 in the product registry (database).