-
-
Notifications
You must be signed in to change notification settings - Fork 126
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 jbaudoux
- Loading branch information
Showing
19 changed files
with
252 additions
and
128 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 |
---|---|---|
@@ -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 | ||
|
@@ -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" | ||
), | ||
) |
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 |
---|---|---|
@@ -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 | ||
|
@@ -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 | ||
) |
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 |
---|---|---|
@@ -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 | ||
|
@@ -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"): | ||
|
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 |
---|---|---|
@@ -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 | ||
|
@@ -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 |
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 |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
* Simone Orsi <[email protected]> | ||
* `Trobz <https://trobz.com>`_: | ||
* Dung Tran <[email protected]> | ||
* Michael Tietz (MT Software) <[email protected]> | ||
|
||
Design | ||
~~~~~~ | ||
|
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
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 |
---|---|---|
@@ -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): | ||
|
@@ -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, | ||
) | ||
|
@@ -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, | ||
) | ||
|
@@ -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, | ||
) | ||
|
@@ -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)]) |
Oops, something went wrong.