forked from OCA/stock-logistics-warehouse
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by simahawk
- Loading branch information
Showing
24 changed files
with
785 additions
and
0 deletions.
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 |
---|---|---|
@@ -0,0 +1,2 @@ | ||
cubiscan | ||
mock |
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 @@ | ||
../../../../stock_cubiscan |
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,2 @@ | ||
from . import models | ||
from . import wizard |
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,27 @@ | ||
# Copyright 2019 Camptocamp SA | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
{ | ||
"name": "Stock Cubiscan", | ||
"summary": "Implement inteface with Cubiscan devices for packaging", | ||
"version": "13.0.1.0.0", | ||
"category": "Warehouse", | ||
"author": "Camptocamp, Odoo Community Association (OCA)", | ||
"license": "AGPL-3", | ||
"depends": [ | ||
"barcodes", | ||
"stock", | ||
"web_tree_dynamic_colored_field", | ||
"product_packaging_dimension", | ||
"product_packaging_type_required", | ||
], | ||
"external_dependencies": {"python": ["cubiscan"]}, | ||
"website": "https://github.com/OCA/stock-logistics-warehouse", | ||
"data": [ | ||
"views/assets.xml", | ||
"views/cubiscan_view.xml", | ||
"wizard/cubiscan_wizard.xml", | ||
"security/ir.model.access.csv", | ||
], | ||
"installable": True, | ||
"development_status": "Alpha", | ||
} |
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 @@ | ||
from . import stock | ||
from . import cubiscan |
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,98 @@ | ||
# Copyright 2019 Camptocamp SA | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
||
from cubiscan.cubiscan import CubiScan | ||
|
||
from odoo import _, api, fields, models | ||
from odoo.exceptions import UserError, ValidationError | ||
|
||
|
||
class CubiscanDevice(models.Model): | ||
_name = "cubiscan.device" | ||
_description = "Cubiscan Device" | ||
_order = "warehouse_id, name" | ||
|
||
name = fields.Char("Name", required=True) | ||
device_address = fields.Char("Device IP Address", required=True) | ||
port = fields.Integer("Port", required=True) | ||
timeout = fields.Integer( | ||
"Timeout", help="Timeout in seconds", required=True, default=30 | ||
) | ||
warehouse_id = fields.Many2one("stock.warehouse", "Warehouse") | ||
state = fields.Selection( | ||
[("not_ready", "Not Ready"), ("ready", "Ready")], | ||
default="not_ready", | ||
readonly=True, | ||
copy=False, | ||
) | ||
|
||
@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")) | ||
|
||
def open_wizard(self): | ||
self.ensure_one() | ||
return { | ||
"name": _("CubiScan Wizard"), | ||
"res_model": "cubiscan.wizard", | ||
"type": "ir.actions.act_window", | ||
"view_id": False, | ||
"view_mode": "form", | ||
"context": {"default_device_id": self.id}, | ||
"target": "fullscreen", | ||
"flags": { | ||
"withControlPanel": False, | ||
"form_view_initial_mode": "edit", | ||
"no_breadcrumbs": True, | ||
}, | ||
} | ||
|
||
def _get_interface_client_args(self): | ||
"""Prepare the arguments to instanciate the CubiScan client | ||
Can be overriden to change the parameters. | ||
Example, adding a ssl certificate:: | ||
args, kwargs = super()._get_interface_client_args() | ||
ctx = SSL.create_default_context() | ||
ctx.load_cert_chain("/usr/lib/ssl/certs/my_cert.pem") | ||
kwargs['ssl'] = ctx | ||
return args, kwargs | ||
Returns a 2 items tuple with: (args, kwargs) where args | ||
is a list and kwargs a dict. | ||
""" | ||
return ([self.device_address, self.port, self.timeout], {}) | ||
|
||
def _get_interface(self): | ||
"""Return the CubiScan client | ||
Can be overrided to customize the way it is instanciated | ||
""" | ||
self.ensure_one() | ||
args, kwargs = self._get_interface_client_args() | ||
return CubiScan(*args, **kwargs) | ||
|
||
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": | ||
device.state = "ready" | ||
elif res and "error" in res and device.state == "ready": | ||
device.state = "not_ready" | ||
|
||
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." | ||
) | ||
) | ||
return self._get_interface().measure() |
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 2019 Camptocamp SA | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
||
from odoo import fields, models | ||
|
||
|
||
class StockWarehouse(models.Model): | ||
_inherit = "stock.warehouse" | ||
|
||
cubiscan_device_ids = fields.One2many( | ||
"cubiscan.device", "warehouse_id", string="Cubiscan Devices" | ||
) | ||
|
||
|
||
class ProductPackaging(models.Model): | ||
_inherit = "product.packaging" | ||
# 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", | ||
"unique (product_id, packaging_type_id)", | ||
"It is forbidden to have different packagings " | ||
"with the same type for a given product.", | ||
) | ||
] |
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,4 @@ | ||
The first step is to configure the Packaging Types (Pallet, Box, ...) in Inventory > Configuration > Product Packaging Types. | ||
|
||
Configure the Cubiscan device in Inventory > Configuration > Cubiscan Devices. | ||
Use the "Test Device" to check the connection with the hardware. |
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 @@ | ||
* Patrick Tombez <patrick.tombez@camptocamp.com> |
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 @@ | ||
Cubiscan_ are dimensioners for cubing and weighing in warehouses. | ||
This module implements the communication with the dimensioners as well | ||
as a screen to measure and weight packaging of the products. | ||
|
||
.. _Cubiscan: https://cubiscan.com/ |
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 @@ | ||
* The UI could get some improvements | ||
* Being able to open the Cubiscan screen from a product would be nice | ||
* The wizard should allow to set weight and size for a single unit in addition to the packaging |
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 @@ | ||
Use the "Wizard" button on a Cubiscan device to open the screen and take | ||
measurements. | ||
|
||
For developers: a script in the directory ``scripts/cubiscan_stub.py`` allows | ||
to simulate a Cubiscan server and send random measurements. |
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,57 @@ | ||
#!/usr/bin/python3 | ||
# pylint: disable=print-used,attribute-deprecated | ||
"""Stub a Cubiscan server | ||
Allow testing the connection to Cubiscan from Odoo | ||
without real hardware. | ||
""" | ||
|
||
import asyncio | ||
import random | ||
|
||
|
||
@asyncio.coroutine | ||
def handle_cubiscan(reader, writer): | ||
message = yield from reader.readline() | ||
addr = writer.get_extra_info("peername") | ||
|
||
print("Received {!r} from {!r}".format(message, addr)) | ||
# print("Expecting {!r} from {!r}".format(message, addr)) | ||
print("{!r}".format(message == b"\x02M\x03\r\n")) | ||
if message == b"\x02M\x03\r\n": | ||
length = random.uniform(0, 1000) | ||
width = random.uniform(0, 1000) | ||
height = random.uniform(0, 1000) | ||
weight = random.uniform(0, 10000) | ||
answer = ( | ||
b"\x02MAH123456,L%05.1f,W%05.1f,H%05.1f,M,K%06.1f,D%06.1f,M,F0000,I\x03\r\n" | ||
% (length, width, height, weight, weight) | ||
) | ||
else: | ||
answer = b"\x02\x03\r\n" | ||
print("Send: {!r}".format(answer)) | ||
writer.write(answer) | ||
yield from writer.drain() | ||
|
||
|
||
def main(): | ||
loop = asyncio.get_event_loop() | ||
coro = asyncio.start_server(handle_cubiscan, "0.0.0.0", 9876, loop=loop) | ||
server = loop.run_until_complete(coro) | ||
|
||
# Serve requests until Ctrl+C is pressed | ||
addr = server.sockets[0].getsockname() | ||
print("Serving on {}".format(addr)) | ||
try: | ||
loop.run_forever() | ||
except KeyboardInterrupt: | ||
pass | ||
|
||
# Close the server | ||
server.close() | ||
loop.run_until_complete(server.wait_closed()) | ||
loop.close() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
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 @@ | ||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink | ||
access_cubiscan_device_inventory_manager,cubiscan.device.inventory.manager,stock_cubiscan.model_cubiscan_device,stock.group_stock_manager,1,1,1,1 | ||
access_cubiscan_device_inventory_user,cubiscan.device.inventory.user,stock_cubiscan.model_cubiscan_device,stock.group_stock_user,1,0,0,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,33 @@ | ||
.o_web_client.o_fullscreen { | ||
.o_form_view.cubiscan_wizard { | ||
font-size: 16px; | ||
|
||
@include media-breakpoint-up(x1) { | ||
font-size: 18px; | ||
} | ||
|
||
.btn { | ||
font-size: 1em; | ||
padding: 1em; | ||
margin: 0 5px; | ||
} | ||
|
||
.o_data_cell:not(.o_list_button) { | ||
padding: 0.75em; | ||
font-size: 1.5em; | ||
margin: 0 5px; | ||
} | ||
|
||
.table-responsive { | ||
overflow: hidden; | ||
} | ||
|
||
.o_field_many2one input.o_input { | ||
font-size: 1.5em; | ||
} | ||
|
||
.o_form_statusbar { | ||
display: none; | ||
} | ||
} | ||
} |
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 @@ | ||
from . import test_cubiscan | ||
from . import test_cubiscan_wizard |
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,33 @@ | ||
# 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 odoo.exceptions import ValidationError | ||
from odoo.tests.common import SavepointCase | ||
|
||
|
||
class TestCubiscan(SavepointCase): | ||
@classmethod | ||
def setUpClass(cls): | ||
super().setUpClass() | ||
cls.device_obj = cls.env["cubiscan.device"] | ||
|
||
def test_constraints(self): | ||
vals = {"name": "Test Device"} | ||
|
||
# Wrong port | ||
vals.update({"device_address": "10.10.0.42", "port": -42}) | ||
with self.assertRaises(ValidationError): | ||
self.device_obj.create(vals) | ||
|
||
def test_device_test(self): | ||
vals = {"name": "Test Device", "device_address": "10.10.0.42", "port": 5982} | ||
device = self.device_obj.create(vals) | ||
self.assertEquals(device.state, "not_ready") | ||
|
||
with patch.object(CubiScan, "_make_request") as mocked: | ||
mocked.return_value = {"identifier": 42} | ||
device.test_device() | ||
|
||
self.assertEquals(device.state, "ready") |
Oops, something went wrong.