forked from OCA/wms
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge commit 'refs/pull/521/head' of github.com:OCA/wms into merge-br…
…anch-2477-BSCOS-4099-cfe45b66
- Loading branch information
Showing
19 changed files
with
408 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
# generated from manifests external_dependencies | ||
biip==2.3.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../shopfloor_gs1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import setuptools | ||
|
||
setuptools.setup( | ||
setup_requires=['setuptools-odoo'], | ||
odoo_addon=True, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
bot, please! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import actions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Copyright 2022 Camptocamp SA (http://www.camptocamp.com) | ||
# @author Simone Orsi <[email protected]> | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
||
{ | ||
"name": "Shopfloor GS1", | ||
"summary": "Integrate GS1 barcode scan into Shopfloor app", | ||
"version": "14.0.1.0.0", | ||
"development_status": "Beta", | ||
"category": "Inventory", | ||
"website": "https://github.com/OCA/wms", | ||
"author": "Camptocamp, Odoo Community Association (OCA)", | ||
"maintainers": ["simahawk", "sebalix"], | ||
"license": "AGPL-3", | ||
"depends": ["shopfloor"], | ||
"external_dependencies": { | ||
"python": [ | ||
# >= 2.3.0 required to use 'GS1Message.parse_hri' method | ||
# and next version 3.0.0 has been refactored bringing | ||
# incompatibility issues (to check later). | ||
"biip==2.3.0" | ||
] | ||
}, | ||
"data": [], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import search |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Copyright 2022 Camptocamp SA (http://www.camptocamp.com) | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
from odoo.addons.component.core import Component | ||
|
||
from ..config import MAPPING_AI_TO_TYPE, MAPPING_TYPE_TO_AI | ||
from ..utils import GS1Barcode | ||
|
||
|
||
class SearchAction(Component): | ||
_inherit = "shopfloor.search.action" | ||
|
||
def _search_type_to_gs1_ai(self, _type): | ||
"""Convert search type to AIs. | ||
Each type can be mapped to multiple AIs. | ||
For instance, you can search a product by barcode (01) or manufacturer code (240). | ||
""" | ||
return MAPPING_TYPE_TO_AI.get(_type) | ||
|
||
def _gs1_ai_to_search_type(self, ai): | ||
"""Convert back GS1 AI to search type.""" | ||
return MAPPING_AI_TO_TYPE[ai] | ||
|
||
def find(self, barcode, types=None, handler_kw=None): | ||
barcode = barcode or "" | ||
# Try to find records via GS1 and fallback to normal search | ||
res = self._find_gs1(barcode, types=types) | ||
if res: | ||
return res | ||
return super().find(barcode, types=types, handler_kw=handler_kw) | ||
|
||
def _find_gs1(self, barcode, types=None, handler_kw=None, safe=True): | ||
types = types or () | ||
ai_whitelist = () | ||
# Collect all AIs by converting from search types | ||
for _type in types: | ||
ai = self._search_type_to_gs1_ai(_type) | ||
if ai: | ||
ai_whitelist += ai | ||
if types and not ai_whitelist: | ||
# A specific type was asked but no AI could be found. | ||
return | ||
parsed = GS1Barcode.parse(barcode, ai_whitelist=ai_whitelist, safe=safe) | ||
# Return the 1st record found if parsing was successful | ||
for item in parsed: | ||
record = self.generic_find( | ||
item.value, | ||
types=(self._gs1_ai_to_search_type(item.ai),), | ||
handler_kw=handler_kw, | ||
) | ||
if record: | ||
return record |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Copyright 2022 Camptocamp SA (http://www.camptocamp.com) | ||
# @author Simone Orsi <[email protected]> | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
||
# https://www.gs1.org/standards/barcodes/application-identifiers | ||
# TODO: define other internal mappings by convention | ||
|
||
|
||
# Each type can be mapped to multiple AIs. | ||
# For instance, you can search a product by barcode (01) or manufacturer code (240). | ||
MAPPING_TYPE_TO_AI = { | ||
"product": ("01", "240"), | ||
"lot": ("10",), | ||
"production_date": ("11",), | ||
"serial": ("21",), | ||
"manuf_product_code": ("240",), | ||
"location": ("254",), | ||
} | ||
MAPPING_AI_TO_TYPE = { | ||
"01": "product", | ||
"10": "lot", | ||
"11": "production_date", | ||
"21": "serial", | ||
"240": "product", | ||
"254": "location", | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
* Simone Orsi <[email protected]> | ||
* Sébastien Alix <[email protected]> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Add GS1 barcode support to Shopfloor. | ||
|
||
Based on https://biip.readthedocs.io/ | ||
|
||
TODO.... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from . import test_utils | ||
from . import test_action_search | ||
from . import test_scan_anything |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
PROD_BARCODE = "09506000117843" | ||
MANUF_CODE = "K000075" | ||
DATE = "141231" | ||
LOT1 = "1234AB" | ||
LOT2 = "1234AC" | ||
GS1_GTIN_BARCODE_1 = f"(01){PROD_BARCODE}(11){DATE}(10){LOT1}" | ||
GS1_GTIN_BARCODE_2 = f"(01){PROD_BARCODE}(11){DATE}(10){LOT2}" | ||
GS1_MANUF_BARCODE = f"(240){MANUF_CODE}(11){DATE}(10){LOT1}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# Copyright 2022 Camptocamp SA (http://www.camptocamp.com) | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
from odoo.addons.shopfloor.tests.test_actions_search import TestSearchBaseCase | ||
|
||
from .common import ( | ||
GS1_GTIN_BARCODE_1, | ||
GS1_MANUF_BARCODE, | ||
LOT1, | ||
MANUF_CODE, | ||
PROD_BARCODE, | ||
) | ||
|
||
|
||
class TestFind(TestSearchBaseCase): | ||
@classmethod | ||
def setUpClassBaseData(cls): | ||
super().setUpClassBaseData() | ||
cls.product_a.barcode = PROD_BARCODE | ||
|
||
def test_find_picking(self): | ||
ptype = self.env.ref("shopfloor.picking_type_single_pallet_transfer_demo") | ||
rec = self._create_picking(picking_type=ptype) | ||
res = self.search.find(rec.name, types=("picking",)) | ||
self.assertEqual(res.record, rec) | ||
|
||
def test_find_location(self): | ||
rec = self.customer_location | ||
barcode = GS1_GTIN_BARCODE_1 + "(254)" + rec.name | ||
res = self.search.find(barcode, types=("location",)) | ||
self.assertEqual(res.record, rec) | ||
res = self.search.find(rec.name, types=("location",)) | ||
self.assertEqual(res.record, rec) | ||
|
||
def test_find_package(self): | ||
rec = self.env["stock.quant.package"].sudo().create({"name": "ABC1234"}) | ||
res = self.search.find(rec.name, types=("package",)) | ||
self.assertEqual(res.record, rec) | ||
|
||
def test_find_product(self): | ||
rec = self.product_a | ||
res = self.search.find(GS1_GTIN_BARCODE_1, types=("product",)) | ||
self.assertEqual(res.record, rec) | ||
rec.barcode = MANUF_CODE | ||
res = self.search.find(GS1_MANUF_BARCODE, types=("product",)) | ||
self.assertEqual(res.record, rec) | ||
|
||
def test_find_lot(self): | ||
rec = ( | ||
self.env["stock.production.lot"] | ||
.sudo() | ||
.create( | ||
{ | ||
"product_id": self.product_a.id, | ||
"company_id": self.env.company.id, | ||
"name": LOT1, | ||
} | ||
) | ||
) | ||
res = self.search.find( | ||
GS1_GTIN_BARCODE_1, | ||
types=("lot",), | ||
handler_kw=dict(lot=dict(products=self.product_a)), | ||
) | ||
self.assertEqual(res.record, rec) | ||
|
||
def test_find_generic_packaging(self): | ||
rec = ( | ||
self.env["product.packaging"] | ||
.sudo() | ||
.create({"name": "TEST PKG", "barcode": "1234"}) | ||
) | ||
res = self.search.find(rec.barcode, types=("delivery_packaging",)) | ||
self.assertEqual(res.record, rec) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# Copyright 2023 Camptocamp SA (http://www.camptocamp.com) | ||
# @author Simone Orsi <[email protected]> | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
||
from odoo.addons.shopfloor.tests.test_actions_data_base import ActionsDataDetailCaseBase | ||
from odoo.addons.shopfloor_base.tests.common_misc import ScanAnythingTestMixin | ||
|
||
from .common import ( | ||
GS1_GTIN_BARCODE_1, | ||
GS1_MANUF_BARCODE, | ||
LOT1, | ||
MANUF_CODE, | ||
PROD_BARCODE, | ||
) | ||
|
||
|
||
class ScanAnythingCase(ActionsDataDetailCaseBase, ScanAnythingTestMixin): | ||
def test_scan_product(self): | ||
record = self.product_b | ||
record.barcode = PROD_BARCODE | ||
record.default_code = MANUF_CODE | ||
rec_type = "product" | ||
data = self.data_detail.product_detail(record) | ||
# All kinds of search supported | ||
for identifier in ( | ||
GS1_GTIN_BARCODE_1, | ||
GS1_MANUF_BARCODE, | ||
record.barcode, | ||
record.default_code, | ||
): | ||
self._test_response_ok(rec_type, data, identifier) | ||
|
||
def test_find_location(self): | ||
record = self.stock_location | ||
rec_type = "location" | ||
gs1_barcode = GS1_GTIN_BARCODE_1 + "(254)" + record.name | ||
data = self.data_detail.location_detail(record) | ||
for identifier in (gs1_barcode, record.name): | ||
self._test_response_ok(rec_type, data, identifier) | ||
|
||
def test_scan_package(self): | ||
record = self.package | ||
rec_type = "package" | ||
identifier = record.name | ||
data = self.data_detail.package_detail(record) | ||
self._test_response_ok(rec_type, data, identifier) | ||
|
||
def test_scan_lot(self): | ||
record = ( | ||
self.env["stock.production.lot"] | ||
.sudo() | ||
.create( | ||
{ | ||
"product_id": self.product_a.id, | ||
"company_id": self.env.company.id, | ||
"name": LOT1, | ||
} | ||
) | ||
) | ||
rec_type = "lot" | ||
identifier = record.name | ||
data = self.data_detail.lot_detail(record) | ||
for identifier in (GS1_GTIN_BARCODE_1, record.name): | ||
self._test_response_ok(rec_type, data, identifier) | ||
|
||
def test_scan_transfer(self): | ||
record = self.picking | ||
rec_type = "transfer" | ||
identifier = record.name | ||
data = self.data_detail.picking_detail(record) | ||
self._test_response_ok(rec_type, data, identifier) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# Copyright 2022 Camptocamp | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
||
import datetime | ||
|
||
from odoo.tests.common import BaseCase | ||
|
||
from ..utils import GS1Barcode | ||
|
||
|
||
class TestUtils(BaseCase): | ||
def test_parse1(self): | ||
code = "(01)09506000117843(11)141231(10)1234AB" | ||
res = GS1Barcode.parse(code) | ||
self.assertEqual(len(res), 3, res) | ||
item = [x for x in res if x.ai == "01"][0] | ||
self.assertEqual(item.code, code) | ||
self.assertEqual(item.value, "09506000117843") | ||
self.assertEqual(item.raw_value, "09506000117843") | ||
item = [x for x in res if x.ai == "11"][0] | ||
self.assertEqual(item.code, code) | ||
self.assertEqual(item.value, datetime.date(2014, 12, 31)) | ||
self.assertEqual(item.raw_value, "141231") | ||
item = [x for x in res if x.ai == "10"][0] | ||
self.assertEqual(item.code, code) | ||
self.assertEqual(item.value, "1234AB") | ||
self.assertEqual(item.raw_value, "1234AB") | ||
|
||
def test_parse2(self): | ||
code = "(01)09506000117843(11)141231(10)1234AB" | ||
res = GS1Barcode.parse(code, ai_whitelist=("01",)) | ||
self.assertEqual(len(res), 1, res) | ||
item = [x for x in res if x.ai == "01"][0] | ||
self.assertEqual(item.code, code) | ||
self.assertEqual(item.value, "09506000117843") | ||
self.assertEqual(item.raw_value, "09506000117843") | ||
|
||
def test_parse3(self): | ||
code = "(240)K000075(11)230201(10)0000392" | ||
res = GS1Barcode.parse(code, ai_whitelist=("240",)) | ||
self.assertEqual(len(res), 1, res) | ||
item = [x for x in res if x.ai == "240"][0] | ||
self.assertEqual(item.code, code) | ||
self.assertEqual(item.value, "K000075") | ||
self.assertEqual(item.raw_value, "K000075") | ||
|
||
def test_parse_order(self): | ||
"""Ensure ai whitelist order is respected""" | ||
code = "(01)09506000117843(11)141231(10)1234AB" | ||
res = GS1Barcode.parse(code, ai_whitelist=("10", "01", "11")) | ||
self.assertEqual(len(res), 3, res) | ||
self.assertEqual(res[0].ai, "10") | ||
self.assertEqual(res[1].ai, "01") | ||
self.assertEqual(res[2].ai, "11") | ||
res = GS1Barcode.parse(code, ai_whitelist=("01", "11", "10")) | ||
self.assertEqual(len(res), 3, res) | ||
self.assertEqual(res[0].ai, "01") | ||
self.assertEqual(res[1].ai, "11") | ||
self.assertEqual(res[2].ai, "10") |
Oops, something went wrong.