diff --git a/stock_cubiscan/__manifest__.py b/stock_cubiscan/__manifest__.py
index e3eb53685..6afdff588 100644
--- a/stock_cubiscan/__manifest__.py
+++ b/stock_cubiscan/__manifest__.py
@@ -3,9 +3,9 @@
{
"name": "Stock Cubiscan",
"summary": "Implement inteface with Cubiscan devices for packaging",
- "version": "12.0.1.0.0",
- "category": "Stock",
- "author": "Camptocamp",
+ "version": "13.0.1.0.0",
+ "category": "Warehouse",
+ "author": "Camptocamp, Odoo Community Association (OCA)",
"license": "AGPL-3",
"depends": [
"barcodes",
@@ -14,7 +14,8 @@
"product_packaging_dimension",
"product_packaging_type_required",
],
- "website": "http://www.camptocamp.com",
+ "external_dependencies": {"python": ["cubiscan"]},
+ "website": "https://github.com/OCA/stock-logistics-warehouse",
"data": [
"views/assets.xml",
"views/cubiscan_view.xml",
diff --git a/stock_cubiscan/models/cubiscan.py b/stock_cubiscan/models/cubiscan.py
index 802892bd1..8e0b48e69 100644
--- a/stock_cubiscan/models/cubiscan.py
+++ b/stock_cubiscan/models/cubiscan.py
@@ -25,23 +25,15 @@ class CubiscanDevice(models.Model):
[("not_ready", "Not Ready"), ("ready", "Ready")],
default="not_ready",
readonly=True,
+ copy=False,
)
- @api.multi
@api.constrains("device_address", "port")
def _check_connection_infos(self):
self.ensure_one()
if not 1 <= self.port <= 65535:
- raise ValidationError("Port must be in range 1-65535")
+ raise ValidationError(_("Port must be in range 1-65535"))
- @api.multi
- def copy(self, default=None):
- if not default:
- default = dict()
- default["state"] = "not_ready"
- return super().copy(default)
-
- @api.multi
def open_wizard(self):
self.ensure_one()
return {
@@ -50,18 +42,20 @@ class CubiscanDevice(models.Model):
"type": "ir.actions.act_window",
"view_id": False,
"view_mode": "form",
- "view_type": "form",
"context": {"default_device_id": self.id},
"target": "fullscreen",
"flags": {
- "headless": True,
+ "withControlPanel": False,
"form_view_initial_mode": "edit",
"no_breadcrumbs": True,
},
}
- @api.multi
def _get_interface(self):
+ """Return the CubiScan client
+
+ Can be overrided to customize the way it is instanciated
+ """
self.ensure_one()
ctx = SSL.create_default_context()
ctx.load_cert_chain("/usr/lib/ssl/certs/camptocamp.pem")
@@ -69,8 +63,8 @@ class CubiscanDevice(models.Model):
ctx.verify_mode = SSL.CERT_NONE
return CubiScan(self.device_address, self.port, self.timeout, ssl=ctx)
- @api.multi
def test_device(self):
+ """Check connection with the Cubiscan device"""
for device in self:
res = device._get_interface().test()
if res and "error" not in res and device.state == "not_ready":
@@ -78,12 +72,14 @@ class CubiscanDevice(models.Model):
elif res and "error" in res and device.state == "ready":
device.state = "not_ready"
- @api.multi
def get_measure(self):
+ """Return a measure from the Cubiscan device"""
self.ensure_one()
if self.state != "ready":
raise UserError(
- "Device is not ready. Please use the 'Test'"
- " button before using the device."
+ _(
+ "Device is not ready. Please use the 'Test'"
+ " button before using the device."
+ )
)
return self._get_interface().measure()
diff --git a/stock_cubiscan/models/stock.py b/stock_cubiscan/models/stock.py
index e28248e4c..e0ce1e610 100644
--- a/stock_cubiscan/models/stock.py
+++ b/stock_cubiscan/models/stock.py
@@ -14,7 +14,8 @@ class StockWarehouse(models.Model):
class ProductPackaging(models.Model):
_inherit = "product.packaging"
- # FIXME: Not sure this is still the best place for this constraint
+ # FIXME: move this constraint in product_packaging_type
+ # https://github.com/OCA/product-attribute/tree/13.0/product_packaging_type
_sql_constraints = [
(
"product_packaging_type_unique",
diff --git a/stock_cubiscan/tests/test_cubiscan_wizard.py b/stock_cubiscan/tests/test_cubiscan_wizard.py
index 5bf5c24d4..ad4e3d941 100644
--- a/stock_cubiscan/tests/test_cubiscan_wizard.py
+++ b/stock_cubiscan/tests/test_cubiscan_wizard.py
@@ -1,7 +1,8 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-# from cubiscan.cubiscan import CubiScan
-# from mock import patch
+from cubiscan.cubiscan import CubiScan
+from mock import patch
+
from odoo.tests.common import SavepointCase
@@ -66,63 +67,56 @@ class TestCubiscanWizard(SavepointCase):
PackType.cron_check_create_required_packaging()
def test_product_onchange(self):
- return
- # self.wizard.product_id = self.product_1.id
+ self.wizard.product_id = self.product_1.id
- # self.assertEqual(len(self.wizard.line_ids), 0)
- # self.wizard.onchange_product_id()
- # self.assertEqual(len(self.wizard.line_ids), 5)
+ self.assertEqual(len(self.wizard.line_ids), 0)
+ self.wizard.onchange_product_id()
+ self.assertEqual(len(self.wizard.line_ids), 6)
def test_product_onchange_barcode(self):
- return
- # self.assertFalse(self.wizard.product_id)
- # self.assertFalse(self.wizard.line_ids)
+ self.assertFalse(self.wizard.product_id)
+ self.assertFalse(self.wizard.line_ids)
- # self.wizard.on_barcode_scanned('424242')
+ self.wizard.on_barcode_scanned("424242")
- # self.assertEqual(self.wizard.product_id, self.product_1)
- # self.assertEqual(len(self.wizard.line_ids), 5)
+ self.assertEqual(self.wizard.product_id, self.product_1)
+ self.assertEqual(len(self.wizard.line_ids), 6)
def test_cubiscan_measures(self):
- return
- # self.wizard.product_id = self.product_1.id
- # self.wizard.onchange_product_id()
+ self.wizard.product_id = self.product_1.id
+ self.wizard.onchange_product_id()
- # with patch.object(CubiScan, '_make_request') as request:
- # for idx, line in enumerate(self.wizard.line_ids):
- # request.return_value = TestCubiscanWizard.get_measure_result(
- # 2 ** idx, 1, 1, 2 ** idx
- # )
- # line.cubiscan_measure()
- # self.assertEqual(
- # line.read(
- # ['length', 'width', 'height', 'max_weight', 'volume']
- # )[0],
- # {
- # 'id': line.id,
- # 'length': (2 ** idx) * 1000,
- # 'width': 1000,
- # 'height': 1000,
- # 'max_weight': 2.0 ** idx,
- # 'volume': 2.0 ** idx,
- # },
- # )
+ with patch.object(CubiScan, "_make_request") as request:
+ for idx, line in enumerate(self.wizard.line_ids):
+ request.return_value = TestCubiscanWizard.get_measure_result(
+ 2 ** idx, 1, 1, 2 ** idx
+ )
+ line.cubiscan_measure()
+ self.assertEqual(
+ line.read(["lngth", "width", "height", "max_weight", "volume"])[0],
+ {
+ "id": line.id,
+ "lngth": (2 ** idx) * 1000,
+ "width": 1000,
+ "height": 1000,
+ "max_weight": 2.0 ** idx,
+ "volume": 2.0 ** idx,
+ },
+ )
- # self.wizard.action_save()
+ self.wizard.action_save()
- # packagings = self.product_1.packaging_ids
- # self.assertEqual(len(packagings), 5)
- # for idx, packaging in enumerate(packagings):
- # self.assertEqual(
- # packaging.read(
- # ['length', 'width', 'height', 'max_weight', 'volume']
- # )[0],
- # {
- # 'id': packaging.id,
- # 'length': (2 ** idx) * 1000,
- # 'width': 1000,
- # 'height': 1000,
- # 'max_weight': 2.0 ** idx,
- # 'volume': 2.0 ** idx,
- # },
- # )
+ packagings = self.product_1.packaging_ids.sorted()
+ self.assertEqual(len(packagings), 6)
+ for idx, packaging in enumerate(packagings):
+ self.assertEqual(
+ packaging.read(["lngth", "width", "height", "max_weight", "volume"])[0],
+ {
+ "id": packaging.id,
+ "lngth": (2 ** idx) * 1000,
+ "width": 1000,
+ "height": 1000,
+ "max_weight": 2.0 ** idx,
+ "volume": 2.0 ** idx,
+ },
+ )
diff --git a/stock_cubiscan/views/cubiscan_view.xml b/stock_cubiscan/views/cubiscan_view.xml
index 93e9131b2..20f7b9b71 100644
--- a/stock_cubiscan/views/cubiscan_view.xml
+++ b/stock_cubiscan/views/cubiscan_view.xml
@@ -46,7 +46,6 @@
CubiScan Devices
cubiscan.device
- form
tree,form
diff --git a/stock_cubiscan/wizard/cubiscan_wizard.py b/stock_cubiscan/wizard/cubiscan_wizard.py
index 2f3115aa1..b007cdbdd 100644
--- a/stock_cubiscan/wizard/cubiscan_wizard.py
+++ b/stock_cubiscan/wizard/cubiscan_wizard.py
@@ -5,6 +5,11 @@ from odoo import _, api, fields, models
class CubiscanWizard(models.TransientModel):
+ """This wizard is used to show a screen showing Cubiscan information
+
+ It is opened in a headless view (no breadcrumb, no menus, fullscreen).
+ """
+
_name = "cubiscan.wizard"
_inherit = "barcodes.barcode_events_mixin"
_description = "Cubiscan Wizard"
@@ -33,7 +38,7 @@ class CubiscanWizard(models.TransientModel):
"name": pack_type.name,
"qty": 0,
"max_weight": 0,
- "length": 0,
+ "lngth": 0,
"width": 0,
"height": 0,
"barcode": False,
@@ -44,7 +49,7 @@ class CubiscanWizard(models.TransientModel):
{
"qty": pack.qty,
"max_weight": pack.max_weight,
- "length": pack.length,
+ "lngth": pack.lngth,
"width": pack.width,
"height": pack.height,
"barcode": pack.barcode,
@@ -58,7 +63,6 @@ class CubiscanWizard(models.TransientModel):
else:
self.line_ids = [(5, 0, 0)]
- @api.multi
def action_reopen_fullscreen(self):
# Action to reopen wizard in fullscreen (e.g. after page refresh)
self.ensure_one()
@@ -75,14 +79,12 @@ class CubiscanWizard(models.TransientModel):
"target": "new",
}
- @api.multi
def on_barcode_scanned(self, barcode):
self.ensure_one()
prod = self.env["product.product"].search([("barcode", "=", barcode)])
self.product_id = prod
self.onchange_product_id()
- @api.multi
def action_save(self):
self.ensure_one()
actions = []
@@ -91,7 +93,7 @@ class CubiscanWizard(models.TransientModel):
"name": line.name,
"qty": line.qty,
"max_weight": line.max_weight,
- "length": line.length,
+ "lngth": line.lngth,
"width": line.width,
"height": line.height,
"barcode": line.barcode,
@@ -103,8 +105,9 @@ class CubiscanWizard(models.TransientModel):
else:
actions.append((0, 0, vals))
self.product_id.packaging_ids = actions
+ # reload lines
+ self.onchange_product_id()
- @api.multi
def action_close(self):
self.ensure_one()
action = self.env.ref("stock_cubiscan.action_cubiscan_device_form").read()[0]
@@ -134,7 +137,9 @@ class CubiscanWizardLine(models.TransientModel):
name = fields.Char("Packaging", readonly=True)
qty = fields.Float("Quantity")
max_weight = fields.Float("Weight (kg)", readonly=True)
- length = fields.Integer("Length (mm)", readonly=True)
+ # this is not a typo:
+ # https://github.com/odoo/odoo/issues/41353#issuecomment-568037415
+ lngth = fields.Integer("Length (mm)", readonly=True)
width = fields.Integer("Width (mm)", readonly=True)
height = fields.Integer("Height (mm)", readonly=True)
volume = fields.Float(
@@ -151,20 +156,20 @@ class CubiscanWizardLine(models.TransientModel):
)
required = fields.Boolean(related="packaging_type_id.required", readonly=True)
- @api.depends("length", "width", "height")
+ @api.depends("lngth", "width", "height")
def _compute_volume(self):
for line in self:
- line.volume = (line.length * line.width * line.height) / 1000.0 ** 3
+ line.volume = (line.lngth * line.width * line.height) / 1000.0 ** 3
- @api.multi
def cubiscan_measure(self):
self.ensure_one()
measures = self.wizard_id.device_id.get_measure()
+ # measures are a tuple of 2 slots (measure, precision error),
+ # we only care about the measure for now
measures = {
- k: (v[0] if k in ["length", "width", "height", "weight"] else False)
- for k, v in measures.items()
+ "lngth": int(measures["length"][0] * 1000),
+ "width": int(measures["width"][0] * 1000),
+ "height": int(measures["height"][0] * 1000),
+ "max_weight": measures["weight"][0],
}
- weight = measures.pop("weight")
- measures = {k: int(v * 1000) for k, v in measures.items()}
- measures["max_weight"] = weight
self.write(measures)
diff --git a/stock_cubiscan/wizard/cubiscan_wizard.xml b/stock_cubiscan/wizard/cubiscan_wizard.xml
index a6595d408..9f0e09a27 100644
--- a/stock_cubiscan/wizard/cubiscan_wizard.xml
+++ b/stock_cubiscan/wizard/cubiscan_wizard.xml
@@ -10,7 +10,10 @@
-
+
+
+
+
@@ -23,7 +26,7 @@
-
+