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

[REF] Refactor: use configurable mixin for identical functionality on… #16

Open
wants to merge 5 commits into
base: 14.0-sale_configurator_option_report
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions sale_configurator_base/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from . import configurable_mixin
from . import sale
from . import ir_ui_view
from . import account_move_line
59 changes: 53 additions & 6 deletions sale_configurator_base/models/account_move_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,59 @@
from odoo import api, fields, models


class AccountMoveLine(models.Model):
_inherit = "account.move.line"
class AccountMove(models.Model):
_name = "account.move"
_inherit = ["configurable.mixin", "account.move"]

@property
def _lines_name(self):
return "line_ids"

@api.depends("line_ids")
def _onchange_children_sequence(self):
super()._onchange_children_sequence()

@api.depends("sale_line_ids")
def _compute_has_parent(self):
def _rebuild_parent_configuration_from_sale(self):
for rec in self:
rec.has_parent = any([line.parent_id for line in rec.sale_line_ids])
lines = rec.line_ids
mapping_sale_line_to_invoice_line = {
line.sale_line_ids.id: line.id for line in lines
}
for line in lines:
sale_line_parent = line.sale_line_ids.parent_id
if sale_line_parent:
line.parent_id = mapping_sale_line_to_invoice_line[
sale_line_parent.id
]


class AccountMoveLine(models.Model):
_name = "account.move.line"
_inherit = ["account.move.line", "configurable.line.mixin"]

parent_id = fields.Many2one(
"account.move.line", "Parent Line", ondelete="cascade", index=True
)
child_ids = fields.One2many("account.move.line", "parent_id", "Children Lines")

@api.depends(
"price_subtotal",
"price_total",
"child_ids.price_subtotal",
"child_ids.price_total",
"parent_id",
)
def _compute_config_amount(self):
return super()._compute_config_amount()

@api.depends("product_id")
def _compute_is_configurable(self):
return super()._compute_is_configurable()

@api.depends("price_unit", "child_ids")
def _compute_report_line_is_empty_parent(self):
return super()._compute_report_line_is_empty_parent()

has_parent = fields.Boolean(compute="_compute_has_parent", store=True)
@property
def _parent_container_name(self):
return "move_id"
178 changes: 178 additions & 0 deletions sale_configurator_base/models/configurable_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# Copyright 2021 Akretion (http://www.akretion.com).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import api, fields, models
from odoo.tools import float_compare


class ConfigurableMixin(models.AbstractModel):
"""
Justification for this model is that we want identical functionality
on sale orders and invoices. We can't implement everything out of the
box, so some re-implementing is necessary when inheriting the mixin.
To implement, define:
- @api.depends functions
- _lines_name property
"""

_name = "configurable.mixin"
_description = "Configurable Mixin"

@property
def _lines_name(self):
raise NotImplementedError

@property
def _lines(self):
return getattr(self, self._lines_name)

def sync_sequence(self):
for record in self:
done = []
lines = record._lines.sorted("sequence")
for line in lines:
if not line.parent_id:
line.sequence = len(done)
done.append(line)
line._sort_children_line(done)

@api.model_create_multi
def create(self, vals_list):
records = super().create(vals_list)
records.sync_sequence()
return records

def write(self, vals):
super().write(vals)
if self._lines_name in vals:
self.sync_sequence()
return True

# @api.depends("lines")
def _onchange_children_sequence(self):
"""Implement using @api.depends
<sale.order/account.move>.line"""
self.sync_sequence()


class ConfigurableLineMixin(models.AbstractModel):
"""
To implement, define:
- parent_id
- child_ids
- _parent_container property
- @api.depends functions
TODO cleanup???:
- reimplement price_config_subtotal and total as fields.Monetary
"""

_name = "configurable.line.mixin"
_description = "Configurable Line Mixin"

child_type = fields.Selection([])
# Monetary fields require a currency, so reimplement it
# TODO do something more elegant
price_config_subtotal = fields.Float(
compute="_compute_config_amount",
string="Config Subtotal",
readonly=True,
store=True,
)
price_config_total = fields.Float(
compute="_compute_config_amount",
string="Config Total",
readonly=True,
store=True,
)

is_configurable = fields.Boolean(
"Line is a configurable Product ?",
compute="_compute_is_configurable",
)
report_line_is_empty_parent = fields.Boolean(
compute="_compute_report_line_is_empty_parent",
help="Technical field used in the report to hide subtotals"
" and taxes in case a parent line (with children lines) "
"has no price by itself",
)

@property
def _parent_container(self):
return getattr(self, self._parent_container_name)

@property
def _parent_container_name(self):
raise NotImplementedError

def _get_child_type_sort(self):
return []

def _sort_children_line(self, done):
types = self._get_child_type_sort()
types.sort()
for _position, child_type in types:
for line in self.child_ids.sorted("sequence"):
if line.child_type == child_type:
line.sequence = len(done)
done.append(line)

# @api.depends("price_unit", "child_ids")
def _compute_report_line_is_empty_parent(self):
for rec in self:
rec.report_line_is_empty_parent = False
price_unit_like_zero = (
float_compare(rec.price_unit, 0.00, precision_digits=2) == 0
)
if rec.child_ids and price_unit_like_zero:
rec.report_line_is_empty_parent = True

# @api.depends("product_id")
def _compute_is_configurable(self):
for record in self:
record.is_configurable = record._is_line_configurable()

def _is_line_configurable(self):
raise NotImplementedError

# @api.depends(
# "price_subtotal",
# "price_total",
# "child_ids.price_subtotal",
# "child_ids.price_total",
# "parent_id",
# )
def _compute_config_amount(self):
"""
Compute the config amounts of the line.
Implement using @api.depends:
- price_subtotal
- price_total
- child_ids.price_subtotal
- child_ids.price_total
- parent_id
"""
for line in self:
line.update(line._get_price_config())

def _get_price_config(self):
self.ensure_one()
if self.parent_id:
return {
"price_config_subtotal": 0,
"price_config_total": 0,
}
else:
return {
"price_config_subtotal": self.price_subtotal
+ sum(self.child_ids.mapped("price_subtotal")),
"price_config_total": self.price_total
+ sum(self.child_ids.mapped("price_total")),
}

@api.model_create_multi
def create(self, vals_list):
parent_name = self._parent_container_name
for vals in vals_list:
if vals.get("parent_id") and parent_name not in vals:
vals[parent_name] = self.browse(vals["parent_id"])._parent_container.id
return super().create(vals_list)
Loading