Skip to content

Commit

Permalink
stock_reception_screen_measuring_device: measure smaller packages
Browse files Browse the repository at this point in the history
When goods are received, triggers measurements also for smaller
packagings that weren't ordered or received.

This behavior is not triggered when received packagings are not the same
as ordered packagings.
  • Loading branch information
mmequignon committed Nov 5, 2024
1 parent c114af4 commit 84dbac6
Show file tree
Hide file tree
Showing 9 changed files with 505 additions and 15 deletions.
2 changes: 1 addition & 1 deletion stock_reception_screen_measuring_device/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"name": "Stock Measuring Device on Reception Screen",
"summary": "Allow to use a measuring device from a reception screen."
"for packaging measurement",
"version": "14.0.1.0.0",
"version": "14.0.2.0.0",
"category": "Warehouse",
"author": "Camptocamp, Odoo Community Association (OCA)",
"license": "AGPL-3",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2024 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

import logging

from odoo import SUPERUSER_ID, api

_logger = logging.getLogger(__name__)


def populate_new_field(env):
records_to_update = (
env["stock.reception.screen"].search([("current_step", "!=", "done")]).exists()
)
_logger.info(
"Set smaller_package_has_missing_dimensions on ongoing reception screens"
)
records_to_update._compute_smaller_package_has_missing_dimensions()


def migrate(cr, version):
if not version:
return
env = api.Environment(cr, SUPERUSER_ID, {})
populate_new_field(env)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

import logging

_logger = logging.getLogger(__name__)


def create_and_populate_new_fields(cr):
cr.execute(
"""
ALTER TABLE stock_reception_screen
ADD COLUMN IF NOT EXISTS smaller_package_has_missing_dimensions BOOLEAN;
"""
)
# Set value to False for done reception screens.
# Otherwise, let the ORM do its job in post migration
_logger.info("Set smaller_package_has_missing_dimensions on done reception screens")
cr.execute(
"""
UPDATE stock_reception_screen
SET smaller_package_has_missing_dimensions = FALSE
WHERE current_step = 'done';
"""
)


def migrate(cr, version):
create_and_populate_new_fields(cr)
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ class StockReceptionScreen(models.Model):
store=True,
help="Indicates if the package have any measurement missing.",
)
smaller_package_has_missing_dimensions = fields.Boolean(
"Smaller Package Requires Measures?",
compute="_compute_smaller_package_has_missing_dimensions",
store=True,
help="Indicates if any smaller package have any measurement missing.",
)
display_package_dimensions = fields.Char(
string="Dimensions (lxhxw)",
compute="_compute_package_dimensions",
Expand All @@ -30,13 +36,18 @@ class StockReceptionScreen(models.Model):
store=True,
)

@api.depends("product_packaging_id", "product_packaging_id.measuring_device_id")
@api.depends(
"current_move_product_id.packaging_ids.measuring_device_id",
)
def _compute_scan_requested(self):
for record in self:
record.scan_requested = (
record.product_packaging_id
and record.product_packaging_id.measuring_device_id
)
# TODO
all_product_packagings = record.current_move_product_id.packaging_ids
record.scan_requested = False
for packaging in all_product_packagings:
if packaging.measuring_device_id:
record.scan_requested = True
break

@api.depends(
"product_packaging_id.packaging_length",
Expand All @@ -54,6 +65,20 @@ def _compute_package_dimensions(self):
else:
record.display_package_dimensions = False

@api.depends(
"product_packaging_id",
"product_packaging_id.qty",
"current_move_product_id.packaging_ids.max_weight",
"current_move_product_id.packaging_ids.packaging_length",
"current_move_product_id.packaging_ids.width",
"current_move_product_id.packaging_ids.height",
)
def _compute_smaller_package_has_missing_dimensions(self):
for record in self:
record.smaller_package_has_missing_dimensions = bool(
record._get_smaller_package_without_dimensions()
)

@api.depends(
"product_packaging_id.max_weight",
"product_packaging_id.packaging_length",
Expand All @@ -71,29 +96,63 @@ def _compute_package_has_missing_dimensions(self):
else:
record.package_has_missing_dimensions = False

def measure_current_packaging(self):
self.ensure_one()
@api.model
def _measure_packaging(self, packaging):
device = self.env["measuring.device"].search(
[("is_default", "=", True)], limit=1
)
if not device:
error_msg = _("No default device set, please configure one.")
_logger.error(error_msg)
self._notify(error_msg)
return UserError(error_msg)
raise UserError(error_msg)
if device._is_being_used():
error_msg = _("Measurement machine already in use.")
_logger.error(error_msg)
self._notify(error_msg)
return UserError(error_msg)
raise UserError(error_msg)

self.product_packaging_id._measuring_device_assign(device)
packaging._measuring_device_assign(device)
return True

def measure_current_packaging(self):
self.ensure_one()
return self._measure_packaging(self.product_packaging_id)

def _get_smaller_package_without_dimensions_domain(self):
self.ensure_one()
# TODO ordered package
return [
("product_id", "=", self.current_move_product_id.id),
("qty", "<", self.product_packaging_id.qty),
"|",
"|",
"|",
("packaging_length", "=", 0.0),
("width", "=", 0.0),
("height", "=", 0.0),
("max_weight", "=", 0.0),
]

def _get_smaller_package_without_dimensions(self):
self.ensure_one()
domain = self._get_smaller_package_without_dimensions_domain()
return self.env["product.packaging"].search(domain, order="qty desc", limit=1)

def measure_smaller_packaging(self):
self.ensure_one()
pack_without_dimensions = self._get_smaller_package_without_dimensions()
if not pack_without_dimensions:
error_msg = _("No available packaging without measurements.")
raise UserError(error_msg)
return self._measure_packaging(pack_without_dimensions)

def cancel_measure_current_packaging(self):
self.ensure_one()
self.product_packaging_id._measuring_device_release()
return True
assigned_packaging = self.current_move_product_id.packaging_ids.filtered(
lambda p: p.measuring_device_id
)
assigned_packaging._measuring_device_release()

def _notify(self, message):
"""Show a gentle notification on the wizard"""
Expand Down
1 change: 1 addition & 0 deletions stock_reception_screen_measuring_device/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_reception_screen_measurement
14 changes: 14 additions & 0 deletions stock_reception_screen_measuring_device/tests/fake_components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2024 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo.addons.component.core import Component


class FakeDevice(Component):
_name = "device.component.fake"
_inherit = "measuring.device.base"
_usage = "fake"

def post_update_packaging_measures(self, measures, packaging, wizard_line):
# Unassign measuring device when measuring is done
packaging._measuring_device_release()
14 changes: 14 additions & 0 deletions stock_reception_screen_measuring_device/tests/fake_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2024 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import fields, models


class FakeMeasuringDevice(models.Model):
_inherit = "measuring.device"

device_type = fields.Selection(selection_add=[("fake", "FAKE")])

def mocked_measure(self, measurements):
self.ensure_one()
self._update_packaging_measures(measurements)
Loading

0 comments on commit 84dbac6

Please sign in to comment.