Skip to content

Commit

Permalink
Merge PR #132 into 14.0
Browse files Browse the repository at this point in the history
Signed-off-by jbaudoux
  • Loading branch information
OCA-git-bot committed Jul 12, 2024
2 parents 42e830b + 2224ba2 commit e7cfa7f
Show file tree
Hide file tree
Showing 19 changed files with 252 additions and 128 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 All @@ -23,3 +24,11 @@ class ResCompany(models.Model):
"deliveries will be shipped by several trucks."
),
)
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 @@ -10,3 +11,6 @@ class ResConfigSettings(models.TransientModel):
shipment_advice_outgoing_backorder_policy = fields.Selection(
related="company_id.shipment_advice_outgoing_backorder_policy", readonly=False
)
shipment_advice_auto_close_incoming = fields.Boolean(
related="company_id.shipment_advice_auto_close_incoming", readonly=False
)
113 changes: 66 additions & 47 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 @@ -379,63 +380,81 @@ def _lock_records(self, records):
sql = "SELECT id FROM %s WHERE ID IN %%s FOR UPDATE" % records._table
self.env.cr.execute(sql, (tuple(records.ids),), log_exceptions=False)

def action_done(self):
def _close_pickings(self):
"""Validate transfers (create backorders for unprocessed lines)"""
self.ensure_one()
wiz_model = self.env["stock.backorder.confirmation"]
pickings = self.env["stock.picking"]
create_backorder = True
if self.shipment_type == "incoming":
self._lock_records(self.planned_picking_ids)
pickings = self.planned_picking_ids
else:
self._lock_records(self.loaded_picking_ids)
pickings = self.to_validate_picking_ids
create_backorder = (
self.company_id.shipment_advice_outgoing_backorder_policy
== "create_backorder"
)
for picking in pickings:
if picking.state in ("cancel", "done"):
continue
if picking._check_backorder():
if not create_backorder:
continue
wiz = wiz_model.create({})
wiz.pick_ids = picking
wiz.with_context(button_validate_picking_ids=picking.ids).process()
else:
picking._action_done()

def _unplan_loaded_moves(self):
"""Unplan moves that were not loaded and validated"""
moves_to_unplan = self.loaded_move_line_ids.move_id.filtered(
lambda m: m.state not in ("cancel", "done") and not m.quantity_done
)
moves_to_unplan.shipment_advice_id = False

def action_done(self):
shipment_advice_ids_to_validate = []
self = self.with_context(shipment_advice_ignore_auto_close=True)
for shipment in self:
if shipment.state != "in_progress":
raise UserError(
_("Shipment {} is not started, operation aborted.").format(
shipment.name
)
)
# Validate transfers (create backorders for unprocessed lines)
if shipment.shipment_type == "incoming":
self._lock_records(self.planned_picking_ids)
for picking in self.planned_picking_ids:
if picking.state in ("cancel", "done"):
continue
if picking._check_backorder():
wiz = wiz_model.create({})
wiz.pick_ids = picking
wiz.with_context(
button_validate_picking_ids=picking.ids
).process()
else:
picking._action_done()
else:
backorder_policy = (
shipment.company_id.shipment_advice_outgoing_backorder_policy
)
self._lock_records(self.loaded_picking_ids)
if backorder_policy == "create_backorder":
for picking in self.to_validate_picking_ids:
if picking.state in ("cancel", "done"):
continue
if picking._check_backorder():
wiz = wiz_model.create({})
wiz.pick_ids = picking
wiz.with_context(
button_validate_picking_ids=picking.ids
).process()
else:
picking._action_done()
else:
for picking in self.to_validate_picking_ids:
if picking.state in ("cancel", "done"):
continue
if not picking._check_backorder():
# no backorder needed means that all qty_done are
# set to fullfill the need => validate
picking._action_done()
# Unplan moves that were not loaded and validated
moves_to_unplan = self.loaded_move_line_ids.move_id.filtered(
lambda m: m.state not in ("cancel", "done") and not m.quantity_done
)
moves_to_unplan.shipment_advice_id = False
shipment.departure_date = fields.Datetime.now()
shipment.state = "done"
shipment._close_pickings()
if shipment.shipment_type == "outgoing":
shipment._unplan_loaded_moves()
shipment_advice_ids_to_validate.append(shipment.id)
if shipment_advice_ids_to_validate:
self.browse(shipment_advice_ids_to_validate)._action_done()
return True

def _action_done(self):
self.write({"departure_date": fields.Datetime.now(), "state": "done"})

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)._action_done()

def action_cancel(self):
for shipment in self:
if shipment.state not in ("confirmed", "in_progress"):
Expand Down
11 changes: 11 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 @@ -23,3 +24,13 @@ def _prepare_merge_moves_distinct_fields(self):
# 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
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
1 change: 1 addition & 0 deletions shipment_advice/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
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
55 changes: 37 additions & 18 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 SavepointCase, new_test_user
from odoo.tests.common import Form, SavepointCase, new_test_user


class Common(SavepointCase):
Expand Down Expand Up @@ -118,32 +119,33 @@ def _create_move(cls, picking_type, product, quantity, group=False):
move.picking_id.action_assign()
return move

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 @@ -153,8 +155,9 @@ def _plan_records_in_shipment(self, shipment_advice, records, user=None):
wiz.action_plan()
return wiz

def _unplan_records_from_shipment(self, records, user=None):
wiz_model = self.env["wizard.unplan.shipment"].with_context(
@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,
)
Expand All @@ -164,9 +167,10 @@ def _unplan_records_from_shipment(self, records, user=None):
wiz.action_unplan()
return wiz

def _load_records_in_shipment(self, shipment_advice, records, user=None):
@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 @@ -176,12 +180,27 @@ 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.product_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)])
Loading

0 comments on commit e7cfa7f

Please sign in to comment.