diff --git a/addons/delivery/controllers/location_selector.py b/addons/delivery/controllers/location_selector.py
index 5222c1f47fc66..9ada6ec1d546f 100644
--- a/addons/delivery/controllers/location_selector.py
+++ b/addons/delivery/controllers/location_selector.py
@@ -4,21 +4,20 @@
class LocationSelectorController(Controller):
-
- @route('/delivery/set_pickup_location', type='jsonrpc', auth='user')
+ @route("/delivery/set_pickup_location", type="jsonrpc", auth="user")
def delivery_set_pickup_location(self, order_id, pickup_location_data):
- """ Fetch the order and set the pickup location on the current order.
+ """Fetch the order and set the pickup location on the current order.
:param int order_id: The sales order, as a `sale.order` id.
:param str pickup_location_data: The JSON-formatted pickup location address.
:return: None
"""
- order = request.env['sale.order'].browse(order_id)
+ order = request.env["sale.order"].browse(order_id)
order._set_pickup_location(pickup_location_data)
- @route('/delivery/get_pickup_locations', type='jsonrpc', auth='user')
+ @route("/delivery/get_pickup_locations", type="jsonrpc", auth="user")
def delivery_get_pickup_locations(self, order_id, zip_code=None):
- """ Fetch the order and return the pickup locations close to a given zip code.
+ """Fetch the order and return the pickup locations close to a given zip code.
Determine the country based on GeoIP or fallback on the order's delivery address' country.
@@ -27,10 +26,10 @@ def delivery_get_pickup_locations(self, order_id, zip_code=None):
:return: The close pickup locations data.
:rtype: dict
"""
- order = request.env['sale.order'].browse(order_id)
+ order = request.env["sale.order"].browse(order_id)
if request.geoip.country_code:
- country = request.env['res.country'].search(
- [('code', '=', request.geoip.country_code)], limit=1,
+ country = request.env["res.country"].search(
+ [("code", "=", request.geoip.country_code)], limit=1
)
else:
country = order.partner_shipping_id.country_id
diff --git a/addons/delivery/models/__init__.py b/addons/delivery/models/__init__.py
index 0acd8064567d5..403f07f6ce4dd 100644
--- a/addons/delivery/models/__init__.py
+++ b/addons/delivery/models/__init__.py
@@ -1,13 +1,15 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import delivery_carrier
-from . import delivery_price_rule
-from . import delivery_zip_prefix
-from . import ir_http
-from . import ir_module_module
-from . import payment_provider
-from . import payment_transaction
-from . import product_category
-from . import res_partner
-from . import sale_order
-from . import sale_order_line
+from . import (
+ delivery_carrier,
+ delivery_price_rule,
+ delivery_zip_prefix,
+ ir_http,
+ ir_module_module,
+ payment_provider,
+ payment_transaction,
+ product_category,
+ res_partner,
+ sale_order,
+ sale_order_line,
+)
diff --git a/addons/delivery/models/delivery_carrier.py b/addons/delivery/models/delivery_carrier.py
index 70b9cb9af19bc..9ea2f91cfb24a 100644
--- a/addons/delivery/models/delivery_carrier.py
+++ b/addons/delivery/models/delivery_carrier.py
@@ -11,11 +11,11 @@
class DeliveryCarrier(models.Model):
- _name = 'delivery.carrier'
+ _name = "delivery.carrier"
_description = "Delivery Method"
- _order = 'sequence, id'
+ _order = "sequence, id"
- ''' A Shipping Provider
+ """A Shipping Provider
In order to add your own external provider, follow these steps:
@@ -29,113 +29,192 @@ class DeliveryCarrier(models.Model):
_cancel_shipment
__get_default_custom_package_code
(they are documented hereunder)
- '''
+ """
- # -------------------------------- #
- # Internals for shipping providers #
- # -------------------------------- #
-
- name = fields.Char('Delivery Method', required=True, translate=True)
+ name = fields.Char(string="Delivery Method", translate=True, required=True)
active = fields.Boolean(default=True)
sequence = fields.Integer(help="Determine the display order", default=10)
- # This field will be overwritten by internal shipping providers by adding their own type (ex: 'fedex')
+ # This field will be overwritten by internal shipping providers by adding their own type.
delivery_type = fields.Selection(
- [('base_on_rule', 'Based on Rules'), ('fixed', 'Fixed Price')],
- string='Provider',
- default='fixed',
+ string="Provider",
+ selection=[("base_on_rule", "Based on Rules"), ("fixed", "Fixed Price")],
+ default="fixed",
required=True,
)
allow_cash_on_delivery = fields.Boolean(
string="Cash on Delivery",
help="Allow customers to choose Cash on Delivery as their payment method.",
)
- integration_level = fields.Selection([('rate', 'Get Rate'), ('rate_and_ship', 'Get Rate and Create Shipment')], string="Integration Level", default='rate_and_ship', help="Action while validating Delivery Orders")
- prod_environment = fields.Boolean("Environment", help="Set to True if your credentials are certified for production.")
- debug_logging = fields.Boolean('Debug logging', help="Log requests in order to ease debugging")
- company_id = fields.Many2one('res.company', string='Company', related='product_id.company_id', store=True, readonly=False)
- product_id = fields.Many2one('product.product', string='Delivery Product', required=True, ondelete='restrict')
- tracking_url = fields.Char(string='Tracking Link', help="This option adds a link for the customer in the portal to track their package easily. Use as a placeholder in your URL.")
- currency_id = fields.Many2one(related='product_id.currency_id')
+ integration_level = fields.Selection(
+ help="Action while validating Delivery Orders.",
+ selection=[("rate", "Get Rate"), ("rate_and_ship", "Get Rate and Create Shipment")],
+ default="rate_and_ship",
+ )
+ prod_environment = fields.Boolean(
+ string="Environment", help="Set to True if your credentials are certified for production."
+ )
+ debug_logging = fields.Boolean(
+ string="Debug logging", help="Log requests in order to ease debugging"
+ )
+ company_id = fields.Many2one(
+ comodel_name="res.company", related="product_id.company_id", store=True, readonly=False
+ )
+ product_id = fields.Many2one(
+ string="Delivery Product",
+ comodel_name="product.product",
+ ondelete="restrict",
+ required=True,
+ )
+ tracking_url = fields.Char(
+ string="Tracking Link",
+ help="This option adds a link for the customer in the portal to track their package easily."
+ " Use as a placeholder in your URL.",
+ )
+ currency_id = fields.Many2one(related="product_id.currency_id")
invoice_policy = fields.Selection(
- selection=[('estimated', "Estimated cost")],
string="Invoicing Policy",
- default='estimated',
- required=True,
help="Estimated Cost: the customer will be invoiced the estimated cost of the shipping.",
+ selection=[("estimated", "Estimated cost")],
+ default="estimated",
+ required=True,
)
- country_ids = fields.Many2many('res.country', 'delivery_carrier_country_rel', 'carrier_id', 'country_id', 'Countries')
- state_ids = fields.Many2many('res.country.state', 'delivery_carrier_state_rel', 'carrier_id', 'state_id', 'States')
+ country_ids = fields.Many2many(
+ string="Countries",
+ comodel_name="res.country",
+ relation="delivery_carrier_country_rel",
+ column1="carrier_id",
+ column2="country_id",
+ )
+ state_ids = fields.Many2many(
+ string="States",
+ comodel_name="res.country.state",
+ relation="delivery_carrier_state_rel",
+ column1="carrier_id",
+ column2="state_id",
+ )
zip_prefix_ids = fields.Many2many(
- 'delivery.zip.prefix', 'delivery_zip_prefix_rel', 'carrier_id', 'zip_prefix_id', 'Zip Prefixes',
- help="Prefixes of zip codes that this delivery method applies to. Note that regular expressions can be used to support countries with varying zip code lengths, i.e. '$' can be added to end of prefix to match the exact zip (e.g. '100$' will only match '100' and not '1000')")
-
- max_weight = fields.Float('Max Weight', help="If the total weight of the order is over this weight, the method won't be available.")
- weight_uom_name = fields.Char(string='Weight unit of measure label', compute='_compute_weight_uom_name')
- max_volume = fields.Float('Max Volume', help="If the total volume of the order is over this volume, the method won't be available.")
- volume_uom_name = fields.Char(string='Volume unit of measure label', compute='_compute_volume_uom_name')
- must_have_tag_ids = fields.Many2many(string='Must Have Tags', comodel_name='product.tag', relation='product_tag_delivery_carrier_must_have_rel',
- help="The method is available only if at least one product of the order has one of these tags.")
- excluded_tag_ids = fields.Many2many(string='Excluded Tags', comodel_name='product.tag', relation='product_tag_delivery_carrier_excluded_rel',
- help="The method is NOT available if at least one product of the order has one of these tags.")
+ string="Zip Prefixes",
+ help="Prefixes of zip codes that this delivery method applies to. Note that regular"
+ " expressions can be used to support countries with varying zip code lengths, i.e. '$'"
+ " can be added to end of prefix to match the exact zip (e.g. '100$' will only match"
+ " '100' and not '1000')",
+ comodel_name="delivery.zip.prefix",
+ relation="delivery_zip_prefix_rel",
+ column1="carrier_id",
+ column2="zip_prefix_id",
+ )
+
+ max_weight = fields.Float(
+ help="If the total weight of the order is over this weight, the method won't be available."
+ )
+ weight_uom_name = fields.Char(
+ string="Weight unit of measure label", compute="_compute_weight_uom_name"
+ )
+ max_volume = fields.Float(
+ help="If the total volume of the order is over this volume, the method won't be available."
+ )
+ volume_uom_name = fields.Char(
+ string="Volume unit of measure label", compute="_compute_volume_uom_name"
+ )
+ must_have_tag_ids = fields.Many2many(
+ string="Must Have Tags",
+ help="The method is available only if at least one product of the order has one of these"
+ " tags.",
+ comodel_name="product.tag",
+ relation="product_tag_delivery_carrier_must_have_rel",
+ )
+ excluded_tag_ids = fields.Many2many(
+ string="Excluded Tags",
+ help="The method is NOT available if at least one product of the order has one of these"
+ " tags.",
+ comodel_name="product.tag",
+ relation="product_tag_delivery_carrier_excluded_rel",
+ )
carrier_description = fields.Text(
- 'Description', translate=True,
- help="A description of the delivery method that you want to communicate to your customers on the Sales Order and sales confirmation email."
- "E.g. instructions for customers to follow.")
+ string="Description",
+ help="A description of the delivery method that you want to communicate to your customers"
+ " on the Sales Order and sales confirmation email. E.g. instructions for customers to"
+ " follow.",
+ translate=True,
+ )
- margin = fields.Float(help='This percentage will be added to the shipping price.')
- fixed_margin = fields.Float(help='This fixed amount will be added to the shipping price.')
- free_over = fields.Boolean('Free if order amount is above', help="If the order total amount (shipping excluded) is above or equal to this value, the customer benefits from a free shipping", default=False)
+ margin = fields.Float(help="This percentage will be added to the shipping price.")
+ fixed_margin = fields.Float(help="This fixed amount will be added to the shipping price.")
+ free_over = fields.Boolean(
+ string="Free if order amount is above",
+ help="If the order total amount (shipping excluded) is above or equal to this value, the"
+ " customer benefits from a free shipping.",
+ )
amount = fields.Float(
- string="Amount",
+ help="Amount of the order to benefit from a free shipping, expressed in the company"
+ " currency.",
default=1000,
- help="Amount of the order to benefit from a free shipping, expressed in the company currency",
)
can_generate_return = fields.Boolean(compute="_compute_can_generate_return")
- return_label_on_delivery = fields.Boolean(string="Generate Return Label", help="The return label is automatically generated at the delivery.")
- get_return_label_from_portal = fields.Boolean(string="Return Label Accessible from Customer Portal", help="The return label can be downloaded by the customer from the customer portal.")
+ return_label_on_delivery = fields.Boolean(
+ string="Generate Return Label",
+ help="The return label is automatically generated at the delivery.",
+ )
+ get_return_label_from_portal = fields.Boolean(
+ string="Return Label Accessible from Customer Portal",
+ help="The return label can be downloaded by the customer from the customer portal.",
+ )
supports_shipping_insurance = fields.Boolean(compute="_compute_supports_shipping_insurance")
shipping_insurance = fields.Integer(
- "Insurance Percentage",
- help="Shipping insurance is a service which may reimburse senders whose parcels are lost, stolen, and/or damaged in transit.",
- default=0
+ string="Insurance Percentage",
+ help="Shipping insurance is a service which may reimburse senders whose parcels are lost,"
+ " stolen, and/or damaged in transit.",
+ default=0,
)
price_rule_ids = fields.One2many(
- 'delivery.price.rule', 'carrier_id', 'Pricing Rules', copy=True
+ string="Pricing Rules",
+ comodel_name="delivery.price.rule",
+ inverse_name="carrier_id",
+ copy=True,
)
_margin_not_under_100_percent = models.Constraint(
- 'CHECK (margin >= -1)',
- 'Margin cannot be lower than -100%',
+ "CHECK (margin >= -1)", "Margin cannot be lower than -100%"
)
_shipping_insurance_is_percentage = models.Constraint(
- 'CHECK(shipping_insurance >= 0 AND shipping_insurance <= 100)',
- 'The shipping insurance must be a percentage between 0 and 100.',
+ "CHECK(shipping_insurance >= 0 AND shipping_insurance <= 100)",
+ "The shipping insurance must be a percentage between 0 and 100.",
)
- @api.constrains('must_have_tag_ids', 'excluded_tag_ids')
+ @api.constrains("must_have_tag_ids", "excluded_tag_ids")
def _check_tags(self):
for carrier in self:
if carrier.must_have_tag_ids & carrier.excluded_tag_ids:
- raise UserError(_("Delivery method %(name)s cannot have the same tag in both Must Have Tags and Excluded Tags."), name=carrier.name)
+ raise UserError(
+ _(
+ "Delivery method %(name)s cannot have the same tag in both Must Have Tags"
+ " and Excluded Tags.",
+ name=carrier.name,
+ )
+ )
def _compute_weight_uom_name(self):
- self.weight_uom_name = self.env['product.template']._get_weight_uom_name_from_ir_config_parameter()
+ self.weight_uom_name = self.env[
+ "product.template"
+ ]._get_weight_uom_name_from_ir_config_parameter()
def _compute_volume_uom_name(self):
- self.volume_uom_name = self.env['product.template']._get_volume_uom_name_from_ir_config_parameter()
+ self.volume_uom_name = self.env[
+ "product.template"
+ ]._get_volume_uom_name_from_ir_config_parameter()
- @api.depends('delivery_type')
+ @api.depends("delivery_type")
def _compute_can_generate_return(self):
for carrier in self:
carrier.can_generate_return = False
- @api.depends('delivery_type')
+ @api.depends("delivery_type")
def _compute_supports_shipping_insurance(self):
for carrier in self:
carrier.supports_shipping_insurance = False
@@ -149,20 +228,20 @@ def toggle_debug(self):
c.debug_logging = not c.debug_logging
def install_more_provider(self):
- exclude_apps = ['delivery_barcode', 'delivery_stock_picking_batch', 'delivery_iot']
+ exclude_apps = ["delivery_barcode", "delivery_stock_picking_batch", "delivery_iot"]
return {
- 'name': _('New Providers'),
- 'res_model': 'ir.module.module',
- 'view_mode': 'kanban,list',
- 'views': [
- (self.env.ref('delivery.delivery_provider_module_kanban').id, 'kanban'),
- (self.env.ref('delivery.delivery_provider_module_list').id, 'list'),
+ "name": _("New Providers"),
+ "res_model": "ir.module.module",
+ "view_mode": "kanban,list",
+ "views": [
+ (self.env.ref("delivery.delivery_provider_module_kanban").id, "kanban"),
+ (self.env.ref("delivery.delivery_provider_module_list").id, "list"),
],
- 'domain': [['name', '=like', 'delivery_%'], ['name', 'not in', exclude_apps]],
- 'type': 'ir.actions.act_window',
- 'help': _('''
+ "domain": [["name", "=like", "delivery_%"], ["name", "not in", exclude_apps]],
+ "type": "ir.actions.act_window",
+ "help": _("""
Buy Odoo Enterprise now to get more providers.
-
'''),
+
"""),
}
def _is_available_for_order(self, order):
@@ -171,8 +250,8 @@ def _is_available_for_order(self, order):
if not self._match(order.partner_shipping_id, order):
return False
- if self.delivery_type == 'base_on_rule':
- return self.rate_shipment(order).get('success')
+ if self.delivery_type == "base_on_rule":
+ return self.rate_shipment(order).get("success")
return True
@@ -196,45 +275,44 @@ def _match_address(self, partner):
if self.state_ids and partner.state_id not in self.state_ids:
return False
if self.zip_prefix_ids:
- regex = re.compile('|'.join(['^' + zip_prefix for zip_prefix in self.zip_prefix_ids.mapped('name')]))
+ regex = re.compile(
+ "|".join(["^" + zip_prefix for zip_prefix in self.zip_prefix_ids.mapped("name")])
+ )
if not partner.zip or not re.match(regex, partner.zip.upper()):
return False
return True
def _match_must_have_tags(self, source):
self.ensure_one()
- if source._name == 'sale.order':
+ if source._name == "sale.order":
products = source.order_line.product_id
- elif source._name == 'stock.picking':
- products = source.move_ids.with_prefetch().mapped('product_id')
+ elif source._name == "stock.picking":
+ products = source.move_ids.with_prefetch().mapped("product_id")
else:
raise UserError(_("Invalid source document type"))
return not self.must_have_tag_ids or any(
- tag in products.all_product_tag_ids
- for tag in self.must_have_tag_ids
+ tag in products.all_product_tag_ids for tag in self.must_have_tag_ids
)
def _match_excluded_tags(self, source):
self.ensure_one()
- if source._name == 'sale.order':
+ if source._name == "sale.order":
products = source.order_line.product_id
- elif source._name == 'stock.picking':
- products = source.move_ids.with_prefetch().mapped('product_id')
+ elif source._name == "stock.picking":
+ products = source.move_ids.with_prefetch().mapped("product_id")
else:
raise UserError(_("Invalid source document type"))
return not any(tag in products.all_product_tag_ids for tag in self.excluded_tag_ids)
def _match_weight(self, source):
self.ensure_one()
- if source._name == 'sale.order':
+ if source._name == "sale.order":
total_weight = sum(
- line.product_id.weight * line.product_qty
- for line in source.order_line
+ line.product_id.weight * line.product_qty for line in source.order_line
)
- elif source._name == 'stock.picking':
+ elif source._name == "stock.picking":
total_weight = sum(
- move.product_id.weight * move.product_uom_qty
- for move in source.move_ids
+ move.product_id.weight * move.product_uom_qty for move in source.move_ids
)
else:
raise UserError(_("Invalid source document type"))
@@ -242,36 +320,34 @@ def _match_weight(self, source):
def _match_volume(self, source):
self.ensure_one()
- if source._name == 'sale.order':
+ if source._name == "sale.order":
total_volume = sum(
- line.product_id.volume * line.product_qty
- for line in source.order_line
+ line.product_id.volume * line.product_qty for line in source.order_line
)
- elif source._name == 'stock.picking':
+ elif source._name == "stock.picking":
total_volume = sum(
- move.product_id.volume * move.product_uom_qty
- for move in source.move_ids
+ move.product_id.volume * move.product_uom_qty for move in source.move_ids
)
else:
raise UserError(_("Invalid source document type"))
return not self.max_volume or total_volume <= self.max_volume
- @api.onchange('integration_level')
+ @api.onchange("integration_level")
def _onchange_integration_level(self):
- if self.integration_level == 'rate':
- self.invoice_policy = 'estimated'
+ if self.integration_level == "rate":
+ self.invoice_policy = "estimated"
- @api.onchange('can_generate_return')
+ @api.onchange("can_generate_return")
def _onchange_can_generate_return(self):
if not self.can_generate_return:
self.return_label_on_delivery = False
- @api.onchange('return_label_on_delivery')
+ @api.onchange("return_label_on_delivery")
def _onchange_return_label_on_delivery(self):
if not self.return_label_on_delivery:
self.get_return_label_from_portal = False
- @api.onchange('country_ids')
+ @api.onchange("country_ids")
def _onchange_country_ids(self):
self.state_ids -= self.state_ids.filtered(
lambda state: state._origin.id not in self.country_ids.state_ids.ids
@@ -281,7 +357,10 @@ def _onchange_country_ids(self):
def copy_data(self, default=None):
vals_list = super().copy_data(default=default)
- return [dict(vals, name=self.env._("%s (copy)", carrier.name)) for carrier, vals in zip(self, vals_list)]
+ return [
+ dict(vals, name=self.env._("%s (copy)", carrier.name))
+ for carrier, vals in zip(self, vals_list)
+ ]
def _get_delivery_type(self):
"""Return the delivery type.
@@ -294,9 +373,13 @@ def _get_delivery_type(self):
def _apply_margins(self, price, order=False):
self.ensure_one()
- if self.delivery_type == 'fixed':
+ if self.delivery_type == "fixed":
return float(price)
- fixed_margin_in_sale_currency = self._compute_currency(order, self.fixed_margin, 'company_to_pricelist') if order else self.fixed_margin
+ fixed_margin_in_sale_currency = (
+ self._compute_currency(order, self.fixed_margin, "company_to_pricelist")
+ if order
+ else self.fixed_margin
+ )
return float(price) * (1.0 + self.margin) + fixed_margin_in_sale_currency
# -------------------------- #
@@ -304,7 +387,7 @@ def _apply_margins(self, price, order=False):
# -------------------------- #
def rate_shipment(self, order):
- ''' Compute the price of the order shipment
+ """Compute the price of the order shipment.
:param order: record of sale.order
:returns: a dict with structure
@@ -315,44 +398,46 @@ def rate_shipment(self, order):
'error_message': a string containing an error message,
'warning_message': a string containing a warning message}
:rtype: dict
- '''
+ """
# TODO maybe the currency code?
self.ensure_one()
- if hasattr(self, '%s_rate_shipment' % self.delivery_type):
- res = getattr(self, '%s_rate_shipment' % self.delivery_type)(order)
+ if hasattr(self, "%s_rate_shipment" % self.delivery_type):
+ res = getattr(self, "%s_rate_shipment" % self.delivery_type)(order)
# apply fiscal position
company = self.company_id or order.company_id or self.env.company
- res['price'] = self.product_id._get_tax_included_unit_price(
+ res["price"] = self.product_id._get_tax_included_unit_price(
company,
company.currency_id,
order.date_order,
- 'sale',
+ "sale",
fiscal_position=order.fiscal_position_id,
- product_price_unit=res['price'],
- product_currency=company.currency_id
+ product_price_unit=res["price"],
+ product_currency=company.currency_id,
)
# apply margin on computed price
- res['price'] = self._apply_margins(res['price'], order)
+ res["price"] = self._apply_margins(res["price"], order)
# save the real price in case a free_over rule overide it to 0
- res['carrier_price'] = res['price']
+ res["carrier_price"] = res["price"]
# free when order is large enough
amount_without_delivery = order._compute_amount_total_without_delivery()
if (
- res['success']
+ res["success"]
and self.free_over
- and self.delivery_type != 'base_on_rule'
- and self._compute_currency(order, amount_without_delivery, 'pricelist_to_company') >= self.amount
+ and self.delivery_type != "base_on_rule"
+ and self._compute_currency(order, amount_without_delivery, "pricelist_to_company")
+ >= self.amount
):
- res['warning_message'] = _('The shipping is free since the order amount exceeds %.2f.', self.amount)
- res['price'] = 0.0
+ res["warning_message"] = _(
+ "The shipping is free since the order amount exceeds %.2f.", self.amount
+ )
+ res["price"] = 0.0
return res
- else:
- return {
- 'success': False,
- 'price': 0.0,
- 'error_message': _('Error: this delivery method is not available.'),
- 'warning_message': False,
- }
+ return {
+ "success": False,
+ "price": 0.0,
+ "error_message": _("Error: this delivery method is not available."),
+ "warning_message": False,
+ }
def log_xml(self, xml_string, func):
self.ensure_one()
@@ -366,15 +451,17 @@ def log_xml(self, xml_string, func):
db_registry = Registry(db_name)
with db_registry.cursor() as cr:
env = api.Environment(cr, SUPERUSER_ID, {})
- IrLogging = env['ir.logging']
- IrLogging.sudo().create({'name': 'delivery.carrier',
- 'type': 'server',
- 'dbname': db_name,
- 'level': 'DEBUG',
- 'message': xml_string,
- 'path': self.delivery_type,
- 'func': func,
- 'line': 1})
+ IrLogging = env["ir.logging"]
+ IrLogging.sudo().create({
+ "name": "delivery.carrier",
+ "type": "server",
+ "dbname": db_name,
+ "level": "DEBUG",
+ "message": xml_string,
+ "path": self.delivery_type,
+ "func": func,
+ "line": 1,
+ })
except psycopg2.Error:
pass
@@ -382,9 +469,14 @@ def log_xml(self, xml_string, func):
# Fixed price shipping, aka a very simple provider #
# ------------------------------------------------ #
- fixed_price = fields.Float(compute='_compute_fixed_price', inverse='_set_product_fixed_price', store=True, string='Fixed Price')
+ fixed_price = fields.Float(
+ string="Fixed Price",
+ compute="_compute_fixed_price",
+ inverse="_set_product_fixed_price",
+ store=True,
+ )
- @api.depends('product_id.list_price', 'product_id.product_tmpl_id.list_price')
+ @api.depends("product_id.list_price", "product_id.product_tmpl_id.list_price")
def _compute_fixed_price(self):
for carrier in self:
carrier.fixed_price = carrier.product_id.list_price
@@ -396,15 +488,16 @@ def _set_product_fixed_price(self):
def fixed_rate_shipment(self, order):
carrier = self._match_address(order.partner_shipping_id)
if not carrier:
- return {'success': False,
- 'price': 0.0,
- 'error_message': _('Error: this delivery method is not available for this address.'),
- 'warning_message': False}
+ return {
+ "success": False,
+ "price": 0.0,
+ "error_message": _(
+ "Error: this delivery method is not available for this address."
+ ),
+ "warning_message": False,
+ }
price = order.pricelist_id._get_product_price(self.product_id, 1.0)
- return {'success': True,
- 'price': price,
- 'error_message': False,
- 'warning_message': False}
+ return {"success": True, "price": price, "error_message": False, "warning_message": False}
# ----------------------------------- #
# Based on rule delivery type methods #
@@ -413,40 +506,52 @@ def fixed_rate_shipment(self, order):
def base_on_rule_rate_shipment(self, order):
carrier = self._match_address(order.partner_shipping_id)
if not carrier:
- return {'success': False,
- 'price': 0.0,
- 'error_message': _('Error: this delivery method is not available for this address.'),
- 'warning_message': False}
+ return {
+ "success": False,
+ "price": 0.0,
+ "error_message": _(
+ "Error: this delivery method is not available for this address."
+ ),
+ "warning_message": False,
+ }
try:
price_unit = self._get_price_available(order)
except UserError as e:
- return {'success': False,
- 'price': 0.0,
- 'error_message': e.args[0],
- 'warning_message': False}
+ return {
+ "success": False,
+ "price": 0.0,
+ "error_message": e.args[0],
+ "warning_message": False,
+ }
- price_unit = self._compute_currency(order, price_unit, 'company_to_pricelist')
+ price_unit = self._compute_currency(order, price_unit, "company_to_pricelist")
- return {'success': True,
- 'price': price_unit,
- 'error_message': False,
- 'warning_message': False}
+ return {
+ "success": True,
+ "price": price_unit,
+ "error_message": False,
+ "warning_message": False,
+ }
def _get_conversion_currencies(self, order, conversion):
- company_currency = (self.company_id or self.env['res.company']._get_main_company()).currency_id
+ company_currency = (
+ self.company_id or self.env["res.company"]._get_main_company()
+ ).currency_id
pricelist_currency = order.currency_id
- if conversion == 'company_to_pricelist':
+ if conversion == "company_to_pricelist":
return company_currency, pricelist_currency
- elif conversion == 'pricelist_to_company':
+ if conversion == "pricelist_to_company":
return pricelist_currency, company_currency
def _compute_currency(self, order, price, conversion):
from_currency, to_currency = self._get_conversion_currencies(order, conversion)
if from_currency.id == to_currency.id:
return price
- return from_currency._convert(price, to_currency, order.company_id, order.date_order or fields.Date.today())
+ return from_currency._convert(
+ price, to_currency, order.company_id, order.date_order or fields.Date.today()
+ )
def _get_price_available(self, order):
self.ensure_one()
@@ -455,7 +560,7 @@ def _get_price_available(self, order):
total = weight = volume = quantity = wv = 0
total_delivery = 0.0
for line in order.order_line:
- if line.state == 'cancel':
+ if line.state == "cancel":
continue
if line.is_delivery:
total_delivery += line.price_total
@@ -463,33 +568,38 @@ def _get_price_available(self, order):
continue
if line.product_id.type == "service":
continue
- qty = line.product_uom_id._compute_quantity(line.product_uom_qty, line.product_id.uom_id)
+ qty = line.product_uom_id._compute_quantity(
+ line.product_uom_qty, line.product_id.uom_id
+ )
weight += (line.product_id.weight or 0.0) * qty
volume += (line.product_id.volume or 0.0) * qty
wv += (line.product_id.weight or 0.0) * (line.product_id.volume or 0.0) * qty
quantity += qty
total = (order.amount_total or 0.0) - total_delivery
- total = self._compute_currency(order, total, 'pricelist_to_company')
+ total = self._compute_currency(order, total, "pricelist_to_company")
# weight is either,
# 1- weight chosen by user in choose.delivery.carrier wizard passed by context
# 2- saved weight to use on sale order
# 3- total order line weight as fallback
- weight = self.env.context.get('order_weight') or order.shipping_weight or weight
+ weight = self.env.context.get("order_weight") or order.shipping_weight or weight
return self._get_price_from_picking(total, weight, volume, quantity, wv=wv)
- def _get_price_dict(self, total, weight, volume, quantity, wv=0.):
- '''Hook allowing to retrieve dict to be used in _get_price_from_picking() function.
- Hook to be overridden when we need to add some field to product and use it in variable factor from price rules. '''
+ def _get_price_dict(self, total, weight, volume, quantity, wv=0.0):
+ """Format delivery price values.
+
+ Hook to be overridden when we need to add some field to product and use it in variable
+ factor from price rules.
+ """
return {
- 'price': total,
- 'volume': volume,
- 'weight': weight,
- 'wv': wv or volume * weight,
- 'quantity': quantity
+ "price": total,
+ "volume": volume,
+ "weight": weight,
+ "wv": wv or volume * weight,
+ "quantity": quantity,
}
- def _get_price_from_picking(self, total, weight, volume, quantity, wv=0.):
+ def _get_price_from_picking(self, total, weight, volume, quantity, wv=0.0):
price = 0.0
criteria_found = False
price_dict = self._get_price_dict(total, weight, volume, quantity, wv=wv)
diff --git a/addons/delivery/models/delivery_price_rule.py b/addons/delivery/models/delivery_price_rule.py
index 20a078be5eb6d..2e14b05b05ce4 100644
--- a/addons/delivery/models/delivery_price_rule.py
+++ b/addons/delivery/models/delivery_price_rule.py
@@ -3,25 +3,32 @@
from odoo import api, fields, models
from odoo.tools import format_amount
-
VARIABLE_SELECTION = [
- ('weight', "Weight"),
- ('volume', "Volume"),
- ('wv', "Weight * Volume"),
- ('price', "Price"),
- ('quantity', "Quantity"),
+ ("weight", "Weight"),
+ ("volume", "Volume"),
+ ("wv", "Weight * Volume"),
+ ("price", "Price"),
+ ("quantity", "Quantity"),
]
class DeliveryPriceRule(models.Model):
- _name = 'delivery.price.rule'
+ _name = "delivery.price.rule"
_description = "Delivery Price Rules"
- _order = 'sequence, list_price, id'
+ _order = "sequence, list_price, id"
- @api.depends('variable', 'operator', 'max_value', 'list_base_price', 'list_price', 'variable_factor', 'currency_id')
+ @api.depends(
+ "variable",
+ "operator",
+ "max_value",
+ "list_base_price",
+ "list_price",
+ "variable_factor",
+ "currency_id",
+ )
def _compute_name(self):
for rule in self:
- name = 'if %s %s %.02f then' % (rule.variable, rule.operator, rule.max_value)
+ name = "if %s %s %.02f then" % (rule.variable, rule.operator, rule.max_value)
if rule.currency_id:
base_price = format_amount(self.env, rule.list_base_price, rule.currency_id)
price = format_amount(self.env, rule.list_price, rule.currency_id)
@@ -29,25 +36,38 @@ def _compute_name(self):
base_price = "%.2f" % rule.list_base_price
price = "%.2f" % rule.list_price
if rule.list_base_price and not rule.list_price:
- name = '%s fixed price %s' % (name, base_price)
+ name = "%s fixed price %s" % (name, base_price)
elif rule.list_price and not rule.list_base_price:
- name = '%s %s times %s' % (name, price, rule.variable_factor)
+ name = "%s %s times %s" % (name, price, rule.variable_factor)
else:
- name = '%s fixed price %s plus %s times %s' % (
- name, base_price, price, rule.variable_factor
+ name = "%s fixed price %s plus %s times %s" % (
+ name,
+ base_price,
+ price,
+ rule.variable_factor,
)
rule.name = name
- name = fields.Char(compute='_compute_name')
+ name = fields.Char(compute="_compute_name")
sequence = fields.Integer(required=True, default=10)
- carrier_id = fields.Many2one('delivery.carrier', 'Carrier', required=True, index=True, ondelete='cascade')
- currency_id = fields.Many2one(related='carrier_id.currency_id')
+ carrier_id = fields.Many2one(
+ comodel_name="delivery.carrier", ondelete="cascade", required=True, index=True
+ )
+ currency_id = fields.Many2one(related="carrier_id.currency_id")
- variable = fields.Selection(selection=VARIABLE_SELECTION, required=True, default='quantity')
- operator = fields.Selection([('==', '='), ('<=', '<='), ('<', '<'), ('>=', '>='), ('>', '>')], required=True, default='<=')
- max_value = fields.Float('Maximum Value', required=True)
- list_base_price = fields.Float(string='Sale Base Price', digits='Product Price', required=True, default=0.0)
- list_price = fields.Float('Sale Price', digits='Product Price', required=True, default=0.0)
+ variable = fields.Selection(selection=VARIABLE_SELECTION, default="quantity", required=True)
+ operator = fields.Selection(
+ selection=[("==", "="), ("<=", "<="), ("<", "<"), (">=", ">="), (">", ">")],
+ default="<=",
+ required=True,
+ )
+ max_value = fields.Float(string="Maximum Value", required=True)
+ list_base_price = fields.Float(
+ string="Sale Base Price", digits="Product Price", default=0.0, required=True
+ )
+ list_price = fields.Float(
+ string="Sale Price", digits="Product Price", default=0.0, required=True
+ )
variable_factor = fields.Selection(
- selection=VARIABLE_SELECTION, string="Variable Factor", required=True, default='weight'
+ selection=VARIABLE_SELECTION, default="weight", required=True
)
diff --git a/addons/delivery/models/delivery_zip_prefix.py b/addons/delivery/models/delivery_zip_prefix.py
index 94d8c715b5edf..bbff60ea30fff 100644
--- a/addons/delivery/models/delivery_zip_prefix.py
+++ b/addons/delivery/models/delivery_zip_prefix.py
@@ -4,27 +4,25 @@
class DeliveryZipPrefix(models.Model):
- """ Zip prefix that a delivery.carrier will deliver to. """
- _name = 'delivery.zip.prefix'
- _description = 'Delivery Zip Prefix'
- _order = 'name, id'
+ """Zip prefix that a delivery.carrier will deliver to."""
- name = fields.Char('Prefix', required=True)
+ _name = "delivery.zip.prefix"
+ _description = "Delivery Zip Prefix"
+ _order = "name, id"
+
+ name = fields.Char(string="Prefix", required=True)
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
- # we cannot easily convert a list of prefix names into upper to compare with partner zips
- # later on, so let's ensure they are always upper
- vals['name'] = vals['name'].upper()
+ # we cannot easily convert a list of prefix names into upper to compare with partner
+ # zips later on, so let's ensure they are always upper
+ vals["name"] = vals["name"].upper()
return super().create(vals_list)
def write(self, vals):
- if 'name' in vals:
- vals['name'] = vals['name'].upper()
+ if "name" in vals:
+ vals["name"] = vals["name"].upper()
return super().write(vals)
- _name_uniq = models.Constraint(
- 'unique (name)',
- 'Prefix already exists!',
- )
+ _name_uniq = models.Constraint("unique (name)", "Prefix already exists!")
diff --git a/addons/delivery/models/ir_http.py b/addons/delivery/models/ir_http.py
index 0a8db84b0ec00..7c543aeffa17f 100644
--- a/addons/delivery/models/ir_http.py
+++ b/addons/delivery/models/ir_http.py
@@ -4,9 +4,9 @@
class IrHttp(models.AbstractModel):
- _inherit = 'ir.http'
+ _inherit = "ir.http"
@classmethod
def _get_translation_frontend_modules_name(cls):
mods = super()._get_translation_frontend_modules_name()
- return mods + ['delivery']
+ return mods + ["delivery"]
diff --git a/addons/delivery/models/ir_module_module.py b/addons/delivery/models/ir_module_module.py
index 93210aba59a53..9a7ba1f3dac90 100644
--- a/addons/delivery/models/ir_module_module.py
+++ b/addons/delivery/models/ir_module_module.py
@@ -4,20 +4,20 @@
class IrModuleModule(models.Model):
- _name = 'ir.module.module'
- _inherit = ['ir.module.module']
+ _name = "ir.module.module"
+ _inherit = ["ir.module.module"]
def action_view_delivery_methods(self):
self.ensure_one()
module_name = self.name # e.g., delivery_dhl
- if not module_name.startswith('delivery_'):
+ if not module_name.startswith("delivery_"):
return False
- delivery_type = module_name.removeprefix('delivery_') # dhl, fedex, etc.
- action = self.env.ref('delivery.action_delivery_carrier_form').read()[0]
- if delivery_type == 'mondialrelay':
- action['context'] = {'search_default_is_mondialrelay': True}
+ delivery_type = module_name.removeprefix("delivery_") # dhl, fedex, etc.
+ action = self.env.ref("delivery.action_delivery_carrier_form").read()[0]
+ if delivery_type == "mondialrelay":
+ action["context"] = {"search_default_is_mondialrelay": True}
else:
- action['context'] = {'search_default_delivery_type': delivery_type}
+ action["context"] = {"search_default_delivery_type": delivery_type}
return action
diff --git a/addons/delivery/models/payment_provider.py b/addons/delivery/models/payment_provider.py
index 7f85225d0bbd8..61d55efaeb306 100644
--- a/addons/delivery/models/payment_provider.py
+++ b/addons/delivery/models/payment_provider.py
@@ -7,16 +7,16 @@
class PaymentProvider(models.Model):
- _inherit = 'payment.provider'
+ _inherit = "payment.provider"
- custom_mode = fields.Selection(selection_add=[('cash_on_delivery', 'Cash On Delivery')])
+ custom_mode = fields.Selection(selection_add=[("cash_on_delivery", "Cash On Delivery")])
# === CRUD METHODS === #
def _get_default_payment_method_codes(self):
- """ Override of `payment` to return the default payment method codes. """
+ """Override of `payment` to return the default payment method codes."""
self.ensure_one()
- if self.custom_mode != 'cash_on_delivery':
+ if self.custom_mode != "cash_on_delivery":
return super()._get_default_payment_method_codes()
return const.DEFAULT_PAYMENT_METHOD_CODES
@@ -24,7 +24,7 @@ def _get_default_payment_method_codes(self):
@api.model
def _get_compatible_providers(self, *args, sale_order_id=None, report=None, **kwargs):
- """ Override of payment to exclude COD providers if the delivery method doesn't match.
+ """Override of payment to exclude COD providers if the delivery method doesn't match.
:param int sale_order_id: The sales order to be paid, if any, as a `sale.order` id.
:param dict report: The availability report.
@@ -35,11 +35,11 @@ def _get_compatible_providers(self, *args, sale_order_id=None, report=None, **kw
*args, sale_order_id=sale_order_id, report=report, **kwargs
)
- sale_order = self.env['sale.order'].browse(sale_order_id).exists()
+ sale_order = self.env["sale.order"].browse(sale_order_id).exists()
if not sale_order.carrier_id.allow_cash_on_delivery:
unfiltered_providers = compatible_providers
compatible_providers = compatible_providers.filtered(
- lambda p: p.custom_mode != 'cash_on_delivery'
+ lambda p: p.custom_mode != "cash_on_delivery"
)
payment_utils.add_to_report(
report,
diff --git a/addons/delivery/models/payment_transaction.py b/addons/delivery/models/payment_transaction.py
index 0b26b8e4081b2..9a4b3bb2fe074 100644
--- a/addons/delivery/models/payment_transaction.py
+++ b/addons/delivery/models/payment_transaction.py
@@ -4,15 +4,15 @@
class PaymentTransaction(models.Model):
- _inherit = 'payment.transaction'
+ _inherit = "payment.transaction"
def _post_process(self):
- """ Override of `payment` to confirm orders with the cash_on_delivery payment method and
- trigger a picking creation. """
+ """Override of `payment` to confirm orders with the cash_on_delivery payment method and
+ trigger a picking creation."""
cod_pending_txs = self.filtered(
- lambda tx: tx.provider_id.custom_mode == 'cash_on_delivery' and tx.state == 'pending'
+ lambda tx: tx.provider_id.custom_mode == "cash_on_delivery" and tx.state == "pending"
)
- cod_pending_txs.sale_order_ids.filtered(
- lambda so: so.state == 'draft'
- ).with_context(send_email=True).action_confirm()
+ cod_pending_txs.sale_order_ids.filtered(lambda so: so.state == "draft").with_context(
+ send_email=True
+ ).action_confirm()
super()._post_process()
diff --git a/addons/delivery/models/product_category.py b/addons/delivery/models/product_category.py
index cff4c7ac3ff3f..147b622645176 100644
--- a/addons/delivery/models/product_category.py
+++ b/addons/delivery/models/product_category.py
@@ -9,6 +9,13 @@ class ProductCategory(models.Model):
@api.ondelete(at_uninstall=False)
def _unlink_except_delivery_category(self):
- delivery_category = self.env.ref('delivery.product_category_deliveries', raise_if_not_found=False)
+ delivery_category = self.env.ref(
+ "delivery.product_category_deliveries", raise_if_not_found=False
+ )
if delivery_category and delivery_category in self:
- raise UserError(_("You cannot delete this product category as it is used on the products linked to delivery methods."))
+ raise UserError(
+ _(
+ "You cannot delete this product category as it is used on the products linked"
+ " to delivery methods."
+ )
+ )
diff --git a/addons/delivery/models/res_partner.py b/addons/delivery/models/res_partner.py
index dd28de04b2a56..ed69097fb8aea 100644
--- a/addons/delivery/models/res_partner.py
+++ b/addons/delivery/models/res_partner.py
@@ -5,10 +5,15 @@
class ResPartner(models.Model):
- _inherit = 'res.partner'
+ _inherit = "res.partner"
- property_delivery_carrier_id = fields.Many2one('delivery.carrier', company_dependent=True, string="Delivery Method", help="Used in sales orders.")
+ property_delivery_carrier_id = fields.Many2one(
+ string="Delivery Method",
+ help="Used in sales orders.",
+ comodel_name="delivery.carrier",
+ company_dependent=True,
+ )
is_pickup_location = fields.Boolean() # Whether it is a pickup point address.
def _get_delivery_address_domain(self):
- return super()._get_delivery_address_domain() & Domain('is_pickup_location', '=', False)
+ return super()._get_delivery_address_domain() & Domain("is_pickup_location", "=", False)
diff --git a/addons/delivery/models/sale_order.py b/addons/delivery/models/sale_order.py
index 370b4b9b0e9e9..8c6b6a1d60818 100644
--- a/addons/delivery/models/sale_order.py
+++ b/addons/delivery/models/sale_order.py
@@ -7,60 +7,83 @@
class SaleOrder(models.Model):
- _inherit = 'sale.order'
+ _inherit = "sale.order"
pickup_location_data = fields.Json()
- carrier_id = fields.Many2one('delivery.carrier', string="Delivery Method", check_company=True, help="Fill this field if you plan to invoice the shipping based on picking.")
+ carrier_id = fields.Many2one(
+ string="Delivery Method",
+ help="Fill this field if you plan to invoice the shipping based on picking.",
+ comodel_name="delivery.carrier",
+ check_company=True,
+ )
delivery_message = fields.Char(readonly=True, copy=False)
- delivery_set = fields.Boolean(compute='_compute_delivery_state')
- recompute_delivery_price = fields.Boolean('Delivery cost should be recomputed')
- is_all_service = fields.Boolean("Service Product", compute="_compute_is_service_products")
- shipping_weight = fields.Float("Shipping Weight", compute="_compute_shipping_weight", store=True, readonly=False)
+ delivery_set = fields.Boolean(compute="_compute_delivery_state")
+ recompute_delivery_price = fields.Boolean(string="Delivery cost should be recomputed")
+ is_all_service = fields.Boolean(
+ string="Service Product", compute="_compute_is_service_products"
+ )
+ shipping_weight = fields.Float(compute="_compute_shipping_weight", store=True, readonly=False)
def _compute_partner_shipping_id(self):
- """ Override to reset the delivery address when a pickup location was selected. """
+ """Override to reset the delivery address when a pickup location was selected."""
super()._compute_partner_shipping_id()
for order in self:
if order.partner_shipping_id.is_pickup_location:
order.partner_shipping_id = order.partner_id
- @api.depends('order_line')
+ @api.depends("order_line")
def _compute_is_service_products(self):
for so in self:
- so.is_all_service = all(line.product_id.type == 'service' for line in so.order_line.filtered(lambda x: not x.display_type))
+ so.is_all_service = all(
+ line.product_id.type == "service"
+ for line in so.order_line.filtered(lambda x: not x.display_type)
+ )
def _compute_amount_total_without_delivery(self):
self.ensure_one()
- delivery_cost = sum([l.price_total for l in self.order_line if l.is_delivery])
+ delivery_cost = sum([ol.price_total for ol in self.order_line if ol.is_delivery])
return self.amount_total - delivery_cost
- @api.depends('order_line')
+ @api.depends("order_line")
def _compute_delivery_state(self):
for order in self:
order.delivery_set = any(line.is_delivery for line in order.order_line)
- @api.onchange('order_line', 'partner_id', 'partner_shipping_id')
+ @api.onchange("order_line", "partner_id", "partner_shipping_id")
def onchange_order_line(self):
self.ensure_one()
- delivery_line = self.order_line.filtered('is_delivery')
+ delivery_line = self.order_line.filtered("is_delivery")
if delivery_line:
self.recompute_delivery_price = True
def _get_update_prices_lines(self):
- """ Exclude delivery lines from price list recomputation based on product instead of carrier """
+ """Exclude delivery lines from pricelist recomputation based on product instead of
+ carrier."""
lines = super()._get_update_prices_lines()
return lines.filtered(lambda line: not line.is_delivery)
def _remove_delivery_line(self):
- """Remove delivery products from the sales orders"""
+ """Remove delivery products from the sales orders."""
delivery_lines = self.order_line.filtered("is_delivery")
if not delivery_lines:
return
to_delete = delivery_lines.filtered(lambda x: x.qty_invoiced == 0)
if not to_delete:
raise UserError(
- _('You can not update the shipping costs on an order where it was already invoiced!\n\nThe following delivery lines (product, invoiced quantity and price) have already been processed:\n\n')
- + '\n'.join(['- %s: %s x %s' % (line.product_id.with_context(display_default_code=False).display_name, line.qty_invoiced, line.price_unit) for line in delivery_lines])
+ _(
+ "You can not update the shipping costs on an order where it was already"
+ " invoiced!\n\nThe following delivery lines (product, invoiced quantity and"
+ " price) have already been processed:\n\n"
+ )
+ + "\n".join([
+ "- %s: %s x %s"
+ % (
+ line.product_id.with_context(display_default_code=False).display_name,
+ line.qty_invoiced,
+ line.price_unit,
+ )
+ for line in delivery_lines
+ ])
)
to_delete.unlink()
@@ -72,7 +95,7 @@ def set_delivery_line(self, carrier, amount):
return True
def _set_pickup_location(self, pickup_location_data):
- """ Set the pickup location on the current order.
+ """Set the pickup location on the current order.
Note: self.ensure_one()
@@ -80,7 +103,7 @@ def _set_pickup_location(self, pickup_location_data):
:return: None
"""
self.ensure_one()
- use_locations_fname = f'{self.carrier_id.delivery_type}_use_locations'
+ use_locations_fname = f"{self.carrier_id.delivery_type}_use_locations"
if hasattr(self.carrier_id, use_locations_fname):
use_location = getattr(self.carrier_id, use_locations_fname)
if use_location and pickup_location_data:
@@ -90,7 +113,7 @@ def _set_pickup_location(self, pickup_location_data):
self.pickup_location_data = pickup_location
def _get_pickup_locations(self, zip_code=None, country=None, **kwargs):
- """ Return the pickup locations of the delivery method close to a given zip code.
+ """Return the pickup locations of the delivery method close to a given zip code.
Use provided `zip_code` and `country` or the order's delivery address to determine the zip
code and the country to use.
@@ -105,44 +128,44 @@ def _get_pickup_locations(self, zip_code=None, country=None, **kwargs):
self.ensure_one()
if zip_code:
assert country # country is required if zip_code is provided.
- partner_address = self.env['res.partner'].new({
- 'active': False,
- 'country_id': country.id,
- 'zip': zip_code,
+ partner_address = self.env["res.partner"].new({
+ "active": False,
+ "country_id": country.id,
+ "zip": zip_code,
})
else:
partner_address = self.partner_shipping_id
try:
- error = {'error': _("No pick-up points are available for this delivery address.")}
- function_name = f'_{self.carrier_id.delivery_type}_get_close_locations'
+ error = {"error": _("No pick-up points are available for this delivery address.")}
+ function_name = f"_{self.carrier_id.delivery_type}_get_close_locations"
if not hasattr(self.carrier_id, function_name):
return error
pickup_locations = getattr(self.carrier_id, function_name)(partner_address, **kwargs)
if not pickup_locations:
return error
- return {'pickup_locations': pickup_locations}
+ return {"pickup_locations": pickup_locations}
except UserError as e:
- return {'error': str(e)}
+ return {"error": str(e)}
def action_open_delivery_wizard(self):
- view_id = self.env.ref('delivery.choose_delivery_carrier_view_form').id
- if self.env.context.get('carrier_recompute'):
- name = _('Update shipping cost')
+ view_id = self.env.ref("delivery.choose_delivery_carrier_view_form").id
+ if self.env.context.get("carrier_recompute"):
+ name = _("Update shipping cost")
else:
- name = _('Add a delivery method')
+ name = _("Add a delivery method")
return {
- 'name': name,
- 'type': 'ir.actions.act_window',
- 'view_mode': 'form',
- 'res_model': 'choose.delivery.carrier',
- 'view_id': view_id,
- 'views': [(view_id, 'form')],
- 'target': 'new',
- 'context': {
- 'default_order_id': self.id,
- 'default_carrier_id': self.carrier_id,
- 'default_total_weight': self._get_estimated_weight()
- }
+ "name": name,
+ "type": "ir.actions.act_window",
+ "view_mode": "form",
+ "res_model": "choose.delivery.carrier",
+ "view_id": view_id,
+ "views": [(view_id, "form")],
+ "target": "new",
+ "context": {
+ "default_order_id": self.id,
+ "default_carrier_id": self.carrier_id,
+ "default_total_weight": self._get_estimated_weight(),
+ },
}
def _action_confirm(self):
@@ -153,51 +176,59 @@ def _action_confirm(self):
continue
# Retrieve all the data : name, street, city, state, zip, country.
- name = order_location.get('name') or order.partner_shipping_id.name
- street = order_location['street']
- city = order_location['city']
- zip_code = order_location['zip_code']
- country_code = order_location['country_code']
- country = order.env['res.country'].search([('code', '=', country_code)]).id
- state = order.env['res.country.state'].search([
- ('code', '=', order_location['state']),
- ('country_id', '=', country),
- ]).id if (order_location.get('state') and country) else None
+ name = order_location.get("name") or order.partner_shipping_id.name
+ street = order_location["street"]
+ city = order_location["city"]
+ zip_code = order_location["zip_code"]
+ country_code = order_location["country_code"]
+ country = order.env["res.country"].search([("code", "=", country_code)]).id
+ state = None
+ if order_location.get("state") and country:
+ state = (
+ order.env["res.country.state"]
+ .search([("code", "=", order_location["state"]), ("country_id", "=", country)])
+ .id
+ )
parent_id = order.partner_shipping_id.id
email = order.partner_shipping_id.email
phone = order.partner_shipping_id.phone
# Check if the current partner has a partner of type 'delivery' with the same address.
- existing_partner = order.env['res.partner'].search([
- ('street', '=', street),
- ('city', '=', city),
- ('state_id', '=', state),
- ('country_id', '=', country),
- ('parent_id', '=', parent_id),
- ('type', '=', 'delivery'),
- ], limit=1)
-
- shipping_partner = existing_partner or order.env['res.partner'].create({
- 'parent_id': parent_id,
- 'type': 'delivery',
- 'name': name,
- 'street': street,
- 'city': city,
- 'state_id': state,
- 'zip': zip_code,
- 'country_id': country,
- 'email': email,
- 'phone': phone,
- 'is_pickup_location': True,
+ existing_partner = order.env["res.partner"].search(
+ [
+ ("street", "=", street),
+ ("city", "=", city),
+ ("state_id", "=", state),
+ ("country_id", "=", country),
+ ("parent_id", "=", parent_id),
+ ("type", "=", "delivery"),
+ ],
+ limit=1,
+ )
+
+ shipping_partner = existing_partner or order.env["res.partner"].create({
+ "parent_id": parent_id,
+ "type": "delivery",
+ "name": name,
+ "street": street,
+ "city": city,
+ "state_id": state,
+ "zip": zip_code,
+ "country_id": country,
+ "email": email,
+ "phone": phone,
+ "is_pickup_location": True,
+ })
+ order.with_context(update_delivery_shipping_partner=True).write({
+ "partner_shipping_id": shipping_partner
})
- order.with_context(update_delivery_shipping_partner=True).write({'partner_shipping_id': shipping_partner})
return super()._action_confirm()
def _prepare_delivery_line_vals(self, carrier, price_unit):
context = {}
if self.partner_id:
# set delivery detail in the customer language
- context['lang'] = self.partner_id.lang
+ context["lang"] = self.partner_id.lang
carrier = carrier.with_context(lang=self.partner_id.lang)
# Apply fiscal position
@@ -209,31 +240,30 @@ def _prepare_delivery_line_vals(self, carrier, price_unit):
# Create the sales order line
if carrier.product_id.description_sale:
- so_description = '%s: %s' % (carrier.name,
- carrier.product_id.description_sale)
+ so_description = "%s: %s" % (carrier.name, carrier.product_id.description_sale)
else:
so_description = carrier.name
values = {
- 'order_id': self.id,
- 'name': so_description,
- 'price_unit': price_unit,
- 'product_uom_qty': 1,
- 'product_id': carrier.product_id.id,
- 'tax_ids': [(6, 0, taxes_ids)],
- 'is_delivery': True,
+ "order_id": self.id,
+ "name": so_description,
+ "price_unit": price_unit,
+ "product_uom_qty": 1,
+ "product_id": carrier.product_id.id,
+ "tax_ids": [(6, 0, taxes_ids)],
+ "is_delivery": True,
}
- if carrier.free_over and self.currency_id.is_zero(price_unit) :
- values['name'] = _('%s\nFree Shipping', values['name'])
+ if carrier.free_over and self.currency_id.is_zero(price_unit):
+ values["name"] = _("%s\nFree Shipping", values["name"])
if self.order_line:
- values['sequence'] = self.order_line[-1].sequence + 1
+ values["sequence"] = self.order_line[-1].sequence + 1
del context
return values
def _create_delivery_line(self, carrier, price_unit):
values = self._prepare_delivery_line_vals(carrier, price_unit)
- return self.env['sale.order.line'].sudo().create(values)
+ return self.env["sale.order.line"].sudo().create(values)
- @api.depends('order_line.product_uom_qty', 'order_line.product_uom_id')
+ @api.depends("order_line.product_uom_qty", "order_line.product_uom_id")
def _compute_shipping_weight(self):
for order in self:
order.shipping_weight = order._get_estimated_weight()
@@ -241,12 +271,17 @@ def _compute_shipping_weight(self):
def _get_estimated_weight(self):
self.ensure_one()
weight = 0.0
- for order_line in self.order_line.filtered(lambda l: l.product_id.type == 'consu' and not l.is_delivery and not l.display_type and l.product_uom_qty > 0):
+ for order_line in self.order_line.filtered(
+ lambda ol: ol.product_id.type == "consu"
+ and not ol.is_delivery
+ and not ol.display_type
+ and ol.product_uom_qty > 0
+ ):
weight += order_line.product_qty * order_line.product_id.weight
return weight
def _update_order_line_info(self, product_id, quantity, **kwargs):
- """ Override of `sale` to recompute the delivery prices.
+ """Override of `sale` to recompute the delivery prices.
:param int product_id: The product, as a `product.product` id.
:return: The unit price price of the product, based on the pricelist of the sale order and
diff --git a/addons/delivery/models/sale_order_line.py b/addons/delivery/models/sale_order_line.py
index ccd6b448d048f..aad9a66e19469 100644
--- a/addons/delivery/models/sale_order_line.py
+++ b/addons/delivery/models/sale_order_line.py
@@ -4,18 +4,18 @@
class SaleOrderLine(models.Model):
- _inherit = 'sale.order.line'
+ _inherit = "sale.order.line"
is_delivery = fields.Boolean(string="Is a Delivery", default=False)
product_qty = fields.Float(
- string='Product Qty', compute='_compute_product_qty', digits='Product Unit'
+ string="Product Qty", compute="_compute_product_qty", digits="Product Unit"
)
- recompute_delivery_price = fields.Boolean(related='order_id.recompute_delivery_price')
+ recompute_delivery_price = fields.Boolean(related="order_id.recompute_delivery_price")
def _can_be_invoiced_alone(self):
return super()._can_be_invoiced_alone() and not self.is_delivery
- @api.depends('product_id', 'product_uom_id', 'product_uom_qty')
+ @api.depends("product_id", "product_uom_id", "product_uom_qty")
def _compute_product_qty(self):
for line in self:
if not line.product_id or not line.product_uom_id or not line.product_uom_qty:
@@ -26,7 +26,7 @@ def _compute_product_qty(self):
)
def unlink(self):
- self.filtered('is_delivery').order_id.filtered('carrier_id').carrier_id = False
+ self.filtered("is_delivery").order_id.filtered("carrier_id").carrier_id = False
return super().unlink()
def _is_delivery(self):
@@ -36,10 +36,9 @@ def _is_delivery(self):
def _get_invalid_delivery_weight_lines(self):
"""Retrieve lines containing physical products with no weight defined."""
return self.filtered(
- lambda line:
- line.product_qty > 0
- and line.product_id.type not in ('service', 'combo')
- and line.product_id.weight == 0,
+ lambda line: line.product_qty > 0
+ and line.product_id.type not in ("service", "combo")
+ and line.product_id.weight == 0
)
# override to allow deletion of delivery line in a confirmed order
@@ -52,11 +51,10 @@ def _check_line_unlink(self):
:rtype: recordset sale.order.line
:returns: set of lines that cannot be deleted
"""
-
undeletable_lines = super()._check_line_unlink()
return undeletable_lines.filtered(lambda line: not line.is_delivery)
def _compute_pricelist_item_id(self):
- delivery_lines = self.filtered('is_delivery')
+ delivery_lines = self.filtered("is_delivery")
super(SaleOrderLine, self - delivery_lines)._compute_pricelist_item_id()
delivery_lines.pricelist_item_id = False
diff --git a/addons/delivery/tests/__init__.py b/addons/delivery/tests/__init__.py
index 59a0c5f869917..7f36e7d33c491 100644
--- a/addons/delivery/tests/__init__.py
+++ b/addons/delivery/tests/__init__.py
@@ -1,7 +1,9 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import test_delivery_cost
-from . import test_delivery_availability
-from . import test_payment_provider
-from . import test_payment_transaction
-from . import test_sale_order
+from . import (
+ test_delivery_availability,
+ test_delivery_cost,
+ test_payment_provider,
+ test_payment_transaction,
+ test_sale_order,
+)
diff --git a/addons/delivery/tests/cash_on_delivery_common.py b/addons/delivery/tests/cash_on_delivery_common.py
index 0c3013595a37a..2106a151fa158 100644
--- a/addons/delivery/tests/cash_on_delivery_common.py
+++ b/addons/delivery/tests/cash_on_delivery_common.py
@@ -5,12 +5,11 @@
class CashOnDeliveryCommon(PaymentCustomCommon, DeliveryCommon):
-
@classmethod
def setUpClass(cls):
super().setUpClass()
- cls.sale_order = cls.env['sale.order'].create({
- 'partner_id': cls.partner.id,
- 'state': 'draft',
+ cls.sale_order = cls.env["sale.order"].create({
+ "partner_id": cls.partner.id,
+ "state": "draft",
})
- cls.cod_provider = cls._prepare_provider(code='custom', custom_mode='cash_on_delivery')
+ cls.cod_provider = cls._prepare_provider(code="custom", custom_mode="cash_on_delivery")
diff --git a/addons/delivery/tests/common.py b/addons/delivery/tests/common.py
index aee1639fb500c..c2e98575d120d 100644
--- a/addons/delivery/tests/common.py
+++ b/addons/delivery/tests/common.py
@@ -4,13 +4,12 @@
class DeliveryCommon(TransactionCase):
-
@classmethod
def setUpClass(cls):
super().setUpClass()
- cls.env['delivery.carrier'].search([]).action_archive()
- cls.delivery_categ = cls.env.ref('delivery.product_category_deliveries')
+ cls.env["delivery.carrier"].search([]).action_archive()
+ cls.delivery_categ = cls.env.ref("delivery.product_category_deliveries")
product = cls._prepare_carrier_product()
cls.free_delivery = cls._prepare_carrier(product, fixed_price=0.0)
@@ -19,22 +18,22 @@ def setUpClass(cls):
@classmethod
def _prepare_carrier_product(cls, **values):
default_values = {
- 'name': "Carrier Product",
- 'type': 'service',
- 'categ_id': cls.delivery_categ.id,
- 'sale_ok': False,
- 'purchase_ok': False,
- 'invoice_policy': 'order',
- 'list_price': 5.0,
+ "name": "Carrier Product",
+ "type": "service",
+ "categ_id": cls.delivery_categ.id,
+ "sale_ok": False,
+ "purchase_ok": False,
+ "invoice_policy": "order",
+ "list_price": 5.0,
}
- return cls.env['product.product'].create(dict(default_values, **values))
+ return cls.env["product.product"].create(dict(default_values, **values))
@classmethod
def _prepare_carrier(cls, product, **values):
default_values = {
- 'name': "Test Carrier",
- 'fixed_price': 5.0,
- 'delivery_type': 'fixed',
- 'product_id': product.id,
+ "name": "Test Carrier",
+ "fixed_price": 5.0,
+ "delivery_type": "fixed",
+ "product_id": product.id,
}
- return cls.env['delivery.carrier'].create(dict(default_values, **values))
+ return cls.env["delivery.carrier"].create(dict(default_values, **values))
diff --git a/addons/delivery/tests/test_delivery_availability.py b/addons/delivery/tests/test_delivery_availability.py
index 52746817d3f16..ad53aa13987f9 100644
--- a/addons/delivery/tests/test_delivery_availability.py
+++ b/addons/delivery/tests/test_delivery_availability.py
@@ -7,190 +7,209 @@
from odoo.addons.sale.tests.common import SaleCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestDeliveryAvailability(DeliveryCommon, SaleCommon):
-
@classmethod
def setUpClass(cls):
super().setUpClass()
- cls.must_have_tag = cls.env['product.tag'].create({
- 'name': 'Must Have',
- })
- cls.exclude_tag = cls.env['product.tag'].create({
- 'name': 'Exclude',
- })
+ cls.must_have_tag = cls.env["product.tag"].create({"name": "Must Have"})
+ cls.exclude_tag = cls.env["product.tag"].create({"name": "Exclude"})
cls.non_restricted_carrier = cls._prepare_carrier(cls.carrier.product_id)
cls.product_line = cls.sale_order.order_line.filtered(
- lambda sol: sol.product_id == cls.product,
+ lambda sol: sol.product_id == cls.product
)
cls.product_line.product_uom_qty = 1.0
def test_00_order_with_heavy_product_simple(self):
- self.carrier.write({
- 'max_weight': 10.0,
- })
+ self.carrier.write({"max_weight": 10.0})
- self.product.write({
- 'weight': 11.0,
- })
+ self.product.write({"weight": 11.0})
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
- self.assertFalse(self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids, "Product weight exceeds carrier's max weight")
+ self.assertFalse(
+ self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids,
+ "Product weight exceeds carrier's max weight",
+ )
def test_01_order_with_heavy_product_different_uom(self):
- self.carrier.write({
- 'max_weight': 10.0,
- })
+ self.carrier.write({"max_weight": 10.0})
- self.product.write({
- 'weight': 1.0,
- })
+ self.product.write({"weight": 1.0})
- self.product_line.write({
- 'product_uom_id': self.uom_dozen.id,
- })
+ self.product_line.write({"product_uom_id": self.uom_dozen.id})
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
- self.assertFalse(self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids, "Order lines should be converted to the default UoM before checking weight")
+ self.assertFalse(
+ self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids,
+ "Order lines should be converted to the default UoM before checking weight",
+ )
def test_02_order_with_big_product_simple(self):
- self.carrier.write({
- 'max_volume': 10.0,
- })
+ self.carrier.write({"max_volume": 10.0})
- self.product.write({
- 'volume': 11.0,
- })
+ self.product.write({"volume": 11.0})
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
- self.assertFalse(self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids, "Product volume exceeds carrier's max volume")
+ self.assertFalse(
+ self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids,
+ "Product volume exceeds carrier's max volume",
+ )
def test_03_order_with_big_product_different_uom(self):
- self.carrier.write({
- 'max_volume': 10.0,
- })
+ self.carrier.write({"max_volume": 10.0})
- self.product.write({
- 'volume': 1.0,
- })
+ self.product.write({"volume": 1.0})
- self.product_line.write({
- 'product_uom_id': self.uom_dozen.id,
- })
+ self.product_line.write({"product_uom_id": self.uom_dozen.id})
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
- self.assertFalse(self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids, "Order lines should be converted to the default UoM before checking volume")
+ self.assertFalse(
+ self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids,
+ "Order lines should be converted to the default UoM before checking volume",
+ )
def test_04_check_must_have_tag(self):
self.carrier.must_have_tag_ids = [
Command.link(self.must_have_tag.id),
- Command.link(self.must_have_tag.copy({'name': "Alt Must Have"}).id),
+ Command.link(self.must_have_tag.copy({"name": "Alt Must Have"}).id),
]
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
- self.assertFalse(self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids, "Delivery method's must have tag is not set on any product in the order")
+ self.assertFalse(
+ self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids,
+ "Delivery method's must have tag is not set on any product in the order",
+ )
- self.product.write({
- 'product_tag_ids': [self.must_have_tag.id],
- })
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ self.product.write({"product_tag_ids": [self.must_have_tag.id]})
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
self.assertIn(
self.carrier,
choose_delivery_carrier.available_carrier_ids,
- "Delivery method should be available if at least one must-have tag is present in the products",
+ "Delivery method should be available if at least one must-have tag is present in the"
+ " products",
)
def test_05_check_excluded_tag(self):
- self.carrier.write({
- 'excluded_tag_ids': [self.exclude_tag.id],
- })
+ self.carrier.write({"excluded_tag_ids": [self.exclude_tag.id]})
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
- self.assertTrue(self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids, "Delivery method's excluded tag is not set on any product in the order")
+ self.assertTrue(
+ self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids,
+ "Delivery method's excluded tag is not set on any product in the order",
+ )
- self.product.write({
- 'product_tag_ids': [self.exclude_tag.id],
- })
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ self.product.write({"product_tag_ids": [self.exclude_tag.id]})
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
- self.assertFalse(self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids, "Delivery method's excluded tag is set on one product in the order")
+ self.assertFalse(
+ self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids,
+ "Delivery method's excluded tag is set on one product in the order",
+ )
def test_06_check_tags_complex(self):
self.carrier.write({
- 'must_have_tag_ids': [self.must_have_tag.id],
- 'excluded_tag_ids': [self.exclude_tag.id],
+ "must_have_tag_ids": [self.must_have_tag.id],
+ "excluded_tag_ids": [self.exclude_tag.id],
})
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
- self.assertFalse(self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids, "Delivery method's must have tag is not set on any product in the order")
+ self.assertFalse(
+ self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids,
+ "Delivery method's must have tag is not set on any product in the order",
+ )
- self.product.write({
- 'product_tag_ids': [self.must_have_tag.id],
- })
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ self.product.write({"product_tag_ids": [self.must_have_tag.id]})
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
- self.assertTrue(self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids, "Delivery method's must have tag is set on one product in the order")
+ self.assertTrue(
+ self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids,
+ "Delivery method's must have tag is set on one product in the order",
+ )
- self.product.write({
- 'product_tag_ids': [self.exclude_tag.id, self.must_have_tag.id],
- })
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ self.product.write({"product_tag_ids": [self.exclude_tag.id, self.must_have_tag.id]})
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
- self.assertFalse(self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids, "Delivery method's excluded tag is set on one product in the order")
+ self.assertFalse(
+ self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids,
+ "Delivery method's excluded tag is set on one product in the order",
+ )
- self.product.write({
- 'product_tag_ids': [self.must_have_tag.id],
- })
- self.service_product.write({
- 'product_tag_ids': [self.exclude_tag.id],
- })
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_order.id,
- 'default_carrier_id': self.non_restricted_carrier.id,
- }))
+ self.product.write({"product_tag_ids": [self.must_have_tag.id]})
+ self.service_product.write({"product_tag_ids": [self.exclude_tag.id]})
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_order.id,
+ "default_carrier_id": self.non_restricted_carrier.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
- self.assertFalse(self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids, "Delivery method's excluded tag is set on one product in the order")
+ self.assertFalse(
+ self.carrier.id in choose_delivery_carrier.available_carrier_ids.ids,
+ "Delivery method's excluded tag is set on one product in the order",
+ )
diff --git a/addons/delivery/tests/test_delivery_cost.py b/addons/delivery/tests/test_delivery_cost.py
index 08555ec4565d6..db3e5e7c87d5b 100644
--- a/addons/delivery/tests/test_delivery_cost.py
+++ b/addons/delivery/tests/test_delivery_cost.py
@@ -10,9 +10,8 @@
from odoo.addons.sale.tests.common import SaleCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestDeliveryCost(DeliveryCommon, SaleCommon):
-
@classmethod
def setUpClass(cls):
super().setUpClass()
@@ -20,224 +19,257 @@ def setUpClass(cls):
cls._enable_uom()
# the tests hereunder assume all the prices in USD
- cls.env.company.country_id = cls.env.ref('base.us').id
+ cls.env.company.country_id = cls.env.ref("base.us").id
cls.product.weight = 1.0
cls.product_delivery_normal = cls._prepare_carrier_product(
- name='Normal Delivery Charges',
- list_price=10.0,
+ name="Normal Delivery Charges", list_price=10.0
)
cls.normal_delivery = cls._prepare_carrier(
product=cls.product_delivery_normal,
- name='Normal Delivery Charges',
- delivery_type='fixed',
+ name="Normal Delivery Charges",
+ delivery_type="fixed",
fixed_price=10.0,
)
- cls.partner_4 = cls.env['res.partner'].create({
- 'name': 'Another Customer',
- 'child_ids': [
- Command.create({
- 'name': "Another Customer's Address",
- })
- ]
+ cls.partner_4 = cls.env["res.partner"].create({
+ "name": "Another Customer",
+ "child_ids": [Command.create({"name": "Another Customer's Address"})],
})
cls.partner_address_13 = cls.partner_4.child_ids
- cls.product_uom_hour = cls.env.ref('uom.product_uom_hour')
+ cls.product_uom_hour = cls.env.ref("uom.product_uom_hour")
def test_00_delivery_cost(self):
# In order to test Carrier Cost
# Create sales order with Normal Delivery Charges
- self.sale_normal_delivery_charges = self.env['sale.order'].create({
- 'partner_id': self.partner.id,
- 'partner_invoice_id': self.partner.id,
- 'partner_shipping_id': self.partner.id,
- 'order_line': [
- Command.create({
- 'product_id': self.product.id,
- 'price_unit': 750.00,
- })
- ],
+ self.sale_normal_delivery_charges = self.env["sale.order"].create({
+ "partner_id": self.partner.id,
+ "partner_invoice_id": self.partner.id,
+ "partner_shipping_id": self.partner.id,
+ "order_line": [Command.create({"product_id": self.product.id, "price_unit": 750.00})],
})
# I add delivery cost in Sales order
- self.a_sale = self.env['account.account'].create({
- 'code': 'X2020',
- 'name': 'Product Sales - (test)',
- 'account_type': 'income',
- 'tag_ids': [Command.set(self.env.ref('account.account_tag_operating').ids)]
+ self.a_sale = self.env["account.account"].create({
+ "code": "X2020",
+ "name": "Product Sales - (test)",
+ "account_type": "income",
+ "tag_ids": [Command.set(self.env.ref("account.account_tag_operating").ids)],
})
- self.product_consultant = self.env['product.product'].create({
- 'sale_ok': True,
- 'list_price': 75.0,
- 'standard_price': 30.0,
- 'uom_id': self.product_uom_hour.id,
- 'name': 'Service',
- 'type': 'service'
+ self.product_consultant = self.env["product.product"].create({
+ "sale_ok": True,
+ "list_price": 75.0,
+ "standard_price": 30.0,
+ "uom_id": self.product_uom_hour.id,
+ "name": "Service",
+ "type": "service",
})
# I add delivery cost in Sales order
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.sale_normal_delivery_charges.id,
- 'default_carrier_id': self.normal_delivery.id
- }))
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.sale_normal_delivery_charges.id,
+ "default_carrier_id": self.normal_delivery.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
choose_delivery_carrier.button_confirm()
# I check sales order after added delivery cost
line = self.sale_normal_delivery_charges.order_line.filtered_domain([
- ('product_id', '=', self.normal_delivery.product_id.id)])
+ ("product_id", "=", self.normal_delivery.product_id.id)
+ ])
self.assertEqual(len(line), 1, "Delivery cost is not Added")
- zin = str(delivery_wizard.display_price) + " " + str(delivery_wizard.delivery_price) + ' ' + line.company_id.country_id.code + line.company_id.name
- self.assertEqual(float_compare(line.price_subtotal, 10.0, precision_digits=2), 0,
- "Delivery cost does not correspond to 10.0. %s %s" % (line.price_subtotal, zin))
+ zin = (
+ str(delivery_wizard.display_price)
+ + " "
+ + str(delivery_wizard.delivery_price)
+ + " "
+ + line.company_id.country_id.code
+ + line.company_id.name
+ )
+ self.assertEqual(
+ float_compare(line.price_subtotal, 10.0, precision_digits=2),
+ 0,
+ "Delivery cost does not correspond to 10.0. %s %s" % (line.price_subtotal, zin),
+ )
# I confirm the sales order
self.sale_normal_delivery_charges.action_confirm()
# Create one more sales order with Free Delivery Charges
- self.delivery_sale_order_cost = self.env['sale.order'].create({
- 'partner_id': self.partner_4.id,
- 'partner_invoice_id': self.partner_address_13.id,
- 'partner_shipping_id': self.partner_address_13.id,
- 'order_line': [
+ self.delivery_sale_order_cost = self.env["sale.order"].create({
+ "partner_id": self.partner_4.id,
+ "partner_invoice_id": self.partner_address_13.id,
+ "partner_shipping_id": self.partner_address_13.id,
+ "order_line": [
Command.create({
- 'product_id': self.product_consultant.id,
- 'product_uom_qty': 24,
- 'price_unit': 75.00,
+ "product_id": self.product_consultant.id,
+ "product_uom_qty": 24,
+ "price_unit": 75.00,
}),
Command.create({
- 'product_id': self.product.id,
- 'product_uom_qty': 30,
- 'price_unit': 38.25,
- })
+ "product_id": self.product.id,
+ "product_uom_qty": 30,
+ "price_unit": 38.25,
+ }),
],
})
# I add free delivery cost in Sales order
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': self.delivery_sale_order_cost.id,
- 'default_carrier_id': self.free_delivery.id
- }))
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": self.delivery_sale_order_cost.id,
+ "default_carrier_id": self.free_delivery.id,
+ })
+ )
choose_delivery_carrier = delivery_wizard.save()
choose_delivery_carrier.button_confirm()
# I check sales order after adding delivery cost
line = self.delivery_sale_order_cost.order_line.filtered_domain([
- ('product_id', '=', self.free_delivery.product_id.id)])
+ ("product_id", "=", self.free_delivery.product_id.id)
+ ])
self.assertEqual(len(line), 1, "Delivery cost is not Added")
- self.assertEqual(float_compare(line.price_subtotal, 0, precision_digits=2), 0,
- "Delivery cost is not correspond.")
+ self.assertEqual(
+ float_compare(line.price_subtotal, 0, precision_digits=2),
+ 0,
+ "Delivery cost is not correspond.",
+ )
# I set default delivery policy
- self.env['res.config.settings'].create({}).execute()
+ self.env["res.config.settings"].create({}).execute()
def test_01_delivery_cost_from_pricelist(self):
- """ This test aims to validate the use of a pricelist to compute the delivery cost in the case the associated
- product of the delivery method is defined in the pricelist """
-
+ """This test aims to validate the use of a pricelist to compute the delivery cost in the
+ case the associated product of the delivery method is defined in the pricelist."""
# Create pricelist with a custom price for the standard delivery method
- my_pricelist = self.env['product.pricelist'].create({
- 'name': 'shipping_cost_change',
- 'item_ids': [Command.create({
- 'compute_price': 'fixed',
- 'fixed_price': 5,
- 'applied_on': '0_product_variant',
- 'product_id': self.normal_delivery.product_id.id,
- })],
+ my_pricelist = self.env["product.pricelist"].create({
+ "name": "shipping_cost_change",
+ "item_ids": [
+ Command.create({
+ "compute_price": "fixed",
+ "fixed_price": 5,
+ "applied_on": "0_product_variant",
+ "product_id": self.normal_delivery.product_id.id,
+ })
+ ],
})
# Create sales order with Normal Delivery Charges
- sale_pricelist_based_delivery_charges = self.env['sale.order'].create({
- 'partner_id': self.partner.id,
- 'pricelist_id': my_pricelist.id,
- 'order_line': [Command.create({
- 'product_id': self.product.id,
- 'product_uom_qty': 1,
- 'price_unit': 750.00,
- })],
+ sale_pricelist_based_delivery_charges = self.env["sale.order"].create({
+ "partner_id": self.partner.id,
+ "pricelist_id": my_pricelist.id,
+ "order_line": [
+ Command.create({
+ "product_id": self.product.id,
+ "product_uom_qty": 1,
+ "price_unit": 750.00,
+ })
+ ],
})
# Add of delivery cost in Sales order
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': sale_pricelist_based_delivery_charges.id,
- 'default_carrier_id': self.normal_delivery.id
- }))
- self.assertEqual(delivery_wizard.delivery_price, 5.0, "Delivery cost does not correspond to 5.0 in wizard")
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": sale_pricelist_based_delivery_charges.id,
+ "default_carrier_id": self.normal_delivery.id,
+ })
+ )
+ self.assertEqual(
+ delivery_wizard.delivery_price,
+ 5.0,
+ "Delivery cost does not correspond to 5.0 in wizard",
+ )
delivery_wizard.save().button_confirm()
line = sale_pricelist_based_delivery_charges.order_line.filtered_domain([
- ('product_id', '=', self.normal_delivery.product_id.id)])
+ ("product_id", "=", self.normal_delivery.product_id.id)
+ ])
self.assertEqual(len(line), 1, "Delivery cost hasn't been added to SO")
self.assertEqual(line.price_subtotal, 5.0, "Delivery cost does not correspond to 5.0")
def test_02_delivery_cost_from_different_currency(self):
- """ This test aims to validate the use of a pricelist using a different currency to compute the delivery cost in
- the case the associated product of the delivery method is defined in the pricelist """
-
+ """This test aims to validate the use of a pricelist using a different currency to compute
+ the delivery cost in the case the associated product of the delivery method is defined in
+ the pricelist."""
# Create pricelist with a custom price for the standard delivery method
- my_pricelist = self.env['product.pricelist'].create({
- 'name': 'shipping_cost_change',
- 'item_ids': [Command.create({
- 'compute_price': 'fixed',
- 'fixed_price': 5,
- 'applied_on': '0_product_variant',
- 'product_id': self.normal_delivery.product_id.id,
- })],
- 'currency_id': self.env.ref('base.EUR').id,
+ my_pricelist = self.env["product.pricelist"].create({
+ "name": "shipping_cost_change",
+ "item_ids": [
+ Command.create({
+ "compute_price": "fixed",
+ "fixed_price": 5,
+ "applied_on": "0_product_variant",
+ "product_id": self.normal_delivery.product_id.id,
+ })
+ ],
+ "currency_id": self.env.ref("base.EUR").id,
})
# Create sales order with Normal Delivery Charges
- sale_pricelist_based_delivery_charges = self.env['sale.order'].create({
- 'partner_id': self.partner.id,
- 'pricelist_id': my_pricelist.id,
- 'order_line': [Command.create({
- 'product_id': self.product.id,
- 'product_uom_qty': 1,
- 'price_unit': 750.00,
- })],
+ sale_pricelist_based_delivery_charges = self.env["sale.order"].create({
+ "partner_id": self.partner.id,
+ "pricelist_id": my_pricelist.id,
+ "order_line": [
+ Command.create({
+ "product_id": self.product.id,
+ "product_uom_qty": 1,
+ "price_unit": 750.00,
+ })
+ ],
})
# Add of delivery cost in Sales order
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({
- 'default_order_id': sale_pricelist_based_delivery_charges.id,
- 'default_carrier_id': self.normal_delivery.id
- }))
- self.assertEqual(delivery_wizard.delivery_price, 5.0, "Delivery cost does not correspond to 5.0 in wizard")
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context({
+ "default_order_id": sale_pricelist_based_delivery_charges.id,
+ "default_carrier_id": self.normal_delivery.id,
+ })
+ )
+ self.assertEqual(
+ delivery_wizard.delivery_price,
+ 5.0,
+ "Delivery cost does not correspond to 5.0 in wizard",
+ )
delivery_wizard.save().button_confirm()
line = sale_pricelist_based_delivery_charges.order_line.filtered_domain([
- ('product_id', '=', self.normal_delivery.product_id.id)])
+ ("product_id", "=", self.normal_delivery.product_id.id)
+ ])
self.assertEqual(len(line), 1, "Delivery cost hasn't been added to SO")
self.assertEqual(line.price_subtotal, 5.0, "Delivery cost does not correspond to 5.0")
def test_01_taxes_on_delivery_cost(self):
# Creating taxes and fiscal position
- self.env.ref('base.group_user').write({'implied_ids': [(4, self.env.ref('product.group_product_pricelist').id)]})
-
- fiscal_position = self.env['account.fiscal.position'].create({
- 'name': 'fiscal_pos_a',
+ self.env.ref("base.group_user").write({
+ "implied_ids": [(4, self.env.ref("product.group_product_pricelist").id)]
})
- tax_price_include, tax_price_exclude = self.env['account.tax'].create([{
- 'name': '10% inc',
- 'type_tax_use': 'sale',
- 'amount_type': 'percent',
- 'amount': 10,
- 'price_include_override': 'tax_included',
- 'include_base_amount': True,
- }, {
- 'name': '15% exc',
- 'type_tax_use': 'sale',
- 'amount_type': 'percent',
- 'amount': 15,
- 'fiscal_position_ids': [Command.link(fiscal_position.id)],
- }])
+
+ fiscal_position = self.env["account.fiscal.position"].create({"name": "fiscal_pos_a"})
+ tax_price_include, tax_price_exclude = self.env["account.tax"].create([
+ {
+ "name": "10% inc",
+ "type_tax_use": "sale",
+ "amount_type": "percent",
+ "amount": 10,
+ "price_include_override": "tax_included",
+ "include_base_amount": True,
+ },
+ {
+ "name": "15% exc",
+ "type_tax_use": "sale",
+ "amount_type": "percent",
+ "amount": 15,
+ "fiscal_position_ids": [Command.link(fiscal_position.id)],
+ },
+ ])
tax_price_exclude.original_tax_ids = tax_price_include
# Setting tax on delivery product
@@ -245,8 +277,8 @@ def test_01_taxes_on_delivery_cost(self):
# Create sales order
# Required to see `pricelist_id` in the view
- self.env.user.group_ids += self.env.ref('product.group_product_pricelist')
- order_form = Form(self.env['sale.order'].with_context(tracking_disable=True))
+ self.env.user.group_ids += self.env.ref("product.group_product_pricelist")
+ order_form = Form(self.env["sale.order"].with_context(tracking_disable=True))
order_form.partner_id = self.partner
order_form.fiscal_position_id = fiscal_position
@@ -256,132 +288,138 @@ def test_01_taxes_on_delivery_cost(self):
line.product_uom_qty = 1.0
sale_order = order_form.save()
- self.assertRecordValues(sale_order.order_line, [{'price_subtotal': 9.09, 'price_total': 10.45}])
+ self.assertRecordValues(
+ sale_order.order_line, [{"price_subtotal": 9.09, "price_total": 10.45}]
+ )
- # Now trying to add the delivery line using the delivery wizard, the results should be the same as before
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context(default_order_id=sale_order.id,
- default_carrier_id=self.normal_delivery.id))
+ # Now trying to add the delivery line using the delivery wizard, the results should be the
+ # same as before
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context(
+ default_order_id=sale_order.id, default_carrier_id=self.normal_delivery.id
+ )
+ )
choose_delivery_carrier = delivery_wizard.save()
choose_delivery_carrier.button_confirm()
line = sale_order.order_line.filtered_domain([
- ('product_id', '=', self.normal_delivery.product_id.id),
- ('is_delivery', '=', True),
+ ("product_id", "=", self.normal_delivery.product_id.id),
+ ("is_delivery", "=", True),
])
- self.assertRecordValues(line, [{'price_subtotal': 9.09, 'price_total': 10.45}])
+ self.assertRecordValues(line, [{"price_subtotal": 9.09, "price_total": 10.45}])
def test_estimated_weight(self):
"""
Test that negative qty SO lines are not included in the estimated weight calculation
of delivery method (since it's used when calculating their rates).
"""
- sale_order = self.env['sale.order'].create({
- 'partner_id': self.partner.id,
- 'order_line': [
- Command.create({
- 'product_id': self.product.id,
- 'product_uom_qty': 1,
- }),
- Command.create({
- 'product_id': self.product.id,
- 'product_uom_qty': -1,
- }),
+ sale_order = self.env["sale.order"].create({
+ "partner_id": self.partner.id,
+ "order_line": [
+ Command.create({"product_id": self.product.id, "product_uom_qty": 1}),
+ Command.create({"product_id": self.product.id, "product_uom_qty": -1}),
],
})
shipping_weight = sale_order._get_estimated_weight()
- self.assertEqual(shipping_weight, self.product.weight, "Only positive quantity products' weights should be included in estimated weight")
+ self.assertEqual(
+ shipping_weight,
+ self.product.weight,
+ "Only positive quantity products' weights should be included in estimated weight",
+ )
def test_get_invalid_delivery_weight_lines(self):
"""Ensure we can retrieve lines that contain physical products without a weight value."""
order = self.empty_order
weightless_product = self._create_product(weight=0.0, list_price=50.0)
- combos = self.env['product.combo'].create([{
- 'name': "Combo A",
- 'combo_item_ids': [Command.create({'product_id': self.product.id})],
- }, {
- 'name': "Combo B",
- 'combo_item_ids': [Command.create({'product_id': weightless_product.id})],
+ combos = self.env["product.combo"].create([
+ {
+ "name": "Combo A",
+ "combo_item_ids": [Command.create({"product_id": self.product.id})],
+ },
+ {
+ "name": "Combo B",
+ "combo_item_ids": [Command.create({"product_id": weightless_product.id})],
},
])
- combo_product = self._create_product(type='combo', combo_ids=combos.ids)
- combo_line = self.env['sale.order.line'].create({
- 'order_id': order.id,
- 'product_id': combo_product.id,
+ combo_product = self._create_product(type="combo", combo_ids=combos.ids)
+ combo_line = self.env["sale.order.line"].create({
+ "order_id": order.id,
+ "product_id": combo_product.id,
})
order.order_line = [
- *[Command.create({
- 'product_id': product.id,
- 'combo_item_id': combo.combo_item_ids.id,
- 'linked_line_id': combo_line.id,
- }) for product, combo in zip(self.product + weightless_product, combos)],
- Command.create({'product_id': weightless_product.id, 'product_uom_qty': 0}),
- Command.create({'product_id': self.service_product.id}),
- Command.create({'display_type': 'line_section', 'name': "Misc."}),
- Command.create({'is_downpayment': True, 'price_unit': 5.0}),
+ *[
+ Command.create({
+ "product_id": product.id,
+ "combo_item_id": combo.combo_item_ids.id,
+ "linked_line_id": combo_line.id,
+ })
+ for product, combo in zip(self.product + weightless_product, combos)
+ ],
+ Command.create({"product_id": weightless_product.id, "product_uom_qty": 0}),
+ Command.create({"product_id": self.service_product.id}),
+ Command.create({"display_type": "line_section", "name": "Misc."}),
+ Command.create({"is_downpayment": True, "price_unit": 5.0}),
]
error_lines = order.order_line._get_invalid_delivery_weight_lines()
self.assertIn(
- weightless_product, error_lines.product_id,
+ weightless_product,
+ error_lines.product_id,
"The weightless product should be part of the erroneous lines",
)
self.assertEqual(len(error_lines), 1, "Only 1 line should have an invalid weight")
self.assertTrue(error_lines.combo_item_id, "The erroneous line should be part of a combo")
def test_fixed_price_margins(self):
- """
- margins should be ignored for fixed price carriers
- """
- sale_order = self.env['sale.order'].create({
- 'partner_id': self.partner.id,
- 'name': 'SO - fixed del',
- 'order_line': [
- (0, 0, {
- 'product_id': self.product.id,
- 'product_uom_qty': 1,
- }),
- ]
+ """Margins should be ignored for fixed price carriers."""
+ sale_order = self.env["sale.order"].create({
+ "partner_id": self.partner.id,
+ "name": "SO - fixed del",
+ "order_line": [(0, 0, {"product_id": self.product.id, "product_uom_qty": 1})],
})
self.normal_delivery.fixed_margin = 100
self.normal_delivery.margin = 4.2
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context(default_order_id=sale_order.id,
- default_carrier_id=self.normal_delivery.id))
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"].with_context(
+ default_order_id=sale_order.id, default_carrier_id=self.normal_delivery.id
+ )
+ )
choose_delivery_carrier = delivery_wizard.save()
choose_delivery_carrier.button_confirm()
- line = sale_order.order_line.filtered('is_delivery')
+ line = sale_order.order_line.filtered("is_delivery")
self.assertEqual(line.price_unit, self.normal_delivery.fixed_price)
def test_price_with_weight_volume_variable(self):
- """ Test that the price is correctly computed when the variable is weight*volume. """
+ """Test that the price is correctly computed when the variable is weight*volume."""
qty = 3
list_price = 2
volume = 2.5
weight = 1.5
- sale_order = self.env['sale.order'].create({
- 'partner_id': self.partner_4.id,
- 'order_line': [
- (0, 0, {
- 'product_id': self.env['product.product'].create({
- 'name': 'wv',
- 'weight': weight,
- 'volume': volume,
- }).id,
- 'product_uom_qty': qty,
- }),
+ sale_order = self.env["sale.order"].create({
+ "partner_id": self.partner_4.id,
+ "order_line": [
+ Command.create({
+ "product_id": self.env["product.product"]
+ .create({"name": "wv", "weight": weight, "volume": volume})
+ .id,
+ "product_uom_qty": qty,
+ })
],
})
- delivery = self.env['delivery.carrier'].create({
- 'name': 'Delivery Charges',
- 'delivery_type': 'base_on_rule',
- 'product_id': self.product_delivery_normal.id,
- 'price_rule_ids': [(0, 0, {
- 'variable': 'price',
- 'operator': '>=',
- 'max_value': 0,
- 'list_price': list_price,
- 'variable_factor': 'wv',
- })]
+ delivery = self.env["delivery.carrier"].create({
+ "name": "Delivery Charges",
+ "delivery_type": "base_on_rule",
+ "product_id": self.product_delivery_normal.id,
+ "price_rule_ids": [
+ Command.create({
+ "variable": "price",
+ "operator": ">=",
+ "max_value": 0,
+ "list_price": list_price,
+ "variable_factor": "wv",
+ })
+ ],
})
self.assertEqual(
delivery._get_price_available(sale_order),
@@ -390,198 +428,201 @@ def test_price_with_weight_volume_variable(self):
)
def test_delivery_product_taxes_on_branch(self):
- """ Check taxes populated on delivery line on branch company.
- Taxes from the branch company should be taken with a fallback on parent company.
+ """Check taxes populated on delivery line on branch company.
+ Taxes from the branch company should be taken with a fallback on parent company.
"""
company = self.env.company
- branch = self.env['res.company'].create({
- 'name': 'Branch',
- 'country_id': company.country_id.id,
- 'parent_id': company.id,
+ branch = self.env["res.company"].create({
+ "name": "Branch",
+ "country_id": company.country_id.id,
+ "parent_id": company.id,
})
# create taxes for the parent company and its branch
- tax_groups = self.env['account.tax.group'].create([{
- 'name': 'Tax Group A',
- 'company_id': company.id,
- }, {
- 'name': 'Tax Group B',
- 'company_id': branch.id,
- }])
- tax_a = self.env['account.tax'].create({
- 'name': 'Tax A',
- 'type_tax_use': 'sale',
- 'amount_type': 'percent',
- 'amount': 10,
- 'tax_group_id': tax_groups[0].id,
- 'company_id': company.id,
- })
- tax_b = self.env['account.tax'].create({
- 'name': 'Tax B',
- 'type_tax_use': 'sale',
- 'amount_type': 'percent',
- 'amount': 20,
- 'tax_group_id': tax_groups[1].id,
- 'company_id': branch.id,
+ tax_groups = self.env["account.tax.group"].create([
+ {"name": "Tax Group A", "company_id": company.id},
+ {"name": "Tax Group B", "company_id": branch.id},
+ ])
+ tax_a = self.env["account.tax"].create({
+ "name": "Tax A",
+ "type_tax_use": "sale",
+ "amount_type": "percent",
+ "amount": 10,
+ "tax_group_id": tax_groups[0].id,
+ "company_id": company.id,
+ })
+ tax_b = self.env["account.tax"].create({
+ "name": "Tax B",
+ "type_tax_use": "sale",
+ "amount_type": "percent",
+ "amount": 20,
+ "tax_group_id": tax_groups[1].id,
+ "company_id": branch.id,
})
# create delivery product with taxes from both branch and parent company
- delivery_product = self.env['product.product'].create({
- 'name': 'Delivery Product',
- 'taxes_id': [Command.set((tax_a + tax_b).ids)],
+ delivery_product = self.env["product.product"].create({
+ "name": "Delivery Product",
+ "taxes_id": [Command.set((tax_a + tax_b).ids)],
})
# create delivery
- delivery = self.env['delivery.carrier'].create({
- 'name': 'Delivery Charges',
- 'delivery_type': 'fixed',
- 'product_id': delivery_product.id,
- 'company_id': branch.id,
+ delivery = self.env["delivery.carrier"].create({
+ "name": "Delivery Charges",
+ "delivery_type": "fixed",
+ "product_id": delivery_product.id,
+ "company_id": branch.id,
})
# create a SO from Branch
- sale_order = self.env['sale.order'].create({
- 'partner_id': self.partner_4.id,
- 'company_id': branch.id,
- 'order_line': [Command.create({
- 'product_id': self.product.id,
- 'product_uom_qty': 1,
- })],
+ sale_order = self.env["sale.order"].create({
+ "partner_id": self.partner_4.id,
+ "company_id": branch.id,
+ "order_line": [Command.create({"product_id": self.product.id, "product_uom_qty": 1})],
})
# add delivery
- wizard = self.env['choose.delivery.carrier'].create({
- 'order_id': sale_order.id,
- 'carrier_id': delivery.id,
- 'company_id': branch.id,
+ wizard = self.env["choose.delivery.carrier"].create({
+ "order_id": sale_order.id,
+ "carrier_id": delivery.id,
+ "company_id": branch.id,
})
wizard.button_confirm()
- delivery_line = sale_order.order_line.filtered(lambda l: l.is_delivery)
+ delivery_line = sale_order.order_line.filtered(lambda line: line.is_delivery)
# delivery line should have taxes from the branch company
- self.assertRecordValues(delivery_line, [{'product_id': delivery_product.id, 'tax_ids': tax_b.ids}])
+ self.assertRecordValues(
+ delivery_line, [{"product_id": delivery_product.id, "tax_ids": tax_b.ids}]
+ )
# update delivery product by setting only the tax from parent company
- delivery_product.write({'taxes_id': [Command.set((tax_a).ids)]})
+ delivery_product.write({"taxes_id": [Command.set((tax_a).ids)]})
# update delivery
- wizard = self.env['choose.delivery.carrier'].create({
- 'order_id': sale_order.id,
- 'carrier_id': delivery.id,
- 'company_id': branch.id,
+ wizard = self.env["choose.delivery.carrier"].create({
+ "order_id": sale_order.id,
+ "carrier_id": delivery.id,
+ "company_id": branch.id,
})
wizard.button_confirm()
- delivery_line = sale_order.order_line.filtered(lambda l: l.is_delivery)
+ delivery_line = sale_order.order_line.filtered(lambda line: line.is_delivery)
- # delivery line should have taxes from the parent company as there is no tax from the branch company
- self.assertRecordValues(delivery_line, [{'product_id': delivery_product.id, 'tax_ids': tax_a.ids}])
+ # delivery line should have taxes from the parent company as there is no tax from the branch
+ # company
+ self.assertRecordValues(
+ delivery_line, [{"product_id": delivery_product.id, "tax_ids": tax_a.ids}]
+ )
def test_update_weight_in_shipping_when_change_quantity(self):
- product_test = self.env['product.product'].create({
- 'name': 'Test product',
- 'weight': 1,
- })
- sale_order = self.env['sale.order'].create({
- 'partner_id': self.partner.id,
- 'order_line': [
+ product_test = self.env["product.product"].create({"name": "Test product", "weight": 1})
+ sale_order = self.env["sale.order"].create({
+ "partner_id": self.partner.id,
+ "order_line": [Command.create({"product_id": product_test.id, "product_uom_qty": 10})],
+ })
+ delivery = self.env["delivery.carrier"].create({
+ "name": "Delivery Charges",
+ "delivery_type": "base_on_rule",
+ "product_id": product_test.id,
+ "price_rule_ids": [
Command.create({
- 'product_id': product_test.id,
- 'product_uom_qty': 10,
+ "variable": "weight",
+ "operator": "<=",
+ "max_value": 30,
+ "list_base_price": 5,
+ "variable_factor": "weight",
}),
- ],
- })
- delivery = self.env['delivery.carrier'].create({
- 'name': 'Delivery Charges',
- 'delivery_type': 'base_on_rule',
- 'product_id': product_test.id,
- 'price_rule_ids': [
Command.create({
- 'variable': 'weight',
- 'operator': '<=',
- 'max_value': 30,
- 'list_base_price': 5,
- 'variable_factor': 'weight',
+ "variable": "weight",
+ "operator": ">=",
+ "max_value": 60,
+ "list_base_price": 10,
+ "variable_factor": "weight",
}),
- Command.create({
- 'variable': 'weight',
- 'operator': '>=',
- 'max_value': 60,
- 'list_base_price': 10,
- 'variable_factor': 'weight',
- })
- ]
+ ],
})
del_form = sale_order.action_open_delivery_wizard()
- choose_delivery_carrier = self.env[del_form['res_model']].with_context(del_form['context']).create({
- 'carrier_id': delivery.id,
- 'order_id': sale_order.id
- })
+ choose_delivery_carrier = (
+ self.env[del_form["res_model"]]
+ .with_context(del_form["context"])
+ .create({"carrier_id": delivery.id, "order_id": sale_order.id})
+ )
choose_delivery_carrier.button_confirm()
self.assertEqual(choose_delivery_carrier.total_weight, 10)
- sale_order.order_line.write({
- 'product_uom_qty': 100,
- })
+ sale_order.order_line.write({"product_uom_qty": 100})
updated_del_form = sale_order.action_open_delivery_wizard()
- self.assertEqual(updated_del_form['context']['default_total_weight'], 100)
+ self.assertEqual(updated_del_form["context"]["default_total_weight"], 100)
def test_base_on_rule_currency_is_converted(self):
"""
For based on rules delivery method without a company, check that the price
- is converted from the main's company's currency to the current company's on SOs
+ is converted from the main's company's currency to the current company's on SOs.
"""
-
# Create a company that uses a different currency
- currency_bells = self.env['res.currency'].create({
- 'name': 'Bell',
- 'symbol': 'C',
- })
+ currency_bells = self.env["res.currency"].create({"name": "Bell", "symbol": "C"})
- nook_inc = self.env['res.company'].create({
- 'name': 'Nook inc.',
- 'currency_id': currency_bells.id,
+ nook_inc = self.env["res.company"].create({
+ "name": "Nook inc.",
+ "currency_id": currency_bells.id,
})
- with freeze_time('2000-01-01'): # Make sure the rate is in the past
- self.env['res.currency.rate'].with_company(nook_inc).create({
- 'currency_id': currency_bells.id,
- 'company_rate': 0.5,
- 'inverse_company_rate': 2,
+ with freeze_time("2000-01-01"): # Make sure the rate is in the past
+ self.env["res.currency.rate"].with_company(nook_inc).create({
+ "currency_id": currency_bells.id,
+ "company_rate": 0.5,
+ "inverse_company_rate": 2,
})
# Company less delivery method
- product_delivery_rule = self.env['product.product'].with_company(nook_inc).create({
- 'name': 'rule delivery charges',
- 'type': 'service',
- 'list_price': 10.0,
- 'categ_id': self.env.ref('delivery.product_category_deliveries').id,
- })
+ product_delivery_rule = (
+ self.env["product.product"]
+ .with_company(nook_inc)
+ .create({
+ "name": "rule delivery charges",
+ "type": "service",
+ "list_price": 10.0,
+ "categ_id": self.env.ref("delivery.product_category_deliveries").id,
+ })
+ )
- delivery = self.env['delivery.carrier'].with_company(nook_inc).create({
- 'name': 'Rule Delivery',
- 'delivery_type': 'base_on_rule',
- 'product_id': product_delivery_rule.id,
- 'price_rule_ids': [(0, 0, {
- 'variable': 'price',
- 'operator': '>=',
- 'max_value': 0,
- 'variable_factor': 'weight',
- 'list_base_price': 15,
- })],
- 'fixed_margin': 10,
- })
+ delivery = (
+ self.env["delivery.carrier"]
+ .with_company(nook_inc)
+ .create({
+ "name": "Rule Delivery",
+ "delivery_type": "base_on_rule",
+ "product_id": product_delivery_rule.id,
+ "price_rule_ids": [
+ Command.create({
+ "variable": "price",
+ "operator": ">=",
+ "max_value": 0,
+ "variable_factor": "weight",
+ "list_base_price": 15,
+ })
+ ],
+ "fixed_margin": 10,
+ })
+ )
# Create sale using the delivery method
- so = self.env['sale.order'].with_company(nook_inc).create({
- 'partner_id': self.partner_4.id,
- 'partner_invoice_id': self.partner_4.id,
- 'partner_shipping_id': self.partner_4.id,
- 'order_line': [(0, 0, {
- 'name': 'PC Assamble + 2GB RAM',
- 'product_id': self.product.id,
- 'product_uom_qty': 1,
- 'price_unit': 750.00,
- })],
- })
+ so = (
+ self.env["sale.order"]
+ .with_company(nook_inc)
+ .create({
+ "partner_id": self.partner_4.id,
+ "partner_invoice_id": self.partner_4.id,
+ "partner_shipping_id": self.partner_4.id,
+ "order_line": [
+ Command.create({
+ "name": "PC Assamble + 2GB RAM",
+ "product_id": self.product.id,
+ "product_uom_qty": 1,
+ "price_unit": 750.00,
+ })
+ ],
+ })
+ )
- delivery_wizard = Form(self.env['choose.delivery.carrier'].with_company(nook_inc).with_context({
- 'default_order_id': so.id,
- 'default_carrier_id': delivery.id,
- }))
+ delivery_wizard = Form(
+ self.env["choose.delivery.carrier"]
+ .with_company(nook_inc)
+ .with_context({"default_order_id": so.id, "default_carrier_id": delivery.id})
+ )
choose_delivery_carrier = delivery_wizard.save()
choose_delivery_carrier.button_confirm()
diff --git a/addons/delivery/tests/test_payment_provider.py b/addons/delivery/tests/test_payment_provider.py
index 9d7b6917917f8..34f9e340dfff3 100644
--- a/addons/delivery/tests/test_payment_provider.py
+++ b/addons/delivery/tests/test_payment_provider.py
@@ -5,27 +5,40 @@
from odoo.addons.delivery.tests.cash_on_delivery_common import CashOnDeliveryCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestCODPaymentProvider(CashOnDeliveryCommon):
-
def test_cod_provider_available_when_dm_cod_enabled(self):
order = self.sale_order
self.free_delivery.allow_cash_on_delivery = True
order.carrier_id = self.free_delivery
- compatible_providers = self.env['payment.provider'].sudo()._get_compatible_providers(
- self.company.id, self.partner.id, self.amount, sale_order_id=order.id
+ compatible_providers = (
+ self.env["payment.provider"]
+ .sudo()
+ ._get_compatible_providers(
+ self.company.id, self.partner.id, self.amount, sale_order_id=order.id
+ )
+ )
+ self.assertTrue(
+ any(
+ p.code == "custom" and p.custom_mode == "cash_on_delivery"
+ for p in compatible_providers
+ )
)
- self.assertTrue(any(
- p.code == 'custom' and p.custom_mode == 'cash_on_delivery' for p in compatible_providers
- ))
def test_cod_provider_unavailable_when_dm_cod_disabled(self):
order = self.sale_order
self.free_delivery.allow_cash_on_delivery = False
order.carrier_id = self.free_delivery
- compatible_providers = self.env['payment.provider'].sudo()._get_compatible_providers(
- self.company.id, self.partner.id, self.amount, sale_order_id=order.id
+ compatible_providers = (
+ self.env["payment.provider"]
+ .sudo()
+ ._get_compatible_providers(
+ self.company.id, self.partner.id, self.amount, sale_order_id=order.id
+ )
+ )
+ self.assertFalse(
+ any(
+ p.code == "custom" and p.custom_mode == "cash_on_delivery"
+ for p in compatible_providers
+ )
)
- self.assertTrue(not any(
- p.code == 'custom' and p.custom_mode == 'cash_on_delivery' for p in compatible_providers
- ))
diff --git a/addons/delivery/tests/test_payment_transaction.py b/addons/delivery/tests/test_payment_transaction.py
index 25933e4bf1f58..ef24d8592767a 100644
--- a/addons/delivery/tests/test_payment_transaction.py
+++ b/addons/delivery/tests/test_payment_transaction.py
@@ -6,21 +6,20 @@
from odoo.addons.delivery.tests.cash_on_delivery_common import CashOnDeliveryCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestCODPaymentTransaction(CashOnDeliveryCommon):
-
def test_choosing_cod_payment_confirms_order(self):
order = self.sale_order
self.free_delivery.allow_cash_on_delivery = True
order.carrier_id = self.free_delivery
tx = self._create_transaction(
- flow='direct',
+ flow="direct",
sale_order_ids=[order.id],
- state='pending',
+ state="pending",
provider_id=self.cod_provider.id,
payment_method_id=self.cod_provider.payment_method_ids.id,
)
- with mute_logger('odoo.addons.sale.models.payment_transaction'):
+ with mute_logger("odoo.addons.sale.models.payment_transaction"):
tx._post_process()
- self.assertEqual(order.state, 'sale')
+ self.assertEqual(order.state, "sale")
diff --git a/addons/delivery/tests/test_sale_order.py b/addons/delivery/tests/test_sale_order.py
index 6117883fad919..4f0705dc2bef5 100644
--- a/addons/delivery/tests/test_sale_order.py
+++ b/addons/delivery/tests/test_sale_order.py
@@ -3,10 +3,9 @@
from odoo.addons.sale.tests.common import SaleCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestSaleOrder(SaleCommon):
-
def test_avoid_setting_pickup_location_as_default_delivery_address(self):
- self._create_partner(type='delivery', parent_id=self.partner.id, is_pickup_location=True)
- so = self.env['sale.order'].create({'partner_id': self.partner.id})
+ self._create_partner(type="delivery", parent_id=self.partner.id, is_pickup_location=True)
+ so = self.env["sale.order"].create({"partner_id": self.partner.id})
self.assertFalse(so.partner_shipping_id.is_pickup_location)
diff --git a/addons/delivery/wizard/choose_delivery_carrier.py b/addons/delivery/wizard/choose_delivery_carrier.py
index a49ef08c8707b..b467143706326 100644
--- a/addons/delivery/wizard/choose_delivery_carrier.py
+++ b/addons/delivery/wizard/choose_delivery_carrier.py
@@ -5,92 +5,113 @@
class ChooseDeliveryCarrier(models.TransientModel):
- _name = 'choose.delivery.carrier'
- _description = 'Delivery Method Selection Wizard'
+ _name = "choose.delivery.carrier"
+ _description = "Delivery Method Selection Wizard"
def _get_default_weight_uom(self):
- return self.env['product.template']._get_weight_uom_name_from_ir_config_parameter()
+ return self.env["product.template"]._get_weight_uom_name_from_ir_config_parameter()
- order_id = fields.Many2one('sale.order', required=True, ondelete="cascade")
- partner_id = fields.Many2one('res.partner', related='order_id.partner_id', required=True)
+ order_id = fields.Many2one(comodel_name="sale.order", ondelete="cascade", required=True)
+ partner_id = fields.Many2one(
+ comodel_name="res.partner", related="order_id.partner_id", required=True
+ )
carrier_id = fields.Many2one(
- 'delivery.carrier',
string="Delivery Method",
- required=True,
+ comodel_name="delivery.carrier",
domain="[('id', 'in', available_carrier_ids)]",
+ required=True,
)
- delivery_type = fields.Selection(related='carrier_id.delivery_type')
+ delivery_type = fields.Selection(related="carrier_id.delivery_type")
delivery_price = fields.Float()
- display_price = fields.Float(string='Cost', readonly=True)
- currency_id = fields.Many2one('res.currency', related='order_id.currency_id')
- company_id = fields.Many2one('res.company', related='order_id.company_id')
- available_carrier_ids = fields.Many2many("delivery.carrier", compute='_compute_available_carrier', string="Available Carriers")
- invoicing_message = fields.Text(compute='_compute_invoicing_message')
+ display_price = fields.Float(string="Cost", readonly=True)
+ currency_id = fields.Many2one(comodel_name="res.currency", related="order_id.currency_id")
+ company_id = fields.Many2one(comodel_name="res.company", related="order_id.company_id")
+ available_carrier_ids = fields.Many2many(
+ string="Available Carriers",
+ comodel_name="delivery.carrier",
+ compute="_compute_available_carrier",
+ )
+ invoicing_message = fields.Text(compute="_compute_invoicing_message")
delivery_message = fields.Text(readonly=True)
- total_weight = fields.Float(string='Total Order Weight', related='order_id.shipping_weight', readonly=False)
- weight_uom_name = fields.Char(readonly=True, default=_get_default_weight_uom)
+ total_weight = fields.Float(
+ string="Total Order Weight", related="order_id.shipping_weight", readonly=False
+ )
+ weight_uom_name = fields.Char(default=_get_default_weight_uom, readonly=True)
- @api.onchange('carrier_id', 'total_weight')
+ @api.onchange("carrier_id", "total_weight")
def _onchange_carrier_id(self):
self.delivery_message = False
- if self.delivery_type in ('fixed', 'base_on_rule'):
+ if self.delivery_type in ("fixed", "base_on_rule"):
vals = self._get_delivery_rate()
- if vals.get('error_message'):
- return {'error': vals['error_message']}
+ if vals.get("error_message"):
+ return {"error": vals["error_message"]}
else:
self.display_price = 0
self.delivery_price = 0
- @api.onchange('order_id')
+ @api.onchange("order_id")
def _onchange_order_id(self):
- # fixed and base_on_rule delivery price will computed on each carrier change so no need to recompute here
- if self.carrier_id and self.order_id.delivery_set and self.delivery_type not in ('fixed', 'base_on_rule'):
+ # Fixed and base_on_rule delivery price will compute on each carrier change so no need to
+ # recompute here
+ if (
+ self.carrier_id
+ and self.order_id.delivery_set
+ and self.delivery_type not in ("fixed", "base_on_rule")
+ ):
vals = self._get_delivery_rate()
- if vals.get('error_message'):
+ if vals.get("error_message"):
warning = {
- 'title': _("%(carrier)s Error", carrier=self.carrier_id.name),
- 'message': vals['error_message'],
- 'type': 'notification',
+ "title": _("%(carrier)s Error", carrier=self.carrier_id.name),
+ "message": vals["error_message"],
+ "type": "notification",
}
- return {'warning': warning}
+ return {"warning": warning}
- @api.depends('carrier_id')
+ @api.depends("carrier_id")
def _compute_invoicing_message(self):
self.ensure_one()
self.invoicing_message = ""
- @api.depends('partner_id')
+ @api.depends("partner_id")
def _compute_available_carrier(self):
for rec in self:
- carriers = self.env['delivery.carrier'].search(self.env['delivery.carrier']._check_company_domain(rec.order_id.company_id))
- rec.available_carrier_ids = carriers.available_carriers(rec.order_id.partner_shipping_id, rec.order_id) if rec.partner_id else carriers
+ carriers = self.env["delivery.carrier"].search(
+ self.env["delivery.carrier"]._check_company_domain(rec.order_id.company_id)
+ )
+ rec.available_carrier_ids = (
+ carriers.available_carriers(rec.order_id.partner_shipping_id, rec.order_id)
+ if rec.partner_id
+ else carriers
+ )
def _get_delivery_rate(self):
- vals = self.carrier_id.with_context(order_weight=self.total_weight).rate_shipment(self.order_id)
- if vals.get('success'):
- self.delivery_message = vals.get('warning_message', False)
- self.delivery_price = vals['price']
- self.display_price = vals['carrier_price']
- return {'no_rate': vals.get('no_rate', False)}
- return {'error_message': vals['error_message']}
+ vals = self.carrier_id.with_context(order_weight=self.total_weight).rate_shipment(
+ self.order_id
+ )
+ if vals.get("success"):
+ self.delivery_message = vals.get("warning_message", False)
+ self.delivery_price = vals["price"]
+ self.display_price = vals["carrier_price"]
+ return {"no_rate": vals.get("no_rate", False)}
+ return {"error_message": vals["error_message"]}
def update_price(self):
vals = self._get_delivery_rate()
- if vals.get('error_message'):
- raise UserError(vals.get('error_message'))
+ if vals.get("error_message"):
+ raise UserError(vals.get("error_message"))
return {
- 'name': _('Add a delivery method'),
- 'type': 'ir.actions.act_window',
- 'view_mode': 'form',
- 'res_model': 'choose.delivery.carrier',
- 'res_id': self.id,
- 'target': 'new',
- 'context': vals,
+ "name": _("Add a delivery method"),
+ "type": "ir.actions.act_window",
+ "view_mode": "form",
+ "res_model": "choose.delivery.carrier",
+ "res_id": self.id,
+ "target": "new",
+ "context": vals,
}
def button_confirm(self):
self.order_id.set_delivery_line(self.carrier_id, self.delivery_price)
self.order_id.write({
- 'recompute_delivery_price': False,
- 'delivery_message': self.delivery_message,
+ "recompute_delivery_price": False,
+ "delivery_message": self.delivery_message,
})
diff --git a/addons/website_sale_collect/controllers/__init__.py b/addons/website_sale_collect/controllers/__init__.py
index 1389142f87fff..6df092ba618ae 100644
--- a/addons/website_sale_collect/controllers/__init__.py
+++ b/addons/website_sale_collect/controllers/__init__.py
@@ -1,5 +1,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import delivery
-from . import main
-from . import payment
+from . import delivery, main, payment
diff --git a/addons/website_sale_collect/controllers/delivery.py b/addons/website_sale_collect/controllers/delivery.py
index f4c06aa5f6ea7..39384856be7e0 100644
--- a/addons/website_sale_collect/controllers/delivery.py
+++ b/addons/website_sale_collect/controllers/delivery.py
@@ -6,27 +6,26 @@
class InStoreDelivery(Delivery):
-
@route()
def website_sale_get_pickup_locations(self, zip_code=None, **kwargs):
- """ Override of `website_sale` to set the pickup in store delivery method on the order in
+ """Override of `website_sale` to set the pickup in store delivery method on the order in
order to retrieve pickup locations when called from the product page. If there is no order
create a temporary one to display pickup locations.
"""
- if kwargs.get('product_id'): # Called from the product page.
+ if kwargs.get("product_id"): # Called from the product page.
order_sudo = request.cart
in_store_dm = request.website.sudo().in_store_dm_id
if not order_sudo: # Pickup location requested without a cart creation.
# Create a temporary order to fetch pickup locations.
- temp_order = request.env['sale.order'].new({'carrier_id': in_store_dm.id})
+ temp_order = request.env["sale.order"].new({"carrier_id": in_store_dm.id})
return temp_order.sudo()._get_pickup_locations(zip_code, **kwargs) # Skip super
- elif order_sudo.carrier_id.delivery_type != 'in_store':
+ if order_sudo.carrier_id.delivery_type != "in_store":
order_sudo.set_delivery_line(in_store_dm, in_store_dm.product_id.list_price)
return super().website_sale_get_pickup_locations(zip_code, **kwargs)
- @route('/shop/set_click_and_collect_location', type='jsonrpc', auth='public', website=True)
+ @route("/shop/set_click_and_collect_location", type="jsonrpc", auth="public", website=True)
def shop_set_click_and_collect_location(self, pickup_location_data):
- """ Set the pickup location and the in-store delivery method on the current order or created
+ """Set the pickup location and the in-store delivery method on the current order or created
one.
This route is called from location selector on /product and is distinct from
@@ -37,14 +36,14 @@ def shop_set_click_and_collect_location(self, pickup_location_data):
:return: None
"""
order_sudo = request.cart or request.website._create_cart()
- if order_sudo.carrier_id.delivery_type != 'in_store':
+ if order_sudo.carrier_id.delivery_type != "in_store":
in_store_dm = request.website.sudo().in_store_dm_id
order_sudo.set_delivery_line(in_store_dm, in_store_dm.product_id.list_price)
order_sudo._set_pickup_location(pickup_location_data)
def _get_additional_delivery_context(self):
- """ Override of `website_sale` to include the default pickup location data for in-store
- delivery methods with a single warehouse. """
+ """Override of `website_sale` to include the default pickup location data for in-store
+ delivery methods with a single warehouse."""
res = super()._get_additional_delivery_context()
order_sudo = request.cart
if request.website.sudo().in_store_dm_id:
@@ -56,6 +55,6 @@ def _get_delivery_methods_express_checkout(cls, order_sudo):
"""Override to exclude `in_store` delivery methods from exress checkout delivery options."""
dm_rate_mapping = super()._get_delivery_methods_express_checkout(order_sudo)
for dm in list(dm_rate_mapping):
- if dm.delivery_type == 'in_store':
+ if dm.delivery_type == "in_store":
del dm_rate_mapping[dm]
return dm_rate_mapping
diff --git a/addons/website_sale_collect/controllers/main.py b/addons/website_sale_collect/controllers/main.py
index c150e09e61fd6..784272c1e8b2a 100644
--- a/addons/website_sale_collect/controllers/main.py
+++ b/addons/website_sale_collect/controllers/main.py
@@ -7,62 +7,60 @@
class WebsiteSaleCollect(WebsiteSale):
-
def _prepare_product_values(self, product, category, **kwargs):
- """ Override of `website_sale` to configure the Click & Collect Availability widget. """
+ """Override of `website_sale` to configure the Click & Collect Availability widget."""
res = super()._prepare_product_values(product, category, **kwargs)
if in_store_dm_sudo := request.website.sudo().in_store_dm_id:
order_sudo = request.cart
selected_location_data = {}
single_location = len(in_store_dm_sudo.warehouse_ids) == 1
if (
- order_sudo.carrier_id.delivery_type == 'in_store'
+ order_sudo.carrier_id.delivery_type == "in_store"
and order_sudo.pickup_location_data
):
selected_location_data = order_sudo.pickup_location_data
elif single_location:
- selected_location_data = (
- in_store_dm_sudo.warehouse_ids[0]._prepare_pickup_location_data()
- )
+ default_wh = in_store_dm_sudo.warehouse_ids[0]
+ selected_location_data = default_wh._prepare_pickup_location_data()
res.update({
- 'selected_location_data': selected_location_data,
- 'show_select_store_button': not single_location,
- 'zip_code': ( # Define the zip code.
+ "selected_location_data": selected_location_data,
+ "show_select_store_button": not single_location,
+ "zip_code": ( # Define the zip code.
order_sudo.partner_shipping_id.zip
- or selected_location_data.get('zip_code')
+ or selected_location_data.get("zip_code")
or request.geoip.postal.code
- or '' # String expected for the widget.
+ or "" # String expected for the widget.
),
})
return res
def _prepare_checkout_page_values(self, order_sudo, **query_params):
- """ Override of `website_sale` to include the unavailable products for the selected pickup
- location and set the pickup location when there is only one warehouse available. """
+ """Override of `website_sale` to include the unavailable products for the selected pickup
+ location and set the pickup location when there is only one warehouse available."""
res = super()._prepare_checkout_page_values(order_sudo, **query_params)
if order_sudo.only_services:
return res
res.update(order_sudo._prepare_in_store_default_location_data())
- if order_sudo.carrier_id.delivery_type == 'in_store' and order_sudo.pickup_location_data:
- res['insufficient_stock_data'] = order_sudo._get_insufficient_stock_data(
- order_sudo.pickup_location_data.get('id')
+ if order_sudo.carrier_id.delivery_type == "in_store" and order_sudo.pickup_location_data:
+ res["insufficient_stock_data"] = order_sudo._get_insufficient_stock_data(
+ order_sudo.pickup_location_data.get("id")
)
return res
def _get_shop_payment_errors(self, order):
- """ Override of `website_sale` to includes errors if no pickup location is selected or some
- products are unavailable. """
+ """Override of `website_sale` to includes errors if no pickup location is selected or some
+ products are unavailable."""
errors = super()._get_shop_payment_errors(order)
- if order._has_deliverable_products() and order.carrier_id.delivery_type == 'in_store':
+ if order._has_deliverable_products() and order.carrier_id.delivery_type == "in_store":
if not order.pickup_location_data:
errors.append((
_("Sorry, we are unable to ship your order."),
_("Please choose a store to collect your order."),
))
else:
- selected_wh_id = order.pickup_location_data['id']
+ selected_wh_id = order.pickup_location_data["id"]
if not order._is_in_stock(selected_wh_id):
errors.append((
_("Sorry, we are unable to ship your order."),
diff --git a/addons/website_sale_collect/controllers/payment.py b/addons/website_sale_collect/controllers/payment.py
index d96016ad62e0f..41507719dbf72 100644
--- a/addons/website_sale_collect/controllers/payment.py
+++ b/addons/website_sale_collect/controllers/payment.py
@@ -7,9 +7,8 @@
class OnSitePaymentPortal(PaymentPortal):
-
def _validate_transaction_for_order(self, transaction, sale_order):
- """ Override of `website_sale` to ensure the on-site payment provider is not used without
+ """Override of `website_sale` to ensure the on-site payment provider is not used without
the in-store pickup delivery method.
This also sets the warehouse of the selected pickup location on the sales order.
@@ -25,9 +24,9 @@ def _validate_transaction_for_order(self, transaction, sale_order):
# This should never be triggered unless the user intentionally forges a request.
provider = transaction.provider_id
if (
- sale_order.carrier_id.delivery_type != 'in_store'
- and provider.code == 'custom'
- and provider.custom_mode == 'on_site'
+ sale_order.carrier_id.delivery_type != "in_store"
+ and provider.code == "custom"
+ and provider.custom_mode == "on_site"
):
raise ValidationError(
_("You can only pay on site when selecting the pick up in store delivery method.")
diff --git a/addons/website_sale_collect/models/__init__.py b/addons/website_sale_collect/models/__init__.py
index 10490be85ef96..6aef2a86f0244 100644
--- a/addons/website_sale_collect/models/__init__.py
+++ b/addons/website_sale_collect/models/__init__.py
@@ -1,10 +1,12 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import delivery_carrier
-from . import payment_provider
-from . import payment_transaction
-from . import product_template
-from . import res_config_settings
-from . import sale_order
-from . import stock_warehouse
-from . import website
+from . import (
+ delivery_carrier,
+ payment_provider,
+ payment_transaction,
+ product_template,
+ res_config_settings,
+ sale_order,
+ stock_warehouse,
+ website,
+)
diff --git a/addons/website_sale_collect/models/delivery_carrier.py b/addons/website_sale_collect/models/delivery_carrier.py
index 4cd8d08e241ff..9a5b2dfe093fa 100644
--- a/addons/website_sale_collect/models/delivery_carrier.py
+++ b/addons/website_sale_collect/models/delivery_carrier.py
@@ -9,29 +9,33 @@
class DeliveryCarrier(models.Model):
- _inherit = 'delivery.carrier'
+ _inherit = "delivery.carrier"
delivery_type = fields.Selection(
- selection_add=[('in_store', "Pick up in store")], ondelete={'in_store': 'set default'}
+ selection_add=[("in_store", "Pick up in store")], ondelete={"in_store": "set default"}
)
- warehouse_ids = fields.Many2many(string="Stores", comodel_name='stock.warehouse')
+ warehouse_ids = fields.Many2many(string="Stores", comodel_name="stock.warehouse")
- @api.constrains('delivery_type', 'is_published', 'warehouse_ids')
+ @api.constrains("delivery_type", "is_published", "warehouse_ids")
def _check_in_store_dm_has_warehouses_when_published(self):
- if any(self.filtered(
- lambda dm: dm.delivery_type == 'in_store'
- and dm.is_published
- and not dm.warehouse_ids
- )):
+ if any(
+ self.filtered(
+ lambda dm: dm.delivery_type == "in_store"
+ and dm.is_published
+ and not dm.warehouse_ids
+ )
+ ):
raise ValidationError(
_("The delivery method must have at least one warehouse to be published.")
)
- @api.constrains('delivery_type', 'company_id', 'warehouse_ids')
+ @api.constrains("delivery_type", "company_id", "warehouse_ids")
def _check_warehouses_have_same_company(self):
for dm in self:
- if dm.delivery_type == 'in_store' and dm.company_id and any(
- wh.company_id and dm.company_id != wh.company_id for wh in dm.warehouse_ids
+ if (
+ dm.delivery_type == "in_store"
+ and dm.company_id
+ and any(wh.company_id and dm.company_id != wh.company_id for wh in dm.warehouse_ids)
):
raise ValidationError(
_("The delivery method and a warehouse must share the same company")
@@ -40,31 +44,29 @@ def _check_warehouses_have_same_company(self):
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
- if vals.get('delivery_type') == 'in_store':
- vals['integration_level'] = 'rate'
- vals['allow_cash_on_delivery'] = False
+ if vals.get("delivery_type") == "in_store":
+ vals["integration_level"] = "rate"
+ vals["allow_cash_on_delivery"] = False
# Set the default warehouses and publish if one is found.
- if 'company_id' in vals:
- company_id = vals.get('company_id')
+ if "company_id" in vals:
+ company_id = vals.get("company_id")
else:
company_id = (
- self.env['product.product'].browse(vals.get('product_id')).company_id.id
+ self.env["product.product"].browse(vals.get("product_id")).company_id.id
or self.env.company.id
)
- warehouses = self.env['stock.warehouse'].search(
- [('company_id', 'in', company_id)]
- )
+ warehouses = self.env["stock.warehouse"].search([("company_id", "in", company_id)])
vals.update({
- 'warehouse_ids': [Command.set(warehouses.ids)],
- 'is_published': bool(warehouses),
+ "warehouse_ids": [Command.set(warehouses.ids)],
+ "is_published": bool(warehouses),
})
return super().create(vals_list)
def write(self, vals):
- if vals.get('delivery_type') == 'in_store':
- vals['integration_level'] = 'rate'
- vals['allow_cash_on_delivery'] = False
+ if vals.get("delivery_type") == "in_store":
+ vals["integration_level"] = "rate"
+ vals["allow_cash_on_delivery"] = False
return super().write(vals)
# === BUSINESS METHODS ===#
@@ -82,9 +84,9 @@ def _in_store_get_close_locations(self, partner_address, product_id=None, uom_id
try:
product_id = product_id and int(product_id)
except ValueError:
- product = self.env['product.product']
+ product = self.env["product.product"]
else:
- product = self.env['product.product'].browse(product_id)
+ product = self.env["product.product"].browse(product_id)
partner_address.geo_localize() # Calculate coordinates.
@@ -97,27 +99,27 @@ def _in_store_get_close_locations(self, partner_address, product_id=None, uom_id
# Prepare the stock data based on either the product or the order.
if product: # Called from the product page.
- uom = self.env['uom.uom'].browse(uom_id)
+ uom = self.env["uom.uom"].browse(uom_id)
cart_qty = order_sudo._get_cart_qty(product.id)
in_store_stock_data = utils.format_product_stock_values(
product, wh_id=wh.id, uom=uom, cart_qty=cart_qty
)
else: # Called from the checkout page.
- in_store_stock_data = {'in_stock': order_sudo._is_in_stock(wh.id)}
+ in_store_stock_data = {"in_stock": order_sudo._is_in_stock(wh.id)}
# Calculate the distance between the partner address and the warehouse location.
pickup_location_values.update({
- 'additional_data': {'in_store_stock_data': in_store_stock_data},
- 'distance': utils.calculate_partner_distance(partner_address, wh.partner_id),
+ "additional_data": {"in_store_stock_data": in_store_stock_data},
+ "distance": utils.calculate_partner_distance(partner_address, wh.partner_id),
})
pickup_locations.append(pickup_location_values)
- return sorted(pickup_locations, key=lambda k: k['distance'])
+ return sorted(pickup_locations, key=lambda k: k["distance"])
def in_store_rate_shipment(self, *_args):
return {
- 'success': True,
- 'price': self.product_id.list_price,
- 'error_message': False,
- 'warning_message': False,
+ "success": True,
+ "price": self.product_id.list_price,
+ "error_message": False,
+ "warning_message": False,
}
diff --git a/addons/website_sale_collect/models/payment_provider.py b/addons/website_sale_collect/models/payment_provider.py
index e047f1fdd7b41..835b4a0c88a9d 100644
--- a/addons/website_sale_collect/models/payment_provider.py
+++ b/addons/website_sale_collect/models/payment_provider.py
@@ -7,16 +7,16 @@
class PaymentProvider(models.Model):
- _inherit = 'payment.provider'
+ _inherit = "payment.provider"
- custom_mode = fields.Selection(selection_add=[('on_site', "Pay on site")])
+ custom_mode = fields.Selection(selection_add=[("on_site", "Pay on site")])
# === CRUD METHODS === #
def _get_default_payment_method_codes(self):
- """ Override of `payment` to return the default payment method codes. """
+ """Override of `payment` to return the default payment method codes."""
self.ensure_one()
- if self.custom_mode != 'on_site':
+ if self.custom_mode != "on_site":
return super()._get_default_payment_method_codes()
return const.DEFAULT_PAYMENT_METHOD_CODES
@@ -26,7 +26,7 @@ def _get_default_payment_method_codes(self):
def _get_compatible_providers(
self, company_id, *args, sale_order_id=None, website_id=None, report=None, **kwargs
):
- """ Override of payment to exclude on-site payment providers if the delivery method is not
+ """Override of payment to exclude on-site payment providers if the delivery method is not
pick up in store.
:param int company_id: The company to which providers must belong, as a `res.company` id
@@ -44,16 +44,16 @@ def _get_compatible_providers(
report=report,
**kwargs,
)
- order = self.env['sale.order'].browse(sale_order_id).exists()
+ order = self.env["sale.order"].browse(sale_order_id).exists()
# Show on-site payment providers only if in-store delivery methods exist and the order
# contains physical products.
- if order.carrier_id.delivery_type != 'in_store' or not any(
- product.type == 'consu' for product in order.order_line.product_id
+ if order.carrier_id.delivery_type != "in_store" or not any(
+ product.type == "consu" for product in order.order_line.product_id
):
unfiltered_providers = compatible_providers
compatible_providers = compatible_providers.filtered(
- lambda p: p.code != 'custom' or p.custom_mode != 'on_site'
+ lambda p: p.code != "custom" or p.custom_mode != "on_site"
)
payment_utils.add_to_report(
report,
diff --git a/addons/website_sale_collect/models/payment_transaction.py b/addons/website_sale_collect/models/payment_transaction.py
index d78d97675b05b..a531c86c3c40b 100644
--- a/addons/website_sale_collect/models/payment_transaction.py
+++ b/addons/website_sale_collect/models/payment_transaction.py
@@ -4,15 +4,15 @@
class PaymentTransaction(models.Model):
- _inherit = 'payment.transaction'
+ _inherit = "payment.transaction"
def _post_process(self):
- """ Override of `payment` to confirm orders with the on_site payment method and trigger
- a picking creation. """
+ """Override of `payment` to confirm orders with the on_site payment method and trigger
+ a picking creation."""
on_site_pending_txs = self.filtered(
- lambda tx: tx.provider_id.custom_mode == 'on_site' and tx.state == 'pending'
+ lambda tx: tx.provider_id.custom_mode == "on_site" and tx.state == "pending"
)
- on_site_pending_txs.sale_order_ids.filtered(
- lambda so: so.state == 'draft'
- ).with_context(send_email=True).action_confirm()
+ on_site_pending_txs.sale_order_ids.filtered(lambda so: so.state == "draft").with_context(
+ send_email=True
+ ).action_confirm()
super()._post_process()
diff --git a/addons/website_sale_collect/models/product_template.py b/addons/website_sale_collect/models/product_template.py
index 83a602ba11bbb..14e442b8178e0 100644
--- a/addons/website_sale_collect/models/product_template.py
+++ b/addons/website_sale_collect/models/product_template.py
@@ -7,11 +7,11 @@
class ProductTemplate(models.Model):
- _inherit = 'product.template'
+ _inherit = "product.template"
def _get_additionnal_combination_info(self, product_or_template, quantity, uom, date, website):
"""Override of `website_sale` to add information on whether Click & Collect is enabled and
- on the stock of the product. """
+ on the stock of the product."""
res = super()._get_additionnal_combination_info(
product_or_template, quantity, uom, date, website
)
@@ -24,36 +24,39 @@ def _get_additionnal_combination_info(self, product_or_template, quantity, uom,
order_sudo = request.cart
cart_qty = order_sudo._get_cart_qty(product_sudo.id)
# Enable the Click & Collect Availability widget.
- res['show_click_and_collect_availability'] = True
- res['uom_id'] = uom.id
+ res["show_click_and_collect_availability"] = True
+ res["uom_id"] = uom.id
# Prepare the delivery stock data.
- available_delivery_methods_sudo = self.env['delivery.carrier'].sudo().search([
- '|', ('website_id', '=', website.id), ('website_id', '=', False),
- ('website_published', '=', True),
- ('delivery_type', '!=', 'in_store'),
+ DeliveryCarrier = self.env["delivery.carrier"].sudo()
+ available_delivery_methods_sudo = DeliveryCarrier.search([
+ "|",
+ ("website_id", "=", website.id),
+ ("website_id", "=", False),
+ ("website_published", "=", True),
+ ("delivery_type", "!=", "in_store"),
])
if available_delivery_methods_sudo:
- res['delivery_stock_data'] = utils.format_product_stock_values(
+ res["delivery_stock_data"] = utils.format_product_stock_values(
product_sudo, uom=uom, cart_qty=cart_qty
)
else:
- res['delivery_stock_data'] = {}
+ res["delivery_stock_data"] = {}
# Prepare the in-store stock data.
if (
order_sudo
- and order_sudo.carrier_id.delivery_type == 'in_store'
+ and order_sudo.carrier_id.delivery_type == "in_store"
and order_sudo.pickup_location_data
): # Get stock values for the product variant in the selected store.
- res['in_store_stock_data'] = utils.format_product_stock_values(
+ res["in_store_stock_data"] = utils.format_product_stock_values(
product_sudo,
uom=uom,
- wh_id=order_sudo.pickup_location_data['id'],
+ wh_id=order_sudo.pickup_location_data["id"],
cart_qty=cart_qty,
)
else:
- res['in_store_stock_data'] = utils.format_product_stock_values(
+ res["in_store_stock_data"] = utils.format_product_stock_values(
product_sudo,
uom=uom,
free_qty=website.sudo()._get_max_in_store_product_available_qty(product_sudo),
diff --git a/addons/website_sale_collect/models/res_config_settings.py b/addons/website_sale_collect/models/res_config_settings.py
index abb2a23660024..f55f691b9bce6 100644
--- a/addons/website_sale_collect/models/res_config_settings.py
+++ b/addons/website_sale_collect/models/res_config_settings.py
@@ -4,23 +4,23 @@
class ResConfigSettings(models.TransientModel):
- _inherit = 'res.config.settings'
+ _inherit = "res.config.settings"
def action_view_in_store_delivery_methods(self):
- """ Return an action to browse pickup delivery methods in list view, or in form view if
- there is only one. """
- in_store_dms = self.env['delivery.carrier'].search([('delivery_type', '=', 'in_store')])
+ """Return an action to browse pickup delivery methods in list view, or in form view if
+ there is only one."""
+ in_store_dms = self.env["delivery.carrier"].search([("delivery_type", "=", "in_store")])
if len(in_store_dms) == 1:
return {
- 'type': 'ir.actions.act_window',
- 'res_model': 'delivery.carrier',
- 'view_mode': 'form',
- 'res_id': in_store_dms.id,
+ "type": "ir.actions.act_window",
+ "res_model": "delivery.carrier",
+ "view_mode": "form",
+ "res_id": in_store_dms.id,
}
return {
- 'type': 'ir.actions.act_window',
- 'name': _("Delivery Methods"),
- 'res_model': 'delivery.carrier',
- 'view_mode': 'list,form',
- 'context': '{"search_default_delivery_type": "in_store"}',
+ "type": "ir.actions.act_window",
+ "name": _("Delivery Methods"),
+ "res_model": "delivery.carrier",
+ "view_mode": "list,form",
+ "context": '{"search_default_delivery_type": "in_store"}',
}
diff --git a/addons/website_sale_collect/models/sale_order.py b/addons/website_sale_collect/models/sale_order.py
index 5dac4b6fbba92..45f182832d96a 100644
--- a/addons/website_sale_collect/models/sale_order.py
+++ b/addons/website_sale_collect/models/sale_order.py
@@ -8,27 +8,25 @@
class SaleOrder(models.Model):
- _inherit = 'sale.order'
+ _inherit = "sale.order"
def _compute_warehouse_id(self):
"""Override of `website_sale_stock` to avoid recomputations for in_store orders
when the warehouse was set by the pickup_location_data."""
in_store_orders_with_pickup_data = self.filtered(
- lambda so: (
- so.carrier_id.delivery_type == 'in_store' and so.pickup_location_data
- )
+ lambda so: (so.carrier_id.delivery_type == "in_store" and so.pickup_location_data)
)
super(SaleOrder, self - in_store_orders_with_pickup_data)._compute_warehouse_id()
for order in in_store_orders_with_pickup_data:
- order.warehouse_id = order.pickup_location_data['id']
+ order.warehouse_id = order.pickup_location_data["id"]
def _compute_fiscal_position_id(self):
"""Override of `sale` to set the fiscal position matching the selected pickup location
for pickup in-store orders."""
in_store_orders = self.filtered(
- lambda so: so.carrier_id.delivery_type == 'in_store' and so.pickup_location_data
+ lambda so: so.carrier_id.delivery_type == "in_store" and so.pickup_location_data
)
- AccountFiscalPosition = self.env['account.fiscal.position'].sudo()
+ AccountFiscalPosition = self.env["account.fiscal.position"].sudo()
for order in in_store_orders:
order.fiscal_position_id = AccountFiscalPosition._get_fiscal_position(
order.partner_id, delivery=order.warehouse_id.partner_id
@@ -37,11 +35,11 @@ def _compute_fiscal_position_id(self):
def _set_delivery_method(self, delivery_method, rate=None):
"""Override of `website_sale` to recompute warehouse and fiscal position when a new
- delivery method is not in-store anymore. """
+ delivery method is not in-store anymore."""
self.ensure_one()
was_in_store_order = (
- self.carrier_id.delivery_type == 'in_store'
- and delivery_method.delivery_type != 'in_store'
+ self.carrier_id.delivery_type == "in_store"
+ and delivery_method.delivery_type != "in_store"
)
super()._set_delivery_method(delivery_method, rate=rate)
if was_in_store_order:
@@ -54,12 +52,12 @@ def _set_pickup_location(self, pickup_location_data):
taxes.
"""
super()._set_pickup_location(pickup_location_data)
- if self.carrier_id.delivery_type != 'in_store':
+ if self.carrier_id.delivery_type != "in_store":
return
self.pickup_location_data = json.loads(pickup_location_data)
if self.pickup_location_data:
- self.warehouse_id = self.pickup_location_data['id']
+ self.warehouse_id = self.pickup_location_data["id"]
self._compute_fiscal_position_id()
else:
self._compute_warehouse_id()
@@ -74,10 +72,10 @@ def _get_pickup_locations(self, zip_code=None, country=None, **kwargs):
if zip_code and not country:
country_code = None
if self.pickup_location_data:
- country_code = self.pickup_location_data['country_code']
+ country_code = self.pickup_location_data["country_code"]
elif request.geoip.country_code:
country_code = request.geoip.country_code
- country = self.env['res.country'].search([('code', '=', country_code)], limit=1)
+ country = self.env["res.country"].search([("code", "=", country_code)], limit=1)
if not country:
zip_code = None # Reset the zip code to skip the `assert` in the `super` call.
return super()._get_pickup_locations(zip_code=zip_code, country=country, **kwargs)
@@ -85,45 +83,45 @@ def _get_pickup_locations(self, zip_code=None, country=None, **kwargs):
def _get_shop_warehouse_id(self):
"""Override of `website_sale_stock` to consider the chosen warehouse."""
self.ensure_one()
- if self.carrier_id.delivery_type == 'in_store':
+ if self.carrier_id.delivery_type == "in_store":
return self.warehouse_id.id
return super()._get_shop_warehouse_id()
def _check_cart_is_ready_to_be_paid(self):
"""Override of `website_sale` to check if all products are in stock in the selected
- warehouse. """
+ warehouse."""
if (
self._has_deliverable_products()
- and self.carrier_id.delivery_type == 'in_store'
+ and self.carrier_id.delivery_type == "in_store"
and not self._is_in_stock(self.warehouse_id.id)
):
- raise ValidationError(self.env._(
- "Some products are not available in the selected store."
- ))
+ raise ValidationError(
+ self.env._("Some products are not available in the selected store.")
+ )
return super()._check_cart_is_ready_to_be_paid()
# === TOOLING ===#
def _prepare_in_store_default_location_data(self):
"""Prepare the default pickup location values for each in-store delivery method available
- for the order. """
+ for the order."""
default_pickup_locations = {}
for dm in self._get_delivery_methods():
if (
- dm.delivery_type == 'in_store'
+ dm.delivery_type == "in_store"
and dm.id != self.carrier_id.id
and len(dm.warehouse_ids) == 1
):
pickup_location_data = dm.warehouse_ids[0]._prepare_pickup_location_data()
if pickup_location_data:
default_pickup_locations[dm.id] = {
- 'pickup_location_data': pickup_location_data,
- 'insufficient_stock_data': self._get_insufficient_stock_data(
- pickup_location_data['id']
+ "pickup_location_data": pickup_location_data,
+ "insufficient_stock_data": self._get_insufficient_stock_data(
+ pickup_location_data["id"]
),
}
- return {'default_pickup_locations': default_pickup_locations}
+ return {"default_pickup_locations": default_pickup_locations}
def _is_in_stock(self, wh_id):
"""Check whether all storable products of the cart are in stock in the given warehouse.
@@ -145,33 +143,36 @@ def _get_insufficient_stock_data(self, wh_id):
:rtype: dict
"""
insufficient_stock_data = {}
- for product, ols in self.order_line.grouped('product_id').items():
+ for product, ols in self.order_line.grouped("product_id").items():
if not product.is_storable or product.allow_out_of_stock_order:
continue
free_qty = product.with_context(warehouse_id=wh_id).free_qty
for ol in ols:
- free_qty_in_uom = max(int(product.uom_id._compute_quantity(
- free_qty, ol.product_uom_id, rounding_method='DOWN'
- )), 0) # Round down as only integer quantities can be sold.
+ free_qty_in_uom = product.uom_id._compute_quantity(
+ free_qty, ol.product_uom_id, rounding_method="DOWN"
+ )
+ # Round down as only integer quantities can be sold.
+ free_qty_in_uom = max(int(free_qty_in_uom), 0)
line_qty_in_uom = ol.product_uom_qty
if line_qty_in_uom > free_qty_in_uom: # Not enough stock.
# Set a warning on the order line.
insufficient_stock_data[ol] = free_qty_in_uom
ol.shop_warning = self.env._(
"%(available_qty)s/%(line_qty)s available at this location",
- available_qty=free_qty_in_uom, line_qty=int(line_qty_in_uom),
+ available_qty=free_qty_in_uom,
+ line_qty=int(line_qty_in_uom),
)
free_qty -= ol.product_uom_id._compute_quantity(line_qty_in_uom, product.uom_id)
return insufficient_stock_data
def _verify_updated_quantity(self, order_line, product_id, new_qty, uom_id, **kwargs):
"""Override of `website_sale_stock` to skip the verification when click and collect
- is activated. The quantity is verified later. """
- product = self.env['product.product'].browse(product_id)
+ is activated. The quantity is verified later."""
+ product = self.env["product.product"].browse(product_id)
if (
product.is_storable
and not product.allow_out_of_stock_order
and self.website_id.in_store_dm_id
):
- return new_qty, ''
+ return new_qty, ""
return super()._verify_updated_quantity(order_line, product_id, new_qty, uom_id, **kwargs)
diff --git a/addons/website_sale_collect/models/stock_warehouse.py b/addons/website_sale_collect/models/stock_warehouse.py
index 84bdbb4efb15d..6338af85f365a 100644
--- a/addons/website_sale_collect/models/stock_warehouse.py
+++ b/addons/website_sale_collect/models/stock_warehouse.py
@@ -5,10 +5,10 @@
class StockWarehouse(models.Model):
- _inherit = 'stock.warehouse'
+ _inherit = "stock.warehouse"
opening_hours = fields.Many2one(
- string="Opening Hours", comodel_name='resource.calendar', check_company=True
+ string="Opening Hours", comodel_name="resource.calendar", check_company=True
)
def _prepare_pickup_location_data(self):
@@ -20,15 +20,15 @@ def _prepare_pickup_location_data(self):
# Format the pickup location values of the warehouse.
try:
pickup_location_values = {
- 'id': self.id,
- 'name': wh_location['name'].title(),
- 'street': wh_location['street'].title(),
- 'city': wh_location.city.title(),
- 'state': wh_location.state_id.code or '',
- 'zip_code': wh_location.zip or '',
- 'country_code': wh_location.country_code,
- 'latitude': wh_location.partner_latitude,
- 'longitude': wh_location.partner_longitude,
+ "id": self.id,
+ "name": wh_location["name"].title(),
+ "street": wh_location["street"].title(),
+ "city": wh_location.city.title(),
+ "state": wh_location.state_id.code or "",
+ "zip_code": wh_location.zip or "",
+ "country_code": wh_location.country_code,
+ "latitude": wh_location.partner_latitude,
+ "longitude": wh_location.partner_longitude,
}
except AttributeError:
return {}
@@ -37,11 +37,11 @@ def _prepare_pickup_location_data(self):
if self.opening_hours:
opening_hours_dict = {str(i): [] for i in range(7)}
for att in self.opening_hours.attendance_ids:
- if att.day_period in ('morning', 'afternoon'):
+ if att.day_period in ("morning", "afternoon"):
opening_hours_dict[att.dayofweek].append(
- f'{format_duration(att.hour_from)} - {format_duration(att.hour_to)}'
+ f"{format_duration(att.hour_from)} - {format_duration(att.hour_to)}"
)
- pickup_location_values['opening_hours'] = opening_hours_dict
+ pickup_location_values["opening_hours"] = opening_hours_dict
else:
- pickup_location_values['opening_hours'] = {}
+ pickup_location_values["opening_hours"] = {}
return pickup_location_values
diff --git a/addons/website_sale_collect/models/website.py b/addons/website_sale_collect/models/website.py
index b4e300fbb51ee..df824a36bd910 100644
--- a/addons/website_sale_collect/models/website.py
+++ b/addons/website_sale_collect/models/website.py
@@ -4,29 +4,34 @@
class Website(models.Model):
- _inherit = 'website'
+ _inherit = "website"
in_store_dm_id = fields.Many2one(
string="In-store Delivery Method",
- comodel_name='delivery.carrier',
- compute='_compute_in_store_dm_id',
+ comodel_name="delivery.carrier",
+ compute="_compute_in_store_dm_id",
)
def _compute_in_store_dm_id(self):
- in_store_delivery_methods = self.env['delivery.carrier'].search(
- [('delivery_type', '=', 'in_store'), ('is_published', '=', True)]
- )
+ in_store_delivery_methods = self.env["delivery.carrier"].search([
+ ("delivery_type", "=", "in_store"),
+ ("is_published", "=", True),
+ ])
for website in self:
website.in_store_dm_id = in_store_delivery_methods.filtered_domain([
- '|', ('website_id', '=', False), ('website_id', '=', website.id),
- '|', ('company_id', '=', False), ('company_id', '=', website.company_id.id),
+ "|",
+ ("website_id", "=", False),
+ ("website_id", "=", website.id),
+ "|",
+ ("company_id", "=", False),
+ ("company_id", "=", website.company_id.id),
])[:1]
def _get_product_available_qty(self, product, **kwargs):
- """ Override of `website_sale_stock` to include free quantities of the product in warehouses
- of in-store delivery method and return maximum possible for one order. Needed only if a
- warehouse is set on website, otherwise free quantity is already calculated from all
- warehouses."""
+ """Override of `website_sale_stock` to include free quantities of the product in warehouses
+ of in-store delivery method and return maximum possible for one order. Needed only if a
+ warehouse is set on website, otherwise free quantity is already calculated from all
+ warehouses."""
free_qty = super()._get_product_available_qty(product, **kwargs)
if self.warehouse_id and self.sudo().in_store_dm_id: # If warehouse is set on website.
# Check free quantities in the in-store warehouses.
@@ -34,8 +39,11 @@ def _get_product_available_qty(self, product, **kwargs):
return free_qty
def _get_max_in_store_product_available_qty(self, product):
- """ Return maximum amount of product available to deliver with in store delivery method. """
- return max([
- product.with_context(warehouse_id=wh.id).free_qty
- for wh in self.sudo().in_store_dm_id.warehouse_ids
- ], default=0)
+ """Return maximum amount of product available to deliver with in store delivery method."""
+ return max(
+ [
+ product.with_context(warehouse_id=wh.id).free_qty
+ for wh in self.sudo().in_store_dm_id.warehouse_ids
+ ],
+ default=0,
+ )
diff --git a/addons/website_sale_collect/tests/__init__.py b/addons/website_sale_collect/tests/__init__.py
index b4ee93e2bf6e5..8eb80449aa41f 100644
--- a/addons/website_sale_collect/tests/__init__.py
+++ b/addons/website_sale_collect/tests/__init__.py
@@ -1,11 +1,13 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import test_delivery_carrier
-from . import test_click_and_collect_express_checkout
-from . import test_click_and_collect_flow
-from . import test_in_store_delivery
-from . import test_payment_provider
-from . import test_payment_transaction
-from . import test_product_template
-from . import test_sale_order
-from . import test_website
+from . import (
+ test_click_and_collect_express_checkout,
+ test_click_and_collect_flow,
+ test_delivery_carrier,
+ test_in_store_delivery,
+ test_payment_provider,
+ test_payment_transaction,
+ test_product_template,
+ test_sale_order,
+ test_website,
+)
diff --git a/addons/website_sale_collect/tests/common.py b/addons/website_sale_collect/tests/common.py
index 57fbf28c9ec4d..d10a5b4a80268 100644
--- a/addons/website_sale_collect/tests/common.py
+++ b/addons/website_sale_collect/tests/common.py
@@ -7,7 +7,6 @@
class ClickAndCollectCommon(PaymentCustomCommon, WebsiteSaleStockCommon):
-
@classmethod
def setUpClass(cls):
super().setUpClass()
@@ -17,11 +16,11 @@ def setUpClass(cls):
# Create the in-store delivery method.
cls.dm_product = cls._prepare_carrier_product(list_price=0.0)
- cls.provider = cls._prepare_provider(code='custom', custom_mode='on_site')
+ cls.provider = cls._prepare_provider(code="custom", custom_mode="on_site")
cls.in_store_dm = cls._prepare_carrier(
cls.dm_product,
fixed_price=0.0,
- delivery_type='in_store',
+ delivery_type="in_store",
warehouse_ids=[Command.set([cls.warehouse.id])],
name="Example in-store delivery",
is_published=True,
@@ -29,12 +28,11 @@ def setUpClass(cls):
def _create_in_store_delivery_order(self, **values):
default_values = {
- 'partner_id': self.partner.id,
- 'website_id': self.website.id,
- 'order_line': [Command.create({
- 'product_id': self.storable_product.id,
- 'product_uom_qty': 5.0,
- })],
- 'carrier_id': self.in_store_dm.id,
+ "partner_id": self.partner.id,
+ "website_id": self.website.id,
+ "order_line": [
+ Command.create({"product_id": self.storable_product.id, "product_uom_qty": 5.0})
+ ],
+ "carrier_id": self.in_store_dm.id,
}
- return self.env['sale.order'].create(dict(default_values, **values))
+ return self.env["sale.order"].create(dict(default_values, **values))
diff --git a/addons/website_sale_collect/tests/test_click_and_collect_express_checkout.py b/addons/website_sale_collect/tests/test_click_and_collect_express_checkout.py
index eb73e71e05870..46d0052da10a1 100644
--- a/addons/website_sale_collect/tests/test_click_and_collect_express_checkout.py
+++ b/addons/website_sale_collect/tests/test_click_and_collect_express_checkout.py
@@ -6,9 +6,8 @@
from odoo.addons.website_sale_collect.tests.common import ClickAndCollectCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestClickAndCollectExpressCheckout(ClickAndCollectCommon):
-
def test_exclude_in_store_delivery_methods(self):
express_delivery_methods = InStoreDelivery._get_delivery_methods_express_checkout(self.cart)
diff --git a/addons/website_sale_collect/tests/test_click_and_collect_flow.py b/addons/website_sale_collect/tests/test_click_and_collect_flow.py
index 69ed71a973368..7beb556bbc111 100644
--- a/addons/website_sale_collect/tests/test_click_and_collect_flow.py
+++ b/addons/website_sale_collect/tests/test_click_and_collect_flow.py
@@ -1,45 +1,41 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests import tagged
-
from odoo.tests.common import HttpCase
+
from odoo.addons.website_sale_collect.tests.common import ClickAndCollectCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestClickAndCollectFlow(HttpCase, ClickAndCollectCommon):
-
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.storable_product.name = "Test CAC Product"
- cls.provider.write({
- 'state': 'enabled',
- 'is_published': True,
- })
- cls.in_store_dm.warehouse_ids[0].partner_id = cls.env['res.partner'].create({
+ cls.provider.write({"state": "enabled", "is_published": True})
+ cls.in_store_dm.warehouse_ids[0].partner_id = cls.env["res.partner"].create({
**cls.dummy_partner_address_values,
- 'name': "Shop 1",
- 'partner_latitude': 1.0,
- 'partner_longitude': 2.0,
+ "name": "Shop 1",
+ "partner_latitude": 1.0,
+ "partner_longitude": 2.0,
})
def test_buy_with_click_and_collect_as_public_user(self):
"""
Test the basic flow of buying with click and collect as a public user with more than
- one delivery method available
+ one delivery method available.
"""
- self.start_tour('/', 'website_sale_collect_widget')
+ self.start_tour("/", "website_sale_collect_widget")
def test_default_location_is_set_for_pick_up_in_store(self):
"""
Verify that when `Pick Up In Store` is the only active delivery method with the only wh,
the checkout flow automatically sets the default store location.
"""
- self.env['delivery.carrier'].search([]).active = False
+ self.env["delivery.carrier"].search([]).active = False
self.in_store_dm.active = True
self.in_store_dm.is_published = True
- self.start_tour('/', 'website_sale_collect_buy_product_default_location_pick_up_in_store')
+ self.start_tour("/", "website_sale_collect_buy_product_default_location_pick_up_in_store")
def test_cash_on_delivery_resets_on_in_store_type(self):
"""
@@ -47,11 +43,11 @@ def test_cash_on_delivery_resets_on_in_store_type(self):
to the 'in_store' delivery type, the 'allow_cash_on_delivery' field
is automatically reset to False.
"""
- carrier = self.env['delivery.carrier'].create({
- 'name': 'Test Carrier',
- 'allow_cash_on_delivery': True,
- 'delivery_type': 'fixed',
- 'product_id': self.storable_product.id,
+ carrier = self.env["delivery.carrier"].create({
+ "name": "Test Carrier",
+ "allow_cash_on_delivery": True,
+ "delivery_type": "fixed",
+ "product_id": self.storable_product.id,
})
- carrier.delivery_type = 'in_store'
+ carrier.delivery_type = "in_store"
self.assertEqual(carrier.allow_cash_on_delivery, False)
diff --git a/addons/website_sale_collect/tests/test_delivery_carrier.py b/addons/website_sale_collect/tests/test_delivery_carrier.py
index f87e2523de13b..2bdb8db9abf6e 100644
--- a/addons/website_sale_collect/tests/test_delivery_carrier.py
+++ b/addons/website_sale_collect/tests/test_delivery_carrier.py
@@ -4,16 +4,15 @@
from odoo import Command
from odoo.exceptions import ValidationError
-from odoo.tests import Form, tagged
+from odoo.tests import tagged
from odoo.addons.website_sale.tests.common import MockRequest
from odoo.addons.website_sale_collect.tests.common import ClickAndCollectCommon
from odoo.addons.website_sale_stock.tests.common import WebsiteSaleStockCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestDeliveryCarrier(ClickAndCollectCommon, WebsiteSaleStockCommon):
-
def test_prevent_publishing_when_no_warehouse(self):
self.in_store_dm.is_published = False
self.in_store_dm.warehouse_ids = [Command.clear()]
@@ -22,82 +21,88 @@ def test_prevent_publishing_when_no_warehouse(self):
def test_same_company_for_delivery_method_and_warehouse(self):
self.in_store_dm.company_id = self.company_id
- self.companyA = self.env['res.company'].create({'name': 'Company A'})
+ self.companyA = self.env["res.company"].create({"name": "Company A"})
self.warehouse_2 = self._create_warehouse(company_id=self.companyA.id)
with self.assertRaises(ValidationError):
self.in_store_dm.warehouse_ids = [Command.set([self.warehouse_2.id])]
def test_creating_in_store_delivery_method_sets_integration_level_to_rate(self):
- new_in_store_carrier = self.env['delivery.carrier'].create({
- 'name': "Test Carrier",
- 'delivery_type': 'in_store',
- 'product_id': self.dm_product.id,
+ new_in_store_carrier = self.env["delivery.carrier"].create({
+ "name": "Test Carrier",
+ "delivery_type": "in_store",
+ "product_id": self.dm_product.id,
})
- self.assertEqual(new_in_store_carrier.integration_level, 'rate')
+ self.assertEqual(new_in_store_carrier.integration_level, "rate")
def test_in_store_get_close_locations_returned_data(self):
so = self._create_in_store_delivery_order()
# Create a partner for a warehouse.
- wh_address_partner = self.env['res.partner'].create({
+ wh_address_partner = self.env["res.partner"].create({
**self.dummy_partner_address_values,
- 'name': "Shop 1",
- 'partner_latitude': 1.0,
- 'partner_longitude': 2.0,
+ "name": "Shop 1",
+ "partner_latitude": 1.0,
+ "partner_longitude": 2.0,
})
self.warehouse.partner_id = wh_address_partner.id
- self.warehouse.opening_hours = self.env['resource.calendar'].create({
- 'name': 'Opening hours',
- 'attendance_ids': [
+ self.warehouse.opening_hours = self.env["resource.calendar"].create({
+ "name": "Opening hours",
+ "attendance_ids": [
Command.create({
- 'name': 'Monday Morning',
- 'dayofweek': '0',
- 'hour_from': 8,
- 'hour_to': 12,
- 'day_period': 'morning',
+ "name": "Monday Morning",
+ "dayofweek": "0",
+ "hour_from": 8,
+ "hour_to": 12,
+ "day_period": "morning",
}),
Command.create({
- 'name': 'Monday Lunch',
- 'dayofweek': '0',
- 'hour_from': 12,
- 'hour_to': 13,
- 'day_period': 'lunch',
+ "name": "Monday Lunch",
+ "dayofweek": "0",
+ "hour_from": 12,
+ "hour_to": 13,
+ "day_period": "lunch",
}),
Command.create({
- 'name': 'Monday Afternoon',
- 'dayofweek': '0',
- 'hour_from': 13,
- 'hour_to': 17,
- 'day_period': 'afternoon',
+ "name": "Monday Afternoon",
+ "dayofweek": "0",
+ "hour_from": 13,
+ "hour_to": 17,
+ "day_period": "afternoon",
}),
],
})
- with patch(
- 'odoo.addons.base_geolocalize.models.res_partner.ResPartner.geo_localize',
- return_value=True
- ), MockRequest(self.env, website=self.website, sale_order_id=so.id):
+ with (
+ patch(
+ "odoo.addons.base_geolocalize.models.res_partner.ResPartner.geo_localize",
+ return_value=True,
+ ),
+ MockRequest(self.env, website=self.website, sale_order_id=so.id),
+ ):
locations = self.in_store_dm._in_store_get_close_locations(wh_address_partner)
self.assertEqual(
- locations, [{
- 'id': self.warehouse.id,
- 'name': wh_address_partner['name'].title(),
- 'street': wh_address_partner['street'].title(),
- 'city': wh_address_partner.city.title(),
- 'zip_code': wh_address_partner.zip,
- 'state': wh_address_partner.state_id.code,
- 'country_code': wh_address_partner.country_code,
- 'latitude': wh_address_partner.partner_latitude,
- 'longitude': wh_address_partner.partner_longitude,
- 'additional_data': {'in_store_stock_data': {'in_stock': True}},
- 'opening_hours': {
- '0': ['08:00 - 12:00', '13:00 - 17:00'],
- '1': [],
- '2': [],
- '3': [],
- '4': [],
- '5': [],
- '6': [],
- },
- 'distance': 0.0,
- }]
+ locations,
+ [
+ {
+ "id": self.warehouse.id,
+ "name": wh_address_partner["name"].title(),
+ "street": wh_address_partner["street"].title(),
+ "city": wh_address_partner.city.title(),
+ "zip_code": wh_address_partner.zip,
+ "state": wh_address_partner.state_id.code,
+ "country_code": wh_address_partner.country_code,
+ "latitude": wh_address_partner.partner_latitude,
+ "longitude": wh_address_partner.partner_longitude,
+ "additional_data": {"in_store_stock_data": {"in_stock": True}},
+ "opening_hours": {
+ "0": ["08:00 - 12:00", "13:00 - 17:00"],
+ "1": [],
+ "2": [],
+ "3": [],
+ "4": [],
+ "5": [],
+ "6": [],
+ },
+ "distance": 0.0,
+ }
+ ],
)
diff --git a/addons/website_sale_collect/tests/test_in_store_delivery.py b/addons/website_sale_collect/tests/test_in_store_delivery.py
index b5df9e682c38d..0a249b65fc047 100644
--- a/addons/website_sale_collect/tests/test_in_store_delivery.py
+++ b/addons/website_sale_collect/tests/test_in_store_delivery.py
@@ -1,6 +1,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from unittest.mock import patch
+
from odoo.tests import tagged
from odoo.addons.payment.tests.http_common import PaymentHttpCommon
@@ -8,19 +9,19 @@
from odoo.addons.website_sale_collect.tests.common import ClickAndCollectCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestInStoreDeliveryController(PaymentHttpCommon, ClickAndCollectCommon):
def setUp(self):
super().setUp()
self.InStoreController = InStoreDelivery()
def test_order_not_created_on_fetching_pickup_location_with_empty_cart(self):
- count_so_before = self.env['sale.order'].search_count([])
- url = self._build_url('/website_sale/get_pickup_locations')
+ count_so_before = self.env["sale.order"].search_count([])
+ url = self._build_url("/website_sale/get_pickup_locations")
with patch(
- 'odoo.addons.website_sale_collect.models.sale_order.SaleOrder._get_pickup_locations',
- return_value={}
+ "odoo.addons.website_sale_collect.models.sale_order.SaleOrder._get_pickup_locations",
+ return_value={},
):
- self.make_jsonrpc_request(url, {'product_id': 1})
- count_so_after = self.env['sale.order'].search_count([])
+ self.make_jsonrpc_request(url, {"product_id": 1})
+ count_so_after = self.env["sale.order"].search_count([])
self.assertEqual(count_so_after, count_so_before)
diff --git a/addons/website_sale_collect/tests/test_payment_provider.py b/addons/website_sale_collect/tests/test_payment_provider.py
index 45628dd7d8495..e6989b991c4e6 100644
--- a/addons/website_sale_collect/tests/test_payment_provider.py
+++ b/addons/website_sale_collect/tests/test_payment_provider.py
@@ -5,23 +5,24 @@
from odoo.addons.website_sale_collect.tests.common import ClickAndCollectCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestOnSitePaymentProvider(HttpCase, ClickAndCollectCommon):
-
def test_on_site_provider_available_when_in_store_delivery_is_chosen(self):
order = self._create_in_store_delivery_order()
- compatible_providers = self.env['payment.provider'].sudo()._get_compatible_providers(
+ PaymentProvider = self.env["payment.provider"].sudo()
+ compatible_providers = PaymentProvider._get_compatible_providers(
self.company.id, self.partner.id, self.amount, sale_order_id=order.id
)
- self.assertTrue(any(
- p.code == 'custom' and p.custom_mode == 'on_site' for p in compatible_providers
- ))
+ self.assertTrue(
+ any(p.code == "custom" and p.custom_mode == "on_site" for p in compatible_providers)
+ )
def test_on_site_provider_unavailable_when_no_in_store_delivery(self):
order = self._create_in_store_delivery_order(carrier_id=self.free_delivery.id)
- compatible_providers = self.env['payment.provider'].sudo()._get_compatible_providers(
+ PaymentProvider = self.env["payment.provider"].sudo()
+ compatible_providers = PaymentProvider._get_compatible_providers(
self.company.id, self.partner.id, self.amount, sale_order_id=order.id
)
- self.assertFalse(any(
- p.code == 'custom' and p.custom_mode == 'on_site' for p in compatible_providers
- ))
+ self.assertFalse(
+ any(p.code == "custom" and p.custom_mode == "on_site" for p in compatible_providers)
+ )
diff --git a/addons/website_sale_collect/tests/test_payment_transaction.py b/addons/website_sale_collect/tests/test_payment_transaction.py
index 14255156f04d4..a2c6e4da6be09 100644
--- a/addons/website_sale_collect/tests/test_payment_transaction.py
+++ b/addons/website_sale_collect/tests/test_payment_transaction.py
@@ -6,18 +6,17 @@
from odoo.addons.website_sale_collect.tests.common import ClickAndCollectCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestOnSitePaymentTransaction(HttpCase, ClickAndCollectCommon):
-
def test_choosing_on_site_payment_confirms_order(self):
- order = self._create_so(carrier_id=self.carrier.id, state='draft')
+ order = self._create_so(carrier_id=self.carrier.id, state="draft")
tx = self._create_transaction(
- flow='direct',
+ flow="direct",
sale_order_ids=[order.id],
- state='pending',
+ state="pending",
payment_method_id=self.provider.payment_method_ids.id,
)
- with mute_logger('odoo.addons.sale.models.payment_transaction'):
+ with mute_logger("odoo.addons.sale.models.payment_transaction"):
tx._post_process()
- self.assertEqual(order.state, 'sale')
+ self.assertEqual(order.state, "sale")
diff --git a/addons/website_sale_collect/tests/test_product_template.py b/addons/website_sale_collect/tests/test_product_template.py
index a7360e830cce3..4227bf2961176 100644
--- a/addons/website_sale_collect/tests/test_product_template.py
+++ b/addons/website_sale_collect/tests/test_product_template.py
@@ -8,19 +8,18 @@
from odoo.addons.website_sale_collect.tests.common import ClickAndCollectCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestProductTemplate(ClickAndCollectCommon):
-
def test_out_of_stock_product_available_when_allow_continue_selling(self):
product = self._create_product(allow_out_of_stock_order=True)
self.free_delivery.is_published = True
with MockRequest(self.env, website=self.website, sale_order_id=self.cart.id):
- comb_info = self.env['product.template']._get_additionnal_combination_info(
+ comb_info = self.env["product.template"]._get_additionnal_combination_info(
product,
quantity=1,
date=datetime(2000, 1, 1),
uom=self.uom_unit,
website=self.website,
)
- self.assertTrue(comb_info['delivery_stock_data']['in_stock'])
- self.assertTrue(comb_info['in_store_stock_data']['in_stock'])
+ self.assertTrue(comb_info["delivery_stock_data"]["in_stock"])
+ self.assertTrue(comb_info["in_store_stock_data"]["in_stock"])
diff --git a/addons/website_sale_collect/tests/test_sale_order.py b/addons/website_sale_collect/tests/test_sale_order.py
index 83808bb2d144c..79e36795157f7 100644
--- a/addons/website_sale_collect/tests/test_sale_order.py
+++ b/addons/website_sale_collect/tests/test_sale_order.py
@@ -1,15 +1,14 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo.fields import Command
from odoo.exceptions import ValidationError
+from odoo.fields import Command
from odoo.tests import tagged
from odoo.addons.website_sale_collect.tests.common import ClickAndCollectCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestSaleOrder(ClickAndCollectCommon):
-
@classmethod
def setUpClass(cls):
super().setUpClass()
@@ -24,54 +23,54 @@ def test_warehouse_is_updated_when_changing_delivery_line(self):
def test_setting_pickup_location_assigns_warehouse(self):
so = self._create_in_store_delivery_order()
- so._set_pickup_location('{"id":' + str(self.warehouse.id) + '}')
+ so._set_pickup_location('{"id":' + str(self.warehouse.id) + "}")
self.assertEqual(so.warehouse_id, self.warehouse)
def test_warehouse_is_not_reset_on_public_user_checkout(self):
warehouse_2 = self._create_warehouse()
so = self._create_in_store_delivery_order(partner_id=self.public_user.id)
- so._set_pickup_location('{"id":' + str(warehouse_2.id) + '}')
+ so._set_pickup_location('{"id":' + str(warehouse_2.id) + "}")
# change the partner_id as would happen in a checkout
so.partner_id = self.partner.id
self.assertEqual(so.warehouse_id, warehouse_2)
def test_warehouse_is_computed_based_on_pickup_location(self):
warehouse_2 = self._create_warehouse()
- so = self._create_in_store_delivery_order(pickup_location_data={'id': warehouse_2.id})
+ so = self._create_in_store_delivery_order(pickup_location_data={"id": warehouse_2.id})
self.assertEqual(so.warehouse_id, warehouse_2)
def test_fiscal_position_id_is_computed_from_pickup_location_partner(self):
- fp_be = self.env['account.fiscal.position'].create({
- 'name': "Test BE fiscal position",
- 'country_id': self.country_be.id,
- 'auto_apply': True,
+ fp_be = self.env["account.fiscal.position"].create({
+ "name": "Test BE fiscal position",
+ "country_id": self.country_be.id,
+ "auto_apply": True,
})
self.default_partner.country_id = self.country_us
self.warehouse.partner_id.country_id = self.country_be
so = self._create_in_store_delivery_order(
partner_shipping_id=self.default_partner.id,
- pickup_location_data={'id': self.warehouse.id},
+ pickup_location_data={"id": self.warehouse.id},
)
self.assertEqual(so.fiscal_position_id, fp_be)
def test_setting_pickup_location_assigns_correct_fiscal_position(self):
- fp_be = self.env['account.fiscal.position'].create({
- 'name': "Test BE fiscal position",
- 'country_id': self.country_be.id,
- 'auto_apply': True,
+ fp_be = self.env["account.fiscal.position"].create({
+ "name": "Test BE fiscal position",
+ "country_id": self.country_be.id,
+ "auto_apply": True,
})
so = self._create_in_store_delivery_order()
self.default_partner.country_id = self.country_be
warehouse = self._create_warehouse()
warehouse.partner_id = self.default_partner
- so._set_pickup_location('{"id":' + str(warehouse.id) + '}')
+ so._set_pickup_location('{"id":' + str(warehouse.id) + "}")
self.assertEqual(so.fiscal_position_id, fp_be)
def test_selecting_not_in_store_dm_resets_fiscal_position(self):
- fp_us = self.env['account.fiscal.position'].create({
- 'name': "Test US fiscal position",
- 'country_id': self.country_us.id,
- 'auto_apply': True,
+ fp_us = self.env["account.fiscal.position"].create({
+ "name": "Test US fiscal position",
+ "country_id": self.country_us.id,
+ "auto_apply": True,
})
so = self._create_in_store_delivery_order()
so.fiscal_position_id = fp_us
@@ -87,10 +86,9 @@ def test_free_qty_calculated_from_order_wh_if_dm_is_in_store(self):
self.assertEqual(free_qty, 10)
def test_prevent_buying_out_of_stock_products(self):
- cart = self._create_in_store_delivery_order(order_line=[Command.create({
- 'product_id': self.product_2.id,
- 'product_uom_qty': 5.0,
- })])
+ cart = self._create_in_store_delivery_order(
+ order_line=[Command.create({"product_id": self.product_2.id, "product_uom_qty": 5.0})]
+ )
cart.warehouse_id = self.warehouse
with self.assertRaises(ValidationError):
cart._check_cart_is_ready_to_be_paid()
@@ -98,10 +96,7 @@ def test_prevent_buying_out_of_stock_products(self):
def test_product_in_stock_is_available(self):
cart = self._create_in_store_delivery_order(
order_line=[
- Command.create({
- 'product_id': self.storable_product.id,
- 'product_uom_qty': 5.0,
- })
+ Command.create({"product_id": self.storable_product.id, "product_uom_qty": 5.0})
]
)
insufficient_stock_data = cart._get_insufficient_stock_data(self.warehouse.id)
@@ -110,14 +105,7 @@ def test_product_in_stock_is_available(self):
def test_product_out_of_stock_continue_selling_is_available(self):
self.product_2.allow_out_of_stock_order = True
cart = self._create_in_store_delivery_order(
- order_line=[
- Command.create(
- {
- 'product_id': self.product_2.id,
- 'product_uom_qty': 5.0,
- }
- )
- ]
+ order_line=[Command.create({"product_id": self.product_2.id, "product_uom_qty": 5.0})]
)
insufficient_stock_data = cart._get_insufficient_stock_data(self.warehouse.id)
self.assertFalse(insufficient_stock_data)
@@ -125,10 +113,7 @@ def test_product_out_of_stock_continue_selling_is_available(self):
def test_product_insufficient_stock_is_unavailable(self):
cart = self._create_in_store_delivery_order(
order_line=[
- Command.create({
- 'product_id': self.storable_product.id,
- 'product_uom_qty': 15.0,
- })
+ Command.create({"product_id": self.storable_product.id, "product_uom_qty": 15.0})
]
)
insufficient_stock_data = cart._get_insufficient_stock_data(self.warehouse.id)
@@ -137,30 +122,26 @@ def test_product_insufficient_stock_is_unavailable(self):
def test_insufficient_stock_with_mixed_uom_order_lines(self):
"""Test that the insufficient stock is correctly computed when the order lines
use different UoMs."""
- pack_of_6_id = self.ref('uom.product_uom_pack_6')
+ pack_of_6_id = self.ref("uom.product_uom_pack_6")
# 1 pack of 6 + 5 units = 11 units in the cart
cart = self._create_in_store_delivery_order(
order_line=[
- Command.create(
- {
- 'product_id': self.storable_product.id,
- 'product_uom_qty': 1.0,
- 'product_uom_id': pack_of_6_id,
- }
- ),
- Command.create(
- {
- 'product_id': self.storable_product.id,
- 'product_uom_qty': 5.0,
- 'product_uom_id': self.storable_product.uom_id.id,
- }
- ),
+ Command.create({
+ "product_id": self.storable_product.id,
+ "product_uom_qty": 1.0,
+ "product_uom_id": pack_of_6_id,
+ }),
+ Command.create({
+ "product_id": self.storable_product.id,
+ "product_uom_qty": 5.0,
+ "product_uom_id": self.storable_product.uom_id.id,
+ }),
]
)
# 10 units available, 11 requested, so 1 unit short
insufficient_stock_data = cart._get_insufficient_stock_data(self.warehouse.id)
ol_unit = cart.order_line.filtered(
- lambda l: l.product_uom_id == self.storable_product.uom_id
+ lambda line: line.product_uom_id == self.storable_product.uom_id
)
# only 4 units are available for the second order line instead of 5
self.assertEqual(insufficient_stock_data[ol_unit], 4)
@@ -168,32 +149,29 @@ def test_insufficient_stock_with_mixed_uom_order_lines(self):
def test_product_in_stock_with_mixed_uom_order_lines_is_available(self):
"""Test that if there is enough stock for all order lines the insufficient stock is
empty."""
- pack_of_6_id = self.ref('uom.product_uom_pack_6')
+ pack_of_6_id = self.ref("uom.product_uom_pack_6")
# 1 pack of 6 + 4 units = 10 units in the cart
- cart = self._create_in_store_delivery_order(order_line=[
- Command.create({
- 'product_id': self.storable_product.id,
- 'product_uom_qty': 4.0,
- 'product_uom_id': self.storable_product.uom_id.id,
- }),
- Command.create({
- 'product_id': self.storable_product.id,
- 'product_uom_qty': 1.0,
- 'product_uom_id': pack_of_6_id,
- }),
- ])
+ cart = self._create_in_store_delivery_order(
+ order_line=[
+ Command.create({
+ "product_id": self.storable_product.id,
+ "product_uom_qty": 4.0,
+ "product_uom_id": self.storable_product.uom_id.id,
+ }),
+ Command.create({
+ "product_id": self.storable_product.id,
+ "product_uom_qty": 1.0,
+ "product_uom_id": pack_of_6_id,
+ }),
+ ]
+ )
# 10 units available, 10 requested
insufficient_stock_data = cart._get_insufficient_stock_data(self.warehouse.id)
self.assertFalse(insufficient_stock_data)
def test_out_of_stock_product_is_unavailable(self):
cart = self._create_in_store_delivery_order(
- order_line=[
- Command.create({
- 'product_id': self.product_2.id,
- 'product_uom_qty': 5.0,
- }),
- ]
+ order_line=[Command.create({"product_id": self.product_2.id, "product_uom_qty": 5.0})]
)
insufficient_stock_data = cart._get_insufficient_stock_data(self.warehouse.id)
self.assertIn(cart.order_line, insufficient_stock_data)
@@ -202,10 +180,7 @@ def test_product_in_different_warehouse_is_unavailable(self):
self.warehouse_2 = self._create_warehouse()
cart = self._create_in_store_delivery_order(
order_line=[
- Command.create({
- 'product_id': self.storable_product.id,
- 'product_uom_qty': 5.0,
- })
+ Command.create({"product_id": self.storable_product.id, "product_uom_qty": 5.0})
]
)
insufficient_stock_data = cart._get_insufficient_stock_data(self.warehouse_2.id)
diff --git a/addons/website_sale_collect/tests/test_website.py b/addons/website_sale_collect/tests/test_website.py
index 07df92096f695..f0d77c7ac9499 100644
--- a/addons/website_sale_collect/tests/test_website.py
+++ b/addons/website_sale_collect/tests/test_website.py
@@ -6,9 +6,8 @@
from odoo.addons.website_sale_collect.tests.common import ClickAndCollectCommon
-@tagged('post_install', '-at_install')
+@tagged("post_install", "-at_install")
class TestWebsite(ClickAndCollectCommon):
-
@classmethod
def setUpClass(cls):
super().setUpClass()
diff --git a/addons/website_sale_collect/views/delivery_carrier_views.xml b/addons/website_sale_collect/views/delivery_carrier_views.xml
index 8e0e3a7b3228f..f2615329e4d35 100644
--- a/addons/website_sale_collect/views/delivery_carrier_views.xml
+++ b/addons/website_sale_collect/views/delivery_carrier_views.xml
@@ -1,6 +1,6 @@
-
+
In-store Delivery Carrier Form