Skip to content

Commit

Permalink
Merge PR #151 into 16.0
Browse files Browse the repository at this point in the history
Signed-off-by jbaudoux
  • Loading branch information
OCA-git-bot committed Nov 6, 2024
2 parents d452354 + 4858531 commit 6b42925
Show file tree
Hide file tree
Showing 19 changed files with 268 additions and 76 deletions.
9 changes: 9 additions & 0 deletions shipment_advice/models/res_company.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2021 Camptocamp SA
# Copyright 2024 Michael Tietz (MT Software) <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import fields, models
Expand Down Expand Up @@ -29,3 +30,11 @@ class ResCompany(models.Model):
"shipment advice validation through a queued jobs. Each picking will be "
"validated in a separate job.",
)
shipment_advice_auto_close_incoming = fields.Boolean(
string="Shipment Advice: Auto Close Incoming Advices",
help=(
"This flag indicates if an incoming shipment advice "
"will be automatically set to done "
"if all related moves are done or canceled"
),
)
4 changes: 4 additions & 0 deletions shipment_advice/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2021 Camptocamp SA
# Copyright 2024 Michael Tietz (MT Software) <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import fields, models
Expand All @@ -13,3 +14,6 @@ class ResConfigSettings(models.TransientModel):
shipment_advice_run_in_queue_job = fields.Boolean(
related="company_id.shipment_advice_run_in_queue_job", readonly=False
)
shipment_advice_auto_close_incoming = fields.Boolean(
related="company_id.shipment_advice_auto_close_incoming", readonly=False
)
34 changes: 29 additions & 5 deletions shipment_advice/models/shipment_advice.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2021 Camptocamp SA
# Copyright 2024 Michael Tietz (MT Software) <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import _, api, fields, models
Expand Down Expand Up @@ -58,7 +59,6 @@ def _default_warehouse_id(self):
string="Type",
default="outgoing",
required=True,
states={"draft": [("readonly", False)]},
readonly=True,
help="Use incoming to plan receptions, use outgoing for deliveries.",
)
Expand Down Expand Up @@ -254,7 +254,7 @@ def _compute_carrier_ids(self):

@api.model_create_multi
def create(self, vals_list):
defaults = self.default_get(["name"])
defaults = self.default_get(["name", "shipment_type"])
outgoing_sequence = self.env.ref(
"shipment_advice.shipment_advice_outgoing_sequence"
)
Expand All @@ -263,7 +263,7 @@ def create(self, vals_list):
)
for vals in vals_list:
sequence = outgoing_sequence
if vals["shipment_type"] == "incoming":
if vals.get("shipment_type", defaults["shipment_type"]) == "incoming":
sequence = incomig_sequence
if vals.get("name", "/") == "/" and defaults.get("name", "/") == "/":
vals["name"] = sequence.next_by_id()
Expand Down Expand Up @@ -314,6 +314,7 @@ def _lock_records(self, records):

def action_done(self):
self._check_action_done_allowed()
self = self.with_context(shipment_advice_ignore_auto_close=True)
for shipment in self:
shipment._action_done()
return True
Expand Down Expand Up @@ -417,8 +418,12 @@ def _postprocess_action_done(self):
}
)
return
if not self.departure_date:
self.departure_date = fields.Datetime.now()
self._close_shipments()

def _close_shipments(self):
for shipment in self:
if not shipment.departure_date:
shipment.departure_date = fields.Datetime.now()
self.write({"state": "done", "error_message": False})

@api.model
Expand All @@ -429,6 +434,25 @@ def _get_error_message(self, error, related_object):
error=str(error),
)

def auto_close_incoming_shipment_advices(self):
"""Set incoming shipment advice to done when all planned moves are processed"""
if self.env.context.get("shipment_advice_ignore_auto_close"):
return
shipment_ids_to_close = []
for shipment in self:
if (
shipment.shipment_type != "incoming"
or not shipment.company_id.shipment_advice_auto_close_incoming
or any(
move.state not in ("cancel", "done")
for move in shipment.planned_move_ids
)
):
continue
shipment_ids_to_close.append(shipment.id)
if shipment_ids_to_close:
self.browse(shipment_ids_to_close)._close_shipments()

def action_cancel(self):
for shipment in self:
if shipment.state not in ("confirmed", "in_progress"):
Expand Down
17 changes: 17 additions & 0 deletions shipment_advice/models/stock_move.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2021 Camptocamp SA
# Copyright 2024 Michael Tietz (MT Software) <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import fields, models
Expand All @@ -18,3 +19,19 @@ class StockMove(models.Model):
def _plan_in_shipment(self, shipment_advice):
"""Plan the moves into the given shipment advice."""
self.shipment_advice_id = shipment_advice

def _prepare_merge_moves_distinct_fields(self):
res = super()._prepare_merge_moves_distinct_fields()
# Avoid having stock move assign to different shipment merged together
res.append("shipment_advice_id")
return res

def _action_done(self, cancel_backorder=False):
res = super()._action_done(cancel_backorder=cancel_backorder)
res.shipment_advice_id.auto_close_incoming_shipment_advices()
return res

def _action_cancel(self):
res = super()._action_cancel()
self.shipment_advice_id.auto_close_incoming_shipment_advices()
return res
9 changes: 9 additions & 0 deletions shipment_advice/models/stock_picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ class StockPicking(models.Model):
"shipment.advice",
compute="_compute_loaded_in_shipment",
)
loaded_waiting_quantity = fields.Float(
"Waiting Quantity", compute="_compute_shipment_loaded_progress"
)

@api.depends("move_line_ids.shipment_advice_id")
def _compute_loaded_in_shipment(self):
Expand Down Expand Up @@ -157,6 +160,12 @@ def _compute_shipment_loaded_progress(self):
picking.loaded_weight_progress = (
f"{picking.loaded_weight} / {total_weight}"
)
waiting_moves = picking.move_ids.filtered(
lambda ml: ml.state not in ["done", "cancel"]
)
picking.loaded_waiting_quantity = sum(
waiting_moves.mapped("product_qty")
) - sum(waiting_moves.mapped("reserved_availability"))
# Overall progress based on the operation type
if picking.picking_type_id.show_entire_packs:
picking.loaded_progress_f = picking.loaded_packages_progress_f
Expand Down
1 change: 1 addition & 0 deletions shipment_advice/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Simone Orsi <[email protected]>
* `Trobz <https://trobz.com>`_:
* Dung Tran <[email protected]>
* Michael Tietz (MT Software) <[email protected]>

Design
~~~~~~
Expand Down
2 changes: 2 additions & 0 deletions shipment_advice/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
from . import test_shipment_advice_async
from . import test_shipment_advice_plan
from . import test_shipment_advice_load
from . import test_shipment_advice_picking_values
from . import test_shipment_advice_unload
from . import test_shipment_advice_stock_user
from . import test_shipment_advice_auto_close
66 changes: 50 additions & 16 deletions shipment_advice/tests/common.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Copyright 2021 Camptocamp SA
# Copyright 2024 Michael Tietz (MT Software) <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import fields
from odoo.tests.common import TransactionCase, new_test_user
from odoo.tests.common import Form, TransactionCase, new_test_user


class Common(TransactionCase):
Expand Down Expand Up @@ -124,30 +125,33 @@ def _check_sequence(self, shipment_advice):
else:
self.assertIn("IN", shipment_advice.name)

def _confirm_shipment_advice(self, shipment_advice, arrival_date=None):
@classmethod
def confirm_shipment_advice(cls, shipment_advice, arrival_date=None):
if shipment_advice.state != "draft":
return
if arrival_date is None:
arrival_date = fields.Datetime.now()
shipment_advice.arrival_date = arrival_date
shipment_advice.action_confirm()
self.assertEqual(shipment_advice.state, "confirmed")

def _in_progress_shipment_advice(self, shipment_advice, dock=None):
self._confirm_shipment_advice(shipment_advice)
@classmethod
def progress_shipment_advice(cls, shipment_advice, dock=None):
cls.confirm_shipment_advice(shipment_advice)
if shipment_advice.state != "confirmed":
return
shipment_advice.dock_id = dock or self.dock
shipment_advice.dock_id = dock or cls.dock
shipment_advice.action_in_progress()
self.assertEqual(shipment_advice.state, "in_progress")

def _cancel_shipment_advice(self, shipment_advice, dock=None):
self._confirm_shipment_advice(shipment_advice)
@classmethod
def cancel_shipment_advice(cls, shipment_advice, dock=None):
cls.confirm_shipment_advice(shipment_advice)
if shipment_advice.state != "confirmed":
return
shipment_advice.action_cancel()
self.assertEqual(shipment_advice.state, "cancel")

def _plan_records_in_shipment(self, shipment_advice, records, user=None):
wiz_model = self.env["wizard.plan.shipment"].with_context(
@classmethod
def plan_records_in_shipment(cls, shipment_advice, records, user=None):
wiz_model = cls.env["wizard.plan.shipment"].with_context(
active_model=records._name,
active_ids=records.ids,
)
Expand All @@ -157,9 +161,22 @@ def _plan_records_in_shipment(self, shipment_advice, records, user=None):
wiz.action_plan()
return wiz

def _load_records_in_shipment(self, shipment_advice, records, user=None):
@classmethod
def unplan_records_from_shipment(cls, records, user=None):
wiz_model = cls.env["wizard.unplan.shipment"].with_context(
active_model=records._name,
active_ids=records.ids,
)
wiz = wiz_model.create({})
if user:
wiz = wiz.with_user(user)
wiz.action_unplan()
return wiz

@classmethod
def load_records_in_shipment(cls, shipment_advice, records, user=None):
"""Load pickings, move lines or package levels in the givent shipment."""
wiz_model = self.env["wizard.load.shipment"].with_context(
wiz_model = cls.env["wizard.load.shipment"].with_context(
active_model=records._name,
active_ids=records.ids,
)
Expand All @@ -169,12 +186,29 @@ def _load_records_in_shipment(self, shipment_advice, records, user=None):
wiz.action_load()
return wiz

def _unload_records_from_shipment(self, shipment_advice, records):
@classmethod
def unload_records_from_shipment(cls, shipment_advice, records):
"""Unload pickings, move lines or package levels from the givent shipment."""
wiz_model = self.env["wizard.unload.shipment"].with_context(
wiz_model = cls.env["wizard.unload.shipment"].with_context(
active_model=records._name,
active_ids=records.ids,
)
wiz = wiz_model.create({})
wiz.action_unload()
return wiz

@classmethod
def validate_picking(cls, picking, qty_done=None):
picking.ensure_one()
for ml in picking.move_line_ids:
ml.qty_done = qty_done or ml.reserved_uom_qty
action_data = picking.button_validate()
if action_data is True:
return cls.env["stock.picking"]
backorder_wizard = Form(
cls.env["stock.backorder.confirmation"].with_context(
**action_data["context"]
)
).save()
backorder_wizard.process()
return cls.env["stock.picking"].search([("backorder_id", "=", picking.id)])
30 changes: 17 additions & 13 deletions shipment_advice/tests/test_shipment_advice.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_shipment_advice_confirm(self):
self.shipment_advice_out.action_confirm()

def test_shipment_advice_in_progress(self):
self._confirm_shipment_advice(self.shipment_advice_out)
self.confirm_shipment_advice(self.shipment_advice_out)
with self.assertRaises(UserError):
self.shipment_advice_out.action_in_progress()
self.shipment_advice_out.dock_id = self.dock
Expand All @@ -41,8 +41,8 @@ def test_shipment_advice_incoming_done_full(self):
with self.assertRaises(UserError):
self.shipment_advice_in.action_done()
self._check_sequence(self.shipment_advice_in)
self._plan_records_in_shipment(self.shipment_advice_in, picking)
self._in_progress_shipment_advice(self.shipment_advice_in)
self.plan_records_in_shipment(self.shipment_advice_in, picking)
self.progress_shipment_advice(self.shipment_advice_in)
for ml in picking.move_line_ids:
ml.qty_done = ml.reserved_uom_qty
picking._action_done()
Expand All @@ -62,8 +62,8 @@ def test_shipment_advice_incoming_done_partial(self):
"""
picking = self.move_product_in1.picking_id
# Plan a move
self._plan_records_in_shipment(self.shipment_advice_in, self.move_product_in1)
self._in_progress_shipment_advice(self.shipment_advice_in)
self.plan_records_in_shipment(self.shipment_advice_in, self.move_product_in1)
self.progress_shipment_advice(self.shipment_advice_in)
# Receive it (making its related transfer partially received)
for ml in self.move_product_in1.move_line_ids:
ml.qty_done = ml.reserved_uom_qty
Expand All @@ -82,8 +82,8 @@ def test_shipment_advice_done_full(self):
validate all fully loaded transfers.
"""
picking = self.move_product_out1.picking_id
self._in_progress_shipment_advice(self.shipment_advice_out)
self._load_records_in_shipment(self.shipment_advice_out, picking)
self.progress_shipment_advice(self.shipment_advice_out)
self.load_records_in_shipment(self.shipment_advice_out, picking)
self.shipment_advice_out.action_done()
self.assertEqual(self.shipment_advice_out.state, "done")
self.assertTrue(
Expand All @@ -103,8 +103,8 @@ def test_shipment_advice_done_backorder_policy_disabled(self):
company.shipment_advice_outgoing_backorder_policy = "leave_open"
# Load a transfer partially (here a package)
package_level = self.move_product_out2.move_line_ids.package_level_id
self._in_progress_shipment_advice(self.shipment_advice_out)
self._load_records_in_shipment(self.shipment_advice_out, package_level)
self.progress_shipment_advice(self.shipment_advice_out)
self.load_records_in_shipment(self.shipment_advice_out, package_level)
# Validate the shipment => the transfer is still open
self.shipment_advice_out.action_done()
picking = package_level.picking_id
Expand All @@ -127,8 +127,8 @@ def test_shipment_advice_done_backorder_policy_enabled(self):
company.shipment_advice_outgoing_backorder_policy = "create_backorder"
# Load a transfer partially (here a package)
package_level = self.move_product_out2.move_line_ids.package_level_id
self._in_progress_shipment_advice(self.shipment_advice_out)
self._load_records_in_shipment(self.shipment_advice_out, package_level)
self.progress_shipment_advice(self.shipment_advice_out)
self.load_records_in_shipment(self.shipment_advice_out, package_level)
self.assertEqual(package_level.picking_id, self.move_product_out1.picking_id)
# Validate the shipment => the transfer is validated, creating a backorder
self.shipment_advice_out.action_done()
Expand All @@ -148,7 +148,7 @@ def test_shipment_advice_done_backorder_policy_enabled(self):
self.assertEqual(picking2.state, "assigned")

def test_shipment_advice_cancel(self):
self._in_progress_shipment_advice(self.shipment_advice_out)
self.progress_shipment_advice(self.shipment_advice_out)
self.shipment_advice_out.action_cancel()
self.assertEqual(self.shipment_advice_out.state, "cancel")

Expand All @@ -160,6 +160,10 @@ def test_shipment_advice_cancel_error(self):
def test_shipment_advice_draft(self):
with self.assertRaises(UserError):
self.shipment_advice_out.action_draft()
self._cancel_shipment_advice(self.shipment_advice_out)
self.cancel_shipment_advice(self.shipment_advice_out)
self.shipment_advice_out.action_draft()
self.assertEqual(self.shipment_advice_out.state, "draft")

def test_shipment_name(self):
self.assertTrue("OUT" in self.shipment_advice_out.name)
self.assertTrue("IN" in self.shipment_advice_in.name)
Loading

0 comments on commit 6b42925

Please sign in to comment.