117 lines
4.2 KiB
Python
Executable File
117 lines
4.2 KiB
Python
Executable File
# Copyright 2018 Akretion (http://www.akretion.com).
|
|
# @author Pierrick BRUN <pierrick.brun@akretion.com>
|
|
# Copyright 2018 Camptocamp
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
# Copyright 2023 Manuel Regidor <manuel.regidor@sygel.es>
|
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
|
|
|
from odoo import api, fields, models
|
|
from odoo.tools import float_compare, float_is_zero
|
|
|
|
|
|
class SaleOrder(models.Model):
|
|
_inherit = "sale.order"
|
|
|
|
delivery_status = fields.Selection(
|
|
# Compute method have a different name then the field because
|
|
# the method _compute_delivery_status already exist in odoo sale_stock
|
|
compute="_compute_oca_delivery_status",
|
|
store=True,
|
|
# Respect the same order as in sale_stock
|
|
# Including the 'started' state
|
|
# that is not used here but we compute it
|
|
# if pickings are available, to be compatible.
|
|
selection=[
|
|
("pending", "Not Delivered"),
|
|
("started", "Started"),
|
|
("partial", "Partially Delivered"),
|
|
("full", "Fully Delivered"),
|
|
],
|
|
)
|
|
|
|
force_delivery_state = fields.Boolean(
|
|
help=(
|
|
"Allow to enforce done state of delivery, for instance if some"
|
|
" quantities were cancelled"
|
|
),
|
|
)
|
|
|
|
def _all_qty_delivered(self):
|
|
"""
|
|
Returns True if all line have qty_delivered >= to ordered quantities
|
|
|
|
If `delivery` module is installed, ignores the lines with delivery costs
|
|
|
|
:returns: boolean
|
|
"""
|
|
self.ensure_one()
|
|
# Skip delivery costs lines
|
|
sale_lines = self.order_line.filtered(
|
|
lambda rec: not rec._is_delivery() and not rec.skip_sale_delivery_state
|
|
)
|
|
precision = self.env["decimal.precision"].precision_get(
|
|
"Product Unit of Measure"
|
|
)
|
|
return all(
|
|
float_compare(
|
|
line.qty_delivered, line.product_uom_qty, precision_digits=precision
|
|
)
|
|
>= 0
|
|
for line in sale_lines
|
|
)
|
|
|
|
def _partially_delivered(self):
|
|
"""
|
|
Returns True if at least one line is delivered
|
|
|
|
:returns: boolean
|
|
"""
|
|
self.ensure_one()
|
|
# Skip delivery costs lines
|
|
sale_lines = self.order_line.filtered(
|
|
lambda rec: not rec._is_delivery() and not rec.skip_sale_delivery_state
|
|
)
|
|
precision = self.env["decimal.precision"].precision_get(
|
|
"Product Unit of Measure"
|
|
)
|
|
return any(
|
|
not float_is_zero(line.qty_delivered, precision_digits=precision)
|
|
for line in sale_lines
|
|
)
|
|
|
|
@api.depends(
|
|
"order_line.qty_delivered",
|
|
"order_line.skip_sale_delivery_state",
|
|
"state",
|
|
"force_delivery_state",
|
|
)
|
|
def _compute_oca_delivery_status(self):
|
|
for order in self:
|
|
if order.state in ("draft", "cancel"):
|
|
order.delivery_status = None
|
|
elif order.force_delivery_state or order._all_qty_delivered():
|
|
order.delivery_status = "full"
|
|
elif order._partially_delivered():
|
|
order.delivery_status = "partial"
|
|
elif order._is_delivery_status_started():
|
|
order.delivery_status = "started"
|
|
else:
|
|
order.delivery_status = "pending"
|
|
|
|
def _is_delivery_status_started(self):
|
|
# Loose dep on sale_stock. Feel free to customize this method
|
|
# to add your own logic or to create sale_stock glue module.
|
|
# NOTE: as the delivery_status is stored the update of a picking
|
|
# won't have any effect here. Hence, if you really want to
|
|
# fully support the started state, you should trigger the update
|
|
# of the sale order when a picking is updated.
|
|
# For now, we don't care that much as this state was not used before.
|
|
has_pickings = "picking_ids" in self._fields
|
|
return has_pickings and any(p.state == "done" for p in self.picking_ids)
|
|
|
|
def action_force_delivery_state(self):
|
|
self.write({"force_delivery_state": True})
|
|
|
|
def action_unforce_delivery_state(self):
|
|
self.write({"force_delivery_state": False})
|