Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] shopinvader_sale_configurator_option: Fix bug on shopinvader price computation when using pricelist discount policy: without_discount #34

Draft
wants to merge 21 commits into
base: 14.0-shopinvader
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
be94d87
[IMP] start adding configurator module for shopinavder
sebastienbeau Jun 4, 2021
56be3c3
[IMP] add categ of option
sebastienbeau Jun 15, 2021
768855d
[IMP] refactor code to avoid duplicated code
sebastienbeau Jul 23, 2021
dce00d3
[IMP] start adding services (extraction of existing custom)
sebastienbeau Nov 7, 2021
0aaf697
[REF] refactor code and start adding test
sebastienbeau Nov 8, 2021
a151c19
[IMP] add area API
sebastienbeau Nov 9, 2021
78caa74
[REF] refactor code for options
sebastienbeau Nov 10, 2021
5e2588d
[ADD] add module shopinvader_sale_configurator_variant
sebastienbeau Nov 10, 2021
57cb6bc
[FIX] fix version of onchange helper
sebastienbeau Nov 9, 2021
6a95fab
[FIX] use specific branch of shopinvader
sebastienbeau Nov 10, 2021
60b8618
[FIX] fix shopinvader_sale_configurator_variant getting name
sebastienbeau Nov 11, 2021
a9e98ca
[REF] refactor code to get price info and all option info
sebastienbeau Nov 12, 2021
edd6b14
[FIX] fix shopinvader_sale_configurator_variants
sebastienbeau Nov 15, 2021
7f59eb9
[FIX] fix perf issue
sebastienbeau Dec 13, 2021
3fbc40b
[IMP] add test to ensure onchange playing to not do any mess
sebastienbeau Mar 24, 2022
5bb26c7
[FIX] fix warnings
sebastienbeau Mar 24, 2022
95d26bd
[IMP] update dependency
sebastienbeau Mar 24, 2022
c1d4d78
shopinvader_sale_configurator_variant: add test
sebastienbeau Jun 23, 2022
f0c8f31
shopinvader_sale_configurator_variant: force price to 0 as onchange s…
sebastienbeau Aug 1, 2022
aacdeb1
shopinvader_sale_configurator_option: fix price computation with pric…
sebastienbeau Sep 8, 2022
23fa48e
[FIX] shopinvader_sale_configurator_option: Fix bug on shopinvader pr…
paradoxxxzero Jan 13, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions setup/shopinvader_sale_configurator_option/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
6 changes: 6 additions & 0 deletions setup/shopinvader_sale_configurator_option_area/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
6 changes: 6 additions & 0 deletions setup/shopinvader_sale_configurator_variant/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
2 changes: 2 additions & 0 deletions shopinvader_sale_configurator_option/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import services
28 changes: 28 additions & 0 deletions shopinvader_sale_configurator_option/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2021 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).


{
"name": "Shopinvader Sale Configurator Option",
"summary": "Shopinvader Sale Configurator Option",
"version": "14.0.1.0.0",
"category": "Shopinvader",
"website": "https://github.com/akretion/sale-configurator",
"author": " Akretion",
"license": "AGPL-3",
"application": False,
"installable": True,
"external_dependencies": {
"python": [],
"bin": [],
},
"depends": [
"shopinvader",
"sale_configurator_option",
],
"data": [
"data/ir_export_product.xml",
],
"demo": [],
}
65 changes: 65 additions & 0 deletions shopinvader_sale_configurator_option/data/ir_export_product.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="ir_exp_shopinvader_option_id" model="ir.exports.line">
<field name="name">configurable_option_ids/id</field>
<field name="target">configurable_option_ids:options/id</field>
<field name="export_id" ref="shopinvader.ir_exp_shopinvader_variant" />
</record>

<record id="ir_exp_shopinvader_option_product_name" model="ir.exports.line">
<field name="name">configurable_option_ids/product_id/name</field>
<field
name="target"
>configurable_option_ids:options/product_id:product/name</field>
<field name="export_id" ref="shopinvader.ir_exp_shopinvader_variant" />
</record>

<record id="ir_exp_shopinvader_option_product_sku" model="ir.exports.line">
<field name="name">configurable_option_ids/product_id/default_code</field>
<field
name="target"
>configurable_option_ids:options/product_id:product/default_code:sku</field>
<field name="export_id" ref="shopinvader.ir_exp_shopinvader_variant" />
</record>

<record id="ir_exp_shopinvader_option_product_categ_name" model="ir.exports.line">
<field name="name">configurable_option_ids/product_id/categ_id/name</field>
<field
name="target"
>configurable_option_ids:options/product_id:product/categ_id:categ/name</field>
<field name="export_id" ref="shopinvader.ir_exp_shopinvader_variant" />
</record>

<record id="ir_exp_shopinvader_option_product_categ_id" model="ir.exports.line">
<field name="name">configurable_option_ids/product_id/categ_id/id</field>
<field
name="target"
>configurable_option_ids:options/product_id:product/categ_id:categ/id</field>
<field name="export_id" ref="shopinvader.ir_exp_shopinvader_variant" />
</record>

<record id="ir_exp_shopinvader_option_product_price" model="ir.exports.line">
<field name="name">configurable_option_ids/product_id/shopinvader_price</field>
<field
name="target"
>configurable_option_ids:options/product_id:product/shopinvader_price:price</field>
<field name="export_id" ref="shopinvader.ir_exp_shopinvader_variant" />
</record>

<record id="ir_exp_shopinvader_option_type" model="ir.exports.line">
<field name="name">configurable_option_ids/option_qty_type</field>
<field
name="target"
>configurable_option_ids:options/option_qty_type:type</field>
<field name="export_id" ref="shopinvader.ir_exp_shopinvader_variant" />
</record>

<record id="ir_exp_shopinvader_option_is_default" model="ir.exports.line">
<field name="name">configurable_option_ids/is_default_option</field>
<field
name="target"
>configurable_option_ids:options/is_default_option:is_default</field>
<field name="export_id" ref="shopinvader.ir_exp_shopinvader_variant" />
</record>

</odoo>
4 changes: 4 additions & 0 deletions shopinvader_sale_configurator_option/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import product_product
from . import shopinvader_variant
from . import sale_order_line
from . import product_pricelist
18 changes: 18 additions & 0 deletions shopinvader_sale_configurator_option/models/product_pricelist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2022 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import models


class ProductPricelist(models.Model):
_inherit = "product.pricelist"

def get_product_price_rule(
self, product, quantity, partner, date=False, uom_id=False
):
# The following line will convert NewId with origin to normal record
product = self.env["product.product"].browse(product.ids)
return super().get_product_price_rule(
product, quantity, partner, date=date, uom_id=uom_id
)
55 changes: 55 additions & 0 deletions shopinvader_sale_configurator_option/models/product_product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Copyright 2021 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models


class ProductProduct(models.Model):
_inherit = "product.product"

shopinvader_price = fields.Serialized(
compute="_compute_shopinvader_price",
string="Computed Price for Shopinvader",
help=(
"Option are not bind on shopinvader, but this field allow to get "
"the price in the shopinvader context"
),
)

def _compute_product_price(self):
tmp_records = self.filtered(lambda s: isinstance(s.id, models.NewId))
real_records = self - tmp_records
if real_records:
super(ProductProduct, real_records)._compute_product_price()
if tmp_records:
for tmp_record in tmp_records:
tmp_record.price = tmp_record._origin.price

def _compute_shopinvader_price(self):
for record in self:
backend = self._context["shopinvader_backend"]
# Create a New shopinvader Variant to be able to call native code
# Even if option are not binded
shopinvader_variant = self.env["shopinvader.variant"].new(
{
"backend_id": backend.id,
"lang_id": backend.lang_ids[0].id,
}
)

# Since shopinvader.variant.record_id is a Many2one inherited from
# product.product, its field is marked as delegate.
# As delegate fields get a NewId when the record is created
# (https://github.com/odoo/odoo/blob/15.0/odoo/fields.py#L2783-L2785)
# we need to disable the delegation otherwise the price computation
# will fail in case of pricelist because it considers the record.id
# to be an int:
# https://github.com/odoo/odoo/blob/15.0/addons/product/models/product_pricelist.py#L94 # noqa

old_delegate = shopinvader_variant._fields["record_id"].delegate
shopinvader_variant._fields["record_id"].delegate = False
shopinvader_variant.record_id = record
shopinvader_variant._fields["record_id"].delegate = old_delegate

record.shopinvader_price = shopinvader_variant.price
21 changes: 21 additions & 0 deletions shopinvader_sale_configurator_option/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2021 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import models


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"

# Playing the onchange on option_ids is useless and broken so don't do it
def play_onchanges(self, vals, fields):
fields = [field for field in fields if field != "option_ids"]
option_ids = vals.pop("option_ids", None)
res = super().play_onchanges(vals, fields)
# restore original option_ids values
if option_ids is not None:
res["option_ids"] = option_ids
else:
res.pop("option_ids", None)
return res
17 changes: 17 additions & 0 deletions shopinvader_sale_configurator_option/models/shopinvader_variant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2021 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).


from odoo import models


class ShopinvaderVariant(models.Model):
_inherit = "shopinvader.variant"

def jsonify(self, parser, one=False):
backend = self.backend_id
backend.ensure_one()
return super(
ShopinvaderVariant, self.with_context(shopinvader_backend=backend)
).jsonify(parser, one=one)
2 changes: 2 additions & 0 deletions shopinvader_sale_configurator_option/services/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import cart
from . import abstract_sale
50 changes: 50 additions & 0 deletions shopinvader_sale_configurator_option/services/abstract_sale.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright 2021 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).


from odoo.addons.component.core import AbstractComponent


class AbstractSaleService(AbstractComponent):
_inherit = "shopinvader.abstract.sale.service"

def _is_item(self, line):
if line.parent_id:
return False
else:
return super()._is_item(line)

def _prepare_option(self, data, option):
res = {
"name": option.name,
"qty": option.option_unit_qty,
"amount": {
"price": option.price_unit,
"untaxed": option.price_subtotal,
"tax": option.price_tax,
"total": option.price_total,
"total_without_discount": option.price_total_no_discount,
},
"discount": {"rate": option.discount, "value": option.discount_total},
}
for product_option in data["product"]["options"]:
if product_option["id"] == option.product_option_id.id:
res["option"] = product_option
return res

def _convert_one_line(self, line):
res = super()._convert_one_line(line)
if not line.parent_id:
# avoid adding this field when _convert_one_line is call on children line
# like in shopinvader_sale_configurable_variant
res["options"] = [
self._prepare_option(res, option) for option in line.option_ids
]
res["amount"].update(
{
"price_config_total": line.price_config_total,
"price_config_untaxed": line.price_config_subtotal,
}
)
return res
76 changes: 76 additions & 0 deletions shopinvader_sale_configurator_option/services/cart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Copyright 2021 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import _
from odoo.exceptions import UserError

from odoo.addons.base_rest.components.service import to_int
from odoo.addons.component.core import Component


class CartService(Component):
_inherit = "shopinvader.cart.service"

def _get_vals_for_product_option(self, product_option):
return {"product_id": product_option.product_id.id}

def _prepare_cart_option(self, params_option):
opt = self.env["product.configurator.option"].browse(params_option["option_id"])
res = self._get_vals_for_product_option(opt)
res["option_unit_qty"] = params_option.get("qty", 1)
return res

def _prepare_cart_item(self, params, cart):
res = super()._prepare_cart_item(params, cart)
res["option_ids"] = []
for op in params.get("options", []):
res["option_ids"].append((0, 0, self._prepare_cart_option(op)))
return res

def _get_options_schema(self):
return {
"options": {
"type": "list",
"schema": {
"type": "dict",
"schema": {
"option_id": {"coerce": to_int, "required": True},
"qty": {"coerce": to_int},
},
},
}
}

def _validator_add_item(self):
res = super()._validator_add_item()
res.update(self._get_options_schema())
return res

def _validator_update_item(self):
res = super()._validator_update_item()
res.update(self._get_options_schema())
return res

def _check_existing_cart_item(self, cart, params):
if "options" in params:
return False
else:
lines = super()._check_existing_cart_item(cart, params)
if lines:
return lines.filtered(lambda l: not l.is_configurable_opt)
else:
return lines

def _upgrade_cart_item_quantity_vals(self, item, params, action="replace"):
vals = super()._upgrade_cart_item_quantity_vals(item, params, action=action)
assert action in ("sum", "replace")
if "options" in params:
if action == "replace":
item.option_ids.unlink()
vals["option_ids"] = [
(0, 0, self._prepare_cart_option(op)) for op in params["options"]
]
else:
raise UserError(_("sum action Not supported with options"))
return vals
1 change: 1 addition & 0 deletions shopinvader_sale_configurator_option/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_cart
Loading