Skip to content

Commit

Permalink
Merge PR #38 into 13.0
Browse files Browse the repository at this point in the history
Signed-off-by guewen
  • Loading branch information
OCA-git-bot committed Aug 27, 2020
2 parents 3aa9db4 + cb74d57 commit 8b7b7d7
Show file tree
Hide file tree
Showing 36 changed files with 663 additions and 573 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ Sale Delivery Carrier Preference
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fwms-lightgray.png?logo=github
:target: https://github.com/OCA/wms/tree/13.0/sale_delivery_carrier_preference
:target: https://github.com/OCA/wms/tree/13.0/delivery_carrier_preference
:alt: OCA/wms
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/wms-13-0/wms-13-0-sale_delivery_carrier_preference
:target: https://translation.odoo-community.org/projects/wms-13-0/wms-13-0-delivery_carrier_preference
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/285/13.0
:alt: Try me on Runbot

|badge1| |badge2| |badge3| |badge4| |badge5|
|badge1| |badge2| |badge3| |badge4| |badge5|

This module allows to define preferred shipping methods for sales in order
to fine tune the selection of proper delivery carrier according to sale order
Expand Down Expand Up @@ -58,7 +58,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/wms/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/wms/issues/new?body=module:%20sale_delivery_carrier_preference%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
`feedback <https://github.com/OCA/wms/issues/new?body=module:%20delivery_carrier_preference%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Expand Down Expand Up @@ -88,6 +88,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/wms <https://github.com/OCA/wms/tree/13.0/sale_delivery_carrier_preference>`_ project on GitHub.
This module is part of the `OCA/wms <https://github.com/OCA/wms/tree/13.0/delivery_carrier_preference>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
File renamed without changes.
26 changes: 26 additions & 0 deletions delivery_carrier_preference/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
{
"name": "Delivery Carrier Preference",
"summary": "Advanced selection of preferred shipping methods",
"version": "13.0.1.2.0",
"category": "Operations/Inventory/Delivery",
"website": "https://github.com/OCA/wms",
"author": "Camptocamp, Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": False,
"installable": True,
"depends": [
"delivery",
"product_total_weight_from_packaging",
"stock_available_to_promise_release",
"stock_picking_group_by_partner_by_carrier",
],
"data": [
"security/ir.model.access.csv",
"views/delivery_carrier_preference.xml",
"views/stock_move.xml",
"views/stock_picking.xml",
"views/stock_location_route.xml",
],
}
5 changes: 5 additions & 0 deletions delivery_carrier_preference/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from . import delivery_carrier_preference
from . import procurement_group
from . import stock_move
from . import stock_picking
from . import stock_location_route
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools import float_compare


class SaleDeliveryCarrierPreference(models.Model):
class DeliveryCarrierPreference(models.Model):

_name = "sale.delivery.carrier.preference"
_name = "delivery.carrier.preference"
_description = "Preferred Shipping Methods"
_order = "sequence, id"

Expand All @@ -18,12 +19,26 @@ class SaleDeliveryCarrierPreference(models.Model):
default="carrier",
)
carrier_id = fields.Many2one("delivery.carrier", ondelete="cascade")
sale_order_max_weight = fields.Float(
"Sale order max weight (kg)", help="Leave empty for no limit"
max_weight = fields.Float("Max weight", help="Leave empty for no limit")
max_weight_uom_id = fields.Many2one(
"uom.uom",
compute="_compute_max_weight_uom_id",
readonly=True,
default=lambda p: p._default_max_weight_uom_id(),
)
max_weight_uom_name = fields.Char(
string="Max weight UOM",
related="max_weight_uom_id.display_name",
readonly=True,
)
company_id = fields.Many2one(
"res.company", required=True, default=lambda self: self.env.company
)
picking_domain = fields.Char(
default="[]",
help="Domain to restrict application of this preference "
"for carrier selection on pickings",
)

@api.constrains("preference", "carrier_id")
def _check_preference_carrier_id(self):
Expand All @@ -46,15 +61,19 @@ def _check_preference_carrier_id(self):
)
)

@api.constrains("sale_order_max_weight")
def _check_sale_order_max_weight(self):
@api.constrains("max_weight")
def _check_max_weight(self):
for pref in self:
if pref.sale_order_max_weight < 0:
if (
float_compare(
pref.max_weight,
0,
precision_rounding=pref.max_weight_uom_id.rounding,
)
< 0
):
raise ValidationError(
_(
"Sale order max weight (kg) must have a positive or "
"null value."
)
_("Max weight must have a positive or null value.")
)

@api.onchange("preference")
Expand All @@ -63,7 +82,7 @@ def onchange_preference(self):
if self.preference == "partner" and self.carrier_id:
self.carrier_id = False

@api.depends("preference", "carrier_id", "sale_order_max_weight")
@api.depends("preference", "carrier_id", "max_weight")
def _compute_name(self):
pref_descr = {
k: v for k, v in self._fields["preference"]._description_selection(self.env)
Expand All @@ -72,34 +91,19 @@ def _compute_name(self):
name = pref_descr.get(pref.preference)
if pref.carrier_id:
name = _("%s: %s") % (name, pref.carrier_id.name)
if pref.sale_order_max_weight:
name = _("%s (Max weight %s kg)") % (name, pref.sale_order_max_weight)
if pref.max_weight:
name = _("%s (Max weight %s %s)") % (
name,
pref.max_weight,
pref.max_weight_uom_id.display_name,
)
pref.name = name

@api.model
def get_preferred_carriers(self, order):
wiz = self.env["choose.delivery.carrier"].new({"order_id": order.id})
carrier_preferences = self.env["sale.delivery.carrier.preference"].search(
[
"&",
"|",
("sale_order_max_weight", ">=", order.shipping_weight),
("sale_order_max_weight", "=", 0.0),
"|",
("carrier_id", "in", wiz.available_carrier_ids.ids),
("carrier_id", "=", False),
]
)
carriers_ids = list()
for cp in carrier_preferences:
if cp.preference == "carrier":
carriers_ids.append(cp.carrier_id.id)
else:
partner_carrier = order.partner_shipping_id.property_delivery_carrier_id
if partner_carrier:
carriers_ids.append(partner_carrier.id)
return (
self.env["delivery.carrier"]
.browse(carriers_ids)
.available_carriers(order.partner_shipping_id)
)
def _default_max_weight_uom_id(self):
return self.env[
"product.template"
]._get_weight_uom_id_from_ir_config_parameter()

def _compute_max_weight_uom_id(self):
for pref in self:
pref.max_weight_uom_id = self._default_max_weight_uom_id().id
11 changes: 11 additions & 0 deletions delivery_carrier_preference/models/procurement_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import fields, models


class ProcurementGroup(models.Model):
_inherit = "procurement.group"

picking_ids = fields.One2many(
comodel_name="stock.picking", inverse_name="group_id", readonly=True
)
14 changes: 14 additions & 0 deletions delivery_carrier_preference/models/stock_location_route.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import fields, models


class StockLocationRoute(models.Model):

_inherit = "stock.location.route"

force_recompute_preferred_carrier_on_release = fields.Boolean(
string="Force recomputation of preferred carrier.",
help="Mark this box to trigger a recomputation of preferred carrier on"
" the release of operations.",
)
100 changes: 100 additions & 0 deletions delivery_carrier_preference/models/stock_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from itertools import groupby

from odoo import api, fields, models, tools


class StockMove(models.Model):

_inherit = "stock.move"

estimated_shipping_weight = fields.Float(
string="Estimated shipping weight",
compute="_compute_estimated_shipping_weight",
help="Total weight available to promise calculated according to the"
" quantity available to promise and weight defined on packagings "
"for this product.",
)

@api.depends(
"product_id",
"product_id.packaging_ids",
"product_id.packaging_ids.max_weight",
"product_id.weight",
"ordered_available_to_promise",
)
def _compute_estimated_shipping_weight(self):
for move in self:
prod = move.product_id
move.estimated_shipping_weight = prod.get_total_weight_from_packaging(
move.ordered_available_to_promise
)

def _get_new_picking_values(self):
vals = super()._get_new_picking_values()
# Take the carrier_id from the group only when we have a related line
# (i.e. we are in an OUT). It reflects the code of the super method in
# "delivery" which takes the carrier of the related SO through SO line
if self.sale_line_id:
group_carrier = self.mapped("group_id.carrier_id")
if group_carrier:
vals["carrier_id"] = group_carrier.id
return vals

@staticmethod
def _filter_recompute_preferred_carrier(move):
precision = move.env["decimal.precision"].precision_get(
"Product Unit of Measure"
)
return (
move.need_release
# do not change the carrier is nothing can be released on the stock move
and not tools.float_is_zero(
move._ordered_available_to_promise(), precision_digits=precision
)
and move.rule_id.route_id.force_recompute_preferred_carrier_on_release
)

def release_available_to_promise(self):
modified_groups = {}
for picking in self.filtered(self._filter_recompute_preferred_carrier).mapped(
"picking_id"
):
if picking.picking_type_code != "outgoing":
continue
picking.add_preferred_carrier()

# if we have other pickings in the same group and now they have different
# carriers, split them in 2 groups and sync the carrier on their group
sorted_pickings = self.mapped("picking_id").sorted(
lambda pick: (pick.group_id, pick.carrier_id)
)
for (group, new_carrier), iter_pickings in groupby(
sorted_pickings, lambda pick: (pick.group_id, pick.carrier_id)
):
pickings = self.env["stock.picking"].union(*iter_pickings)
if group.carrier_id != new_carrier:
# always create a new procurement group when we change carrier,
# the old group will be reassigned to the backorders if any,
# otherwise it will stay empty in the depths of nothingness
new_group = group.copy(
default={"name": "{} ({})".format(group.name, new_carrier.name)}
)
pickings.move_lines.group_id = new_group

# sync carrier
new_group.carrier_id = new_carrier
modified_groups[new_group] = group

res = super().release_available_to_promise()

for new_group, original_group in modified_groups.items():
# these are backorders created for unavailable qties,
# reassign them the original group and carrier
need_release_pickings = new_group.picking_ids.filtered("need_release")
# reassign the original group and carriers on the backorders
need_release_pickings.move_lines.group_id = original_group
need_release_pickings.carrier_id = original_group.carrier_id

return res
Loading

0 comments on commit 8b7b7d7

Please sign in to comment.