19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:12 +01:00
parent 79f83631d5
commit 73afc09215
6267 changed files with 1534193 additions and 1130106 deletions

View file

@ -2,7 +2,10 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import website
from . import product_combo
from . import product_feed
from . import product_product
from . import product_ribbon
from . import product_template
from . import res_config_settings
from . import sale_order

View file

@ -0,0 +1,25 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class ProductCombo(models.Model):
_inherit = 'product.combo'
def _get_max_quantity(self, website, sale_order, **kwargs):
""" The max quantity of a combo is the max quantity of its combo item with the highest max
quantity. If one of the combo items has no max quantity, then the combo also has no max
quantity.
Note: self.ensure_one()
:param website website: The website for which to compute the max quantity.
:return: The max quantity of the combo.
:rtype: float | None
"""
self.ensure_one()
max_quantities = [
item.product_id._get_max_quantity(website, sale_order, **kwargs)
for item in self.combo_item_ids
]
return max(max_quantities) if (None not in max_quantities) else None

View file

@ -0,0 +1,15 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class ProductFeed(models.Model):
_inherit = 'product.feed'
def _prepare_gmc_stock_info(self, product):
"""Override of `website_sale` to check the stock level if the current product cannot be out
of stock."""
stock_info = super()._prepare_gmc_stock_info(product)
if product._is_sold_out():
stock_info['availability'] = 'out_of_stock'
return stock_info

View file

@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, fields, _
from odoo.http import request
from odoo import _, fields, models
class ProductProduct(models.Model):
@ -14,49 +12,82 @@ class ProductProduct(models.Model):
self.ensure_one()
return partner in self.stock_notification_partner_ids
def _get_cart_qty(self, website=None):
if not self.allow_out_of_stock_order:
website = website or self.env['website'].get_current_website()
# When the cron is run manually, request has no attribute website, and that would cause a crash
# so we check for it
cart = website and request and hasattr(request, 'website') and website.sale_get_order() or None
if cart:
return sum(
cart._get_common_product_lines(product=self).mapped('product_uom_qty')
)
return 0
def _get_max_quantity(self, website, sale_order, **kwargs):
""" The max quantity of a product is the difference between the quantity that's free to use
and the quantity that's already been added to the cart.
Note: self.ensure_one()
:param website website: The website for which to compute the max quantity.
:return: The max quantity of the product.
:rtype: float | None
"""
self.ensure_one()
if self.is_storable and not self.allow_out_of_stock_order:
free_qty = website._get_product_available_qty(self.sudo(), **kwargs)
cart_qty = sale_order._get_cart_qty(self.id)
return free_qty - cart_qty
return None
def _is_sold_out(self):
combination_info = self.with_context(website_sale_stock_get_quantity=True).product_tmpl_id._get_combination_info(product_id=self.id)
return combination_info['product_type'] == 'product' and combination_info['free_qty'] <= 0
"""Return whether the product is sold out (no available quantity).
If a product inventory is not tracked, or if it's allowed to be sold regardless
of availabilities, the product is never considered sold out.
:return: whether the product can still be sold
:rtype: bool
"""
self.ensure_one()
if not self.is_storable or self.allow_out_of_stock_order:
return False
free_qty = self.env['website'].get_current_website()._get_product_available_qty(self.sudo())
return free_qty <= 0
def _website_show_quick_add(self):
return (self.allow_out_of_stock_order or not self._is_sold_out()) and super()._website_show_quick_add()
return not self._is_sold_out() and super()._website_show_quick_add()
def _send_availability_email(self):
website = self.env['website'].get_current_website()
for product in self.search([('stock_notification_partner_ids', '!=', False)]):
if product._is_sold_out():
continue
for partner in product.stock_notification_partner_ids:
self_ctxt = self.with_context(lang=partner.lang)
self_ctxt = self.with_context(lang=partner.lang).with_user(website.salesperson_id)
product_ctxt = product.with_context(lang=partner.lang)
body_html = self_ctxt.env['ir.qweb']._render(
'website_sale_stock.availability_email_body', {'product': product_ctxt})
msg = self_ctxt.env['mail.message'].sudo().new(dict(body=body_html, record_name=product_ctxt.name))
full_mail = self_ctxt.env['mail.render.mixin']._render_encapsulate(
"mail.mail_notification_light",
'website_sale_stock.availability_email_body',
{'product': product_ctxt},
)
full_mail = product_ctxt.env['mail.render.mixin']._render_encapsulate(
'mail.mail_notification_light',
body_html,
add_context=dict(message=msg, model_description=_("Product")),
add_context={'model_description': _("Product")},
context_record=product_ctxt,
)
context = {'lang': partner.lang} # Use partner lang to translate mail subject below
mail_values = {
"subject": _("The product '%(product_name)s' is now available", product_name=product_ctxt.name),
"email_from": (product.company_id.partner_id or self.env.user).email_formatted,
"email_to": partner.email_formatted,
"body_html": full_mail,
'subject': _(
"The product '%(product_name)s' is now available",
product_name=product_ctxt.name
),
'email_from': (website.company_id.partner_id or self_ctxt.env.user).email_formatted,
'email_to': partner.email_formatted,
'body_html': full_mail,
}
del context
mail = self_ctxt.env['mail.mail'].sudo().create(mail_values)
mail.send(raise_exception=False)
product.stock_notification_partner_ids -= partner
def _to_markup_data(self, website):
""" Override of `website_sale` to include the product availability in the offer. """
markup_data = super()._to_markup_data(website)
if self.is_product_variant and self.is_storable:
if not self._is_sold_out():
availability = 'https://schema.org/InStock'
else:
availability = 'https://schema.org/OutOfStock'
markup_data['offers']['availability'] = availability
return markup_data

View file

@ -0,0 +1,28 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class ProductRibbon(models.Model):
_inherit = 'product.ribbon'
assign = fields.Selection(
selection_add=[('out_of_stock', "when out of stock")],
ondelete={'out_of_stock': 'cascade'},
help=(
"Defines how this ribbon is assigned to products:\n"
"- Manually: You assign the ribbon manually to products.\n"
"- Sale: Applied when the product is visibly on sale.\n"
"- New: Applied based on the New period you will define.\n"
"- Out Of Stock: Applied when the product is out of stock."
),
)
def _is_applicable_for(self, product, price_data):
"""Override of `website_sale` to handle `out_of_stock` ribbons."""
return super()._is_applicable_for(product, price_data) or (
product
and self.assign == 'out_of_stock'
and not product.product_tmpl_id.allow_out_of_stock_order
and product._is_sold_out()
)

View file

@ -1,65 +1,135 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
from odoo.tools.translate import html_translate
from odoo import api, fields, models
from odoo.http import request
from odoo.tools import float_round
from odoo.tools.translate import html_translate
from odoo.addons.website.models import ir_http
class ProductTemplate(models.Model):
_inherit = 'product.template'
allow_out_of_stock_order = fields.Boolean(string='Continue selling when out-of-stock', default=True)
allow_out_of_stock_order = fields.Boolean(string="Sell when Out-of-Stock", default=True)
available_threshold = fields.Float(string='Show Threshold', default=5.0)
show_availability = fields.Boolean(string='Show availability Qty', default=False)
available_threshold = fields.Float(string="Show Threshold", default=5.0)
show_availability = fields.Boolean(string="Show availability Qty", default=False)
out_of_stock_message = fields.Html(string="Out-of-Stock Message", translate=html_translate)
def _get_combination_info(self, combination=False, product_id=False, add_qty=1, pricelist=False, parent_combination=False, only_template=False):
combination_info = super(ProductTemplate, self)._get_combination_info(
combination=combination, product_id=product_id, add_qty=add_qty, pricelist=pricelist,
parent_combination=parent_combination, only_template=only_template)
def _is_sold_out(self):
"""Return whether the product is sold out (no available quantity).
If a product inventory is not tracked, or if it's allowed to be sold regardless
of availabilities, the product is never considered sold out.
Note: only checks the availability of the first variant of the template.
:return: whether the product can still be sold
:rtype: bool
"""
if not self.is_storable or self.allow_out_of_stock_order:
return False
return self.product_variant_id._is_sold_out()
def _website_show_quick_add(self):
return (
super()._website_show_quick_add()
and not self._is_sold_out()
)
def _get_additionnal_combination_info(self, product_or_template, quantity, uom, date, website):
res = super()._get_additionnal_combination_info(product_or_template, quantity, uom, date, website)
if not self.env.context.get('website_sale_stock_get_quantity'):
return combination_info
return res
if combination_info['product_id']:
product = self.env['product.product'].sudo().browse(combination_info['product_id'])
website = self.env['website'].get_current_website()
free_qty = product.with_context(warehouse=website._get_warehouse_available()).free_qty
has_stock_notification = product._has_stock_notification(self.env.user.partner_id) \
or request \
and product.id in request.session.get('product_with_stock_notification_enabled',
set())
if product_or_template.type == 'combo':
# The max quantity of a combo product is the max quantity of its combo with the lowest
# max quantity. If none of the combos has a max quantity, then the combo product also
# has no max quantity.
max_quantities = [
max_quantity for combo in product_or_template.sudo().combo_ids
if (max_quantity := combo._get_max_quantity(website, request.cart)) is not None
]
if max_quantities:
# No uom conversion: combo are not supposed to be sold with other uoms.
res['max_combo_quantity'] = min(max_quantities)
if not product_or_template.is_storable:
return res
res.update({
'is_storable': True,
'allow_out_of_stock_order': product_or_template.allow_out_of_stock_order,
'available_threshold': product_or_template.available_threshold,
})
if product_or_template.is_product_variant:
product_sudo = product_or_template.sudo()
computed_qty = product_sudo.uom_id._compute_quantity(
website._get_product_available_qty(product_sudo),
to_unit=uom,
round=False,
)
free_qty = float_round(computed_qty, precision_digits=0, rounding_method='DOWN')
has_stock_notification = (
product_sudo._has_stock_notification(self.env.user.partner_id)
or (
request
and product_sudo.id in request.session.get(
'product_with_stock_notification_enabled', set()
)
)
)
stock_notification_email = request and request.session.get('stock_notification_email', '')
combination_info.update({
cart_quantity = 0.0
if not product_sudo.allow_out_of_stock_order:
cart_quantity = product_sudo.uom_id._compute_quantity(
request.cart._get_cart_qty(product_sudo.id),
to_unit=uom,
)
res.update({
'free_qty': free_qty,
'product_type': product.type,
'product_template': self.id,
'available_threshold': self.available_threshold,
'cart_qty': product._get_cart_qty(website),
'uom_name': product.uom_id.name,
'uom_rounding': product.uom_id.rounding,
'allow_out_of_stock_order': self.allow_out_of_stock_order,
'show_availability': self.show_availability,
'out_of_stock_message': self.out_of_stock_message,
'cart_qty': cart_quantity,
'uom_name': uom.name,
'uom_rounding': uom.rounding,
'show_availability': product_sudo.show_availability,
'out_of_stock_message': product_sudo.out_of_stock_message,
'has_stock_notification': has_stock_notification,
'stock_notification_email': stock_notification_email,
})
else:
product_template = self.sudo()
combination_info.update({
res.update({
'free_qty': 0,
'product_type': product_template.type,
'allow_out_of_stock_order': product_template.allow_out_of_stock_order,
'available_threshold': product_template.available_threshold,
'product_template': product_template.id,
'cart_qty': 0,
})
return combination_info
return res
def _is_sold_out(self):
return self.product_variant_id._is_sold_out()
@api.model
def _get_additional_configurator_data(
self, product_or_template, date, currency, pricelist, *, uom=None, **kwargs
):
"""Override of `website_sale` to append stock data.
def _website_show_quick_add(self):
return (self.allow_out_of_stock_order or not self._is_sold_out()) and super()._website_show_quick_add()
:param product.product|product.template product_or_template: The product for which to get
additional data.
:param datetime date: The date to use to compute prices.
:param res.currency currency: The currency to use to compute prices.
:param product.pricelist pricelist: The pricelist to use to compute prices.
:param uom.uom uom: The uom to use to compute prices.
:param dict kwargs: Locally unused data passed to overrides.
:rtype: dict
:return: A dict containing additional data about the specified product.
"""
data = super()._get_additional_configurator_data(
product_or_template, date, currency, pricelist, **kwargs
)
if (website := ir_http.get_request_website()) and product_or_template.is_product_variant:
max_quantity = product_or_template._get_max_quantity(website, request.cart, **kwargs)
if max_quantity is not None:
if uom:
max_quantity = product_or_template.uom_id._compute_quantity(max_quantity, to_unit=uom)
data['free_qty'] = max_quantity
return data

View file

@ -1,43 +1,30 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, api
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
allow_out_of_stock_order = fields.Boolean(
string='Continue selling when out-of-stock',
default=True)
available_threshold = fields.Float(
string='Show Threshold',
default=5.0)
show_availability = fields.Boolean(
string='Show availability Qty',
default=False)
default_allow_out_of_stock_order = fields.Boolean(
string="Continue selling when out-of-stock",
default=True,
default_model='product.template',
)
default_available_threshold = fields.Float(
string="Show Threshold",
default=5.0,
default_model='product.template',
)
default_show_availability = fields.Boolean(
string="Show availability Qty",
default=False,
default_model='product.template',
)
website_warehouse_id = fields.Many2one(
'stock.warehouse',
related='website_id.warehouse_id',
domain="[('company_id', '=', website_company_id)]",
readonly=False)
def set_values(self):
super(ResConfigSettings, self).set_values()
IrDefault = self.env['ir.default'].sudo()
IrDefault.set('product.template', 'allow_out_of_stock_order', self.allow_out_of_stock_order)
IrDefault.set('product.template', 'available_threshold', self.available_threshold)
IrDefault.set('product.template', 'show_availability', self.show_availability)
@api.model
def get_values(self):
res = super(ResConfigSettings, self).get_values()
IrDefault = self.env['ir.default'].sudo()
allow_out_of_stock_order = IrDefault.get('product.template', 'allow_out_of_stock_order')
res.update(
allow_out_of_stock_order=allow_out_of_stock_order if allow_out_of_stock_order is not None else True,
available_threshold=IrDefault.get('product.template', 'available_threshold') or 5.0,
show_availability=IrDefault.get('product.template', 'show_availability') or False)
return res
readonly=False,
)

View file

@ -1,34 +1,37 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, _
from odoo import models
from odoo.exceptions import ValidationError
from odoo.tools import float_round
class SaleOrder(models.Model):
_inherit = 'sale.order'
def _get_warehouse_available(self):
self.ensure_one()
warehouse = self.website_id._get_warehouse_available()
if not warehouse and self.user_id and self.company_id:
warehouse = self.user_id.with_company(self.company_id.id)._get_default_warehouse_id()
if not warehouse:
warehouse = self.env.user._get_default_warehouse_id()
return warehouse
def _compute_warehouse_id(self):
website_orders = self.filtered('website_id')
super(SaleOrder, self - website_orders)._compute_warehouse_id()
for order in website_orders:
order.warehouse_id = order._get_warehouse_available()
if order.website_id.warehouse_id:
order.warehouse_id = order.website_id.warehouse_id
else:
super(SaleOrder, order)._compute_warehouse_id()
if not order.warehouse_id:
order.warehouse_id = self.env.user._get_default_warehouse_id()
def _verify_updated_quantity(self, order_line, product_id, new_qty, **kwargs):
def _verify_updated_quantity(self, order_line, product_id, new_qty, uom_id, **kwargs):
self.ensure_one()
product = self.env['product.product'].browse(product_id)
if product.type == 'product' and not product.allow_out_of_stock_order:
product_qty_in_cart, available_qty = self._get_cart_and_free_qty(
line=order_line, product=product, **kwargs
)
if product.is_storable and not product.allow_out_of_stock_order:
uom = self.env['uom.uom'].browse(uom_id)
product_uom = product.uom_id
product_qty_in_cart, available_qty = self._get_cart_and_free_qty(product)
# Convert cart and available quantities to the requested uom
product_qty_in_cart = product_uom._compute_quantity(product_qty_in_cart, uom)
available_qty = product_uom._compute_quantity(available_qty, uom, round=False)
available_qty = float_round(available_qty, precision_digits=0, rounding_method='DOWN')
old_qty = order_line.product_uom_qty if order_line else 0
added_qty = new_qty - old_qty
@ -36,81 +39,106 @@ class SaleOrder(models.Model):
if available_qty < total_cart_qty:
allowed_line_qty = available_qty - (product_qty_in_cart - old_qty)
if allowed_line_qty > 0:
def format_qty(qty):
return int(qty) if float(qty).is_integer() else qty
if order_line:
order_line._set_shop_warning_stock(total_cart_qty, available_qty)
warning = order_line._set_shop_warning_stock(
format_qty(total_cart_qty),
format_qty(available_qty),
save=False,
)
else:
self._set_shop_warning_stock(total_cart_qty, available_qty)
else: # 0 or negative allowed_qty
# if existing line: it will be deleted
# if no existing line: no line will be created
self.shop_warning = _(
"Some products became unavailable and your cart has been updated. We're sorry for the inconvenience.")
return allowed_line_qty, order_line.shop_warning or self.shop_warning
return super()._verify_updated_quantity(order_line, product_id, new_qty, **kwargs)
warning = self.env._(
"You ask for %(desired_qty)s products but only %(available_qty)s is"
" available.",
desired_qty=format_qty(total_cart_qty),
available_qty=format_qty(available_qty),
)
elif order_line:
# Line will be deleted
warning = self.env._(
"Some products became unavailable and your cart has been updated. We're"
" sorry for the inconvenience."
)
else:
warning = self.env._(
"%(product_name)s has not been added to your cart since it is not available.",
product_name=product.name,
)
return allowed_line_qty, warning
return super()._verify_updated_quantity(order_line, product_id, new_qty, uom_id, **kwargs)
def _get_cart_and_free_qty(self, line=None, product=None, **kwargs):
""" Get cart quantity and free quantity for given product or line's product.
def _get_cart_and_free_qty(self, product):
"""Get cart quantity and free quantity for given product.
Note: self.ensure_one()
:param SaleOrderLine line: The optional line
:param ProductProduct product: The optional product
:param product: `product.product` record.
:returns: cart quantity and available quantity in the product uom
:rtype: tuple
"""
self.ensure_one()
if not line and not product:
return 0, 0
cart_qty = sum(
self._get_common_product_lines(line, product, **kwargs).mapped('product_uom_qty')
)
free_qty = (product or line.product_id).with_context(warehouse=self.warehouse_id.id).free_qty
return cart_qty, free_qty
product.ensure_one()
def _get_common_product_lines(self, line=None, product=None, **kwargs):
""" Get the lines with the same product or line's product
return self._get_cart_qty(product.id), self._get_free_qty(product)
:param SaleOrderLine line: The optional line
:param ProductProduct product: The optional product
def _get_free_qty(self, product):
return product.with_context(warehouse_id=self._get_shop_warehouse_id()).free_qty
def _get_shop_warehouse_id(self):
"""Return the warehouse to use for shop availability checks.
If no warehouse is specified on the website, all warehouses are considered,
regardless of the warehouse automatically assigned to the order.
Note: self.ensure_one()
:returns: `stock.warehouse` id
:rtype: int or False
"""
if not line and not product:
return self.env['sale.order.line']
product = product or line.product_id
return self.order_line.filtered(lambda l: l.product_id == product)
def _set_shop_warning_stock(self, desired_qty, new_qty):
self.ensure_one()
self.shop_warning = _(
'You ask for %(desired_qty)s products but only %(new_qty)s is available',
desired_qty=desired_qty, new_qty=new_qty
return self.website_id.warehouse_id.id
def _get_cart_qty(self, product_id):
"""Return the quantity of the given product in the current cart, if any.
:param int product_id: `product.product` id
:return: product quantity in the product uom
:rtype: float
"""
if not self:
return 0.0
order_lines = self._get_common_product_lines(product_id)
return sum(
order_lines.mapped(
lambda sol: sol.product_uom_id._compute_quantity(
sol.product_uom_qty, sol.product_id.uom_id,
)
)
)
return self.shop_warning
def _get_cache_key_for_line(self, line):
return line.product_id
def _get_common_product_lines(self, product_id=None):
"""Get all the lines of the current order with the given product."""
return self.order_line.filtered(lambda sol: sol.product_id.id == product_id)
def _get_context_for_line(self, line):
return {
'website_sale_stock_get_quantity': True,
}
def _check_cart_is_ready_to_be_paid(self):
values = [
line.shop_warning
for line in self.order_line
if not line._check_availability()
]
if values:
raise ValidationError(' '.join(values))
return super()._check_cart_is_ready_to_be_paid()
def _filter_can_send_abandoned_cart_mail(self):
""" Filter sale orders on their product availability. """
self = super()._filter_can_send_abandoned_cart_mail()
combination_info_cache = {}
"""Filter sale orders on their product availability."""
return super()._filter_can_send_abandoned_cart_mail().filtered(
lambda so: so._all_product_available()
)
def _are_all_product_available_for_purchase(sale_order):
for line in sale_order.order_line:
product = line.product_id
if product.type != 'product':
continue
cache_key = self._get_cache_key_for_line(line)
combination_info = combination_info_cache.get(cache_key)
if not combination_info:
combination_info = product.with_context(**self._get_context_for_line(line), website_id=sale_order.website_id.id)._get_combination_info_variant(add_qty=line.product_uom_qty)
combination_info_cache[cache_key] = combination_info
if not product.allow_out_of_stock_order and combination_info['free_qty'] == 0:
return False
def _all_product_available(self):
self.ensure_one()
if not (lines := self.order_line):
return True
# If none of the products in the checkout are available for purchase (empty inventory, for example),
# then the email won't be sent.
return self.filtered(_are_all_product_available_for_purchase)
return not any(product._is_sold_out() for product in lines.product_id)

View file

@ -1,18 +1,46 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, models
from odoo import models
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
def _set_shop_warning_stock(self, desired_qty, new_qty):
def _set_shop_warning_stock(self, desired_qty, new_qty, save=True):
self.ensure_one()
self.shop_warning = _(
'You ask for %(desired_qty)s %(product_name)s but only %(new_qty)s is available',
warning = self.env._(
"You ask for %(desired_qty)s %(product_name)s but only %(new_qty)s is available",
desired_qty=desired_qty, product_name=self.product_id.name, new_qty=new_qty
)
return self.shop_warning
if save:
self.shop_warning = warning
return warning
def _get_max_line_qty(self):
max_quantity = self._get_max_available_qty()
return self.product_uom_qty + max_quantity if (max_quantity is not None) else None
def _get_max_available_qty(self):
return self.product_id.free_qty - self.product_id._get_cart_qty()
""" The max quantity of a combo product is the max quantity of its selected combo item with
the lowest max quantity. If none of the combo items has a max quantity, then the combo
product also has no max quantity.
"""
self.ensure_one()
cart_and_free_quantities = [
line.order_id._get_cart_and_free_qty(line.product_id)
for line in self._get_lines_with_price()
if line.product_id.is_storable and not line.product_id.allow_out_of_stock_order
]
max_quantities = [
free_qty - cart_qty for cart_qty, free_qty in cart_and_free_quantities
]
return min(max_quantities, default=None)
def _check_availability(self):
self.ensure_one()
if self.product_id.is_storable and not self.product_id.allow_out_of_stock_order:
cart_qty, avl_qty = self.order_id._get_cart_and_free_qty(self.product_id)
if cart_qty > avl_qty:
self._set_shop_warning_stock(cart_qty, max(avl_qty, 0))
return False
return True

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class Website(models.Model):
@ -7,23 +8,16 @@ class Website(models.Model):
warehouse_id = fields.Many2one('stock.warehouse', string='Warehouse')
def _prepare_sale_order_values(self, partner_sudo):
values = super()._prepare_sale_order_values(partner_sudo)
def _get_product_available_qty(self, product, **kwargs):
"""Give the available quantity of a given product.
warehouse_id = self._get_warehouse_available()
if warehouse_id:
values['warehouse_id'] = warehouse_id
return values
NB: this method is only meant to be used on the shop before the checkout.
For checkout steps, please use `cart._get_free_qty` instead to consider
the chosen warehouse for delivery (website_sale_collect).
def _get_warehouse_available(self):
return (
self.warehouse_id.id or
self.env['ir.default'].get('sale.order', 'warehouse_id', company_id=self.company_id.id) or
self.env['ir.default'].get('sale.order', 'warehouse_id') or
self.env['stock.warehouse'].sudo().search([('company_id', '=', self.company_id.id)], limit=1).id
)
# FIXME VFE check if still needed
def sale_get_order(self, *args, **kwargs):
so = super().sale_get_order(*args, **kwargs)
return so.with_context(warehouse=so.warehouse_id.id) if so else so
:param product: product.product record
:param dict kwargs: unused parameters, available for overrides
:return: available quantity
:rtype: float
"""
return product.with_context(warehouse_id=self.warehouse_id.id).free_qty