Skip to content

Commit

Permalink
[IMP] sustainability: Allow supplerinfo to be used for purchase order
Browse files Browse the repository at this point in the history
  • Loading branch information
BonnetAdam committed Jan 17, 2025
1 parent 87ea8d0 commit f0e2310
Show file tree
Hide file tree
Showing 11 changed files with 250 additions and 46 deletions.
27 changes: 16 additions & 11 deletions sustainability/models/carbon_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,24 @@


# Todo: make this extendable from sub modules
CARBON_MODELS = [
"carbon.factor",
"product.category",
"product.product",
"product.template",
"res.partner",
"res.company",
"res.country",
]


class CarbonMixin(models.AbstractModel):
_name = "carbon.mixin"
_description = "A mixin used to add carbon values on any model"
_carbon_types = ["in", "out"]
_fallback_records = []

@classmethod
def _CARBON_MODELS(cls):
return [
"carbon.factor",
"product.category",
"product.product",
"product.template",
"res.partner",
"res.company",
"res.country",
]

@api.constrains("carbon_in_use_distribution", "carbon_in_distribution_line_ids")
def _check_carbon_in_distribution(self):
Expand All @@ -75,7 +77,7 @@ def _get_available_carbon_compute_methods(self) -> list[tuple[str, str]]:
@api.model
def _selection_fallback_model(self):
return [
(x, _(self.env[x]._description)) for x in CARBON_MODELS if x in self.env
(x, _(self.env[x]._description)) for x in self._CARBON_MODELS() if x in self.env
]

def get_allowed_factors(self):
Expand Down Expand Up @@ -270,6 +272,9 @@ def _search_fallback_record(self, carbon_type: str) -> list[Any] | None:
self.ensure_one()
fallback_path = []
for rec in self._build_fallback_records_list(carbon_type):
# skip unsaved records with temporary IDs
if not rec.id or isinstance(rec.id, str) and rec.id.startswith("NewId"):
continue
fallback_path.append(rec)
if rec.has_valid_carbon_value(carbon_type):
return fallback_path
Expand Down
2 changes: 1 addition & 1 deletion sustainability_purchase/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# Data
"data/ir_cron.xml",
# Views
# "views/product_supplierinfo.xml",
"views/product_supplierinfo.xml",
"views/carbon_factor.xml",
"views/carbon_line_origin.xml",
"views/purchase_order.xml",
Expand Down
4 changes: 3 additions & 1 deletion sustainability_purchase/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# from . import product_supplierinfo
from . import product_supplierinfo
from . import account_move_line
from . import product_template
from . import purchase_order
from . import purchase_order_line
from . import res_partner
from . import carbon_factor
from . import carbon_line_origin
from . import carbon_line_mixin
from . import carbon_mixin
9 changes: 9 additions & 0 deletions sustainability_purchase/models/carbon_line_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from odoo import models, _

class CarbonLineMixin(models.AbstractModel):
_inherit = 'carbon.line.mixin'

def _get_computation_levels_mapping(self):
mapping = super()._get_computation_levels_mapping()
mapping['product.supplierinfo'] = _("Product supplier")
return mapping
9 changes: 9 additions & 0 deletions sustainability_purchase/models/carbon_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from odoo import models, _

class CarbonMixin(models.AbstractModel):
_inherit = 'carbon.mixin'

def _CARBON_MODELS(self):
res = super()._CARBON_MODELS()
res.append('product.supplierinfo')
return res
30 changes: 21 additions & 9 deletions sustainability_purchase/models/product_supplierinfo.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
from odoo import models
from odoo import api, models


class ProductSupplierInfo(models.Model):
_name = "product.supplierinfo"
_inherit = ["product.supplierinfo", "carbon.mixin"]

def _get_carbon_in_fallback_records(self) -> list:
self.ensure_one()
res = super()._get_carbon_in_fallback_records()
return res + [self.partner_id]
def _update_carbon_in_fields(self, vals):
"""
Helper method to update carbon_in_is_manual and carbon_in_mode based on carbon_in_factor_id.
"""
if vals.get("carbon_in_factor_id"):
vals["carbon_in_is_manual"] = True
vals["carbon_in_mode"] = "manual"
else:
vals["carbon_in_is_manual"] = False
vals["carbon_in_mode"] = "auto"
return vals

def _get_carbon_out_fallback_records(self) -> list:
self.ensure_one()
res = super()._get_carbon_out_fallback_records()
return res + [self.partner_id]
@api.model
def create(self, vals):
vals = self._update_carbon_in_fields(vals)
return super().create(vals)

def write(self, vals):
if "carbon_in_factor_id" in vals:
vals = self._update_carbon_in_fields(vals)
return super().write(vals)
41 changes: 39 additions & 2 deletions sustainability_purchase/models/purchase_order_line.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
from typing import Any

from odoo import api, models
from odoo import api, fields, models


class PurchaseOrderLine(models.Model):
_name = "purchase.order.line"
_inherit = ["purchase.order.line", "carbon.line.mixin"]

carbon_seller_id = fields.Many2one(
comodel_name="product.supplierinfo",
string="Supplier Info",
compute="_compute_carbon_seller_id",
)

def _compute_carbon_seller_id(self):
"""
Compute the carbon_seller_id field.
Note that since the same seller can be added multiple times to the same product,
we need to filter the sellers by the partner_id of the order.
If there are multiple matches,
we'll take the most recent one.
"""

for line in self:
seller = line.product_id.seller_ids.filtered(
lambda s: s.partner_id == line.order_id.partner_id # noqa: B023
)
line.carbon_seller_id = seller[-1] if len(seller) > 1 else seller

def _prepare_account_move_line(self, move=False):
res = super()._prepare_account_move_line(move)
if self.carbon_is_locked:
Expand All @@ -32,6 +53,12 @@ def _prepare_account_move_line(self, move=False):
# --------------------------------------------

@api.depends(
# Seller
"product_id.seller_ids",
"product_id.seller_ids.carbon_in_factor_id",
"order_id.partner_id",
"partner_id",
# Product
"product_id.carbon_in_factor_id",
"product_qty",
"product_uom",
Expand All @@ -54,7 +81,7 @@ def _get_state_field_name(self) -> str:

@api.model
def _get_carbon_compute_possible_fields(self) -> list[str]:
return ["product_id"]
return ["carbon_seller_id", "product_id"]

def _get_lines_to_compute_domain(self, force_compute: list[str]):
domain = super()._get_lines_to_compute_domain(force_compute)
Expand Down Expand Up @@ -90,6 +117,16 @@ def get_product_id_carbon_compute_values(self) -> dict:
self.ensure_one()
return {"quantity": self.product_qty, "from_uom_id": self.product_uom}

def can_use_carbon_seller_id_carbon_value(self) -> bool:
self.ensure_one()
return bool(
self.carbon_seller_id
) and self.carbon_seller_id.can_compute_carbon_value("in")

def get_carbon_seller_id_carbon_compute_values(self) -> dict:
self.ensure_one()
return self.get_product_id_carbon_compute_values()

@api.model
def create(self, vals):
lines = super().create(vals)
Expand Down
1 change: 1 addition & 0 deletions sustainability_purchase/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_purchase
71 changes: 71 additions & 0 deletions sustainability_purchase/tests/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from odoo.addons.sustainability.tests.common import CarbonCommon

from odoo import Command


class CarbonPurchaseCommon(CarbonCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()

cls.partner_2 = cls.env["res.partner"].create(
{
"name": "Test Partner number 2",
"email": "[email protected]",
"phone": "+123456782",
"street": "123 Test Street 2",
"city": "Test City 2",
"country_id": cls.env.ref("base.us").id,
}
)

cls.kg_uom_id = cls.env.ref("uom.product_uom_kgm")
cls.product_template = cls.env["product.template"].create(
dict(
categ_id=cls.product_category.id,
name="Wooden Chair",
uom_id=cls.kg_uom_id.id,
uom_po_id=cls.kg_uom_id.id,
list_price=100,
seller_ids=[
Command.create(
dict(
currency_id=cls.currency_usd.id,
delay=1,
min_qty=1,
partner_id=cls.partner.id,
price=50,
carbon_in_factor_id=cls.carbon_factor_monetary.id,
)
),
Command.create(
dict(
currency_id=cls.currency_usd.id,
delay=4,
min_qty=1,
partner_id=cls.partner_2.id,
price=30,
carbon_in_factor_id=cls.carbon_factor_default_fallback.id,
)
),
],
)
)
cls.product_product = cls.env["product.product"].search(
[("product_tmpl_id", "=", cls.product_template.id)], limit=1
)
cls.purchase = cls.env["purchase.order"].create(
dict(
partner_id=cls.partner.id,
order_line=[
Command.create(
dict(
product_id=cls.product_product.id,
product_qty=1.0,
product_uom=cls.kg_uom_id.id,
price_unit=100.0,
)
),
],
)
)
18 changes: 18 additions & 0 deletions sustainability_purchase/tests/test_purchase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from odoo.addons.sustainability_purchase.tests.common import CarbonPurchaseCommon


class TestCarbonPurchase(CarbonPurchaseCommon):
def test_create_purchase_order(self):
# Check with the default values
self.purchase.action_recompute_carbon()
self.assertEqual(round(self.purchase.carbon_debt, 2), 2.38)

# Check with a different partner
self.purchase.partner_id = self.partner_2.id
self.purchase.action_recompute_carbon()
self.assertEqual(round(self.purchase.carbon_debt, 2), 952.4)

# Check with a different price
self.purchase.order_line[0].price_unit = 200
self.purchase.action_recompute_carbon()
self.assertEqual(round(self.purchase.carbon_debt, 2), 1904.8)
84 changes: 62 additions & 22 deletions sustainability_purchase/views/product_supplierinfo.xml
Original file line number Diff line number Diff line change
@@ -1,34 +1,74 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<record id="product_supplierinfo_tree_view2" model="ir.ui.view">
<field name="name">product_supplierinfo_tree_view2</field>
<record id="product_supplierinfo_form_view" model="ir.ui.view">
<field name="name">product_supplierinfo_form_view</field>
<field name="model">product.supplierinfo</field>
<field name="inherit_id" ref="product.product_supplierinfo_form_view" />
<field name="arch" type="xml">
<xpath expr="//group[@name='vendor']" position="after">
<group string="CO2 Settings" name="group_carbon_settings">
<field name="carbon_allowed_factor_ids" invisible="1" />
<group string="Purchases">
<label for="carbon_in_is_manual" string="Mode" />
<div class="gap-1 d-inline-flex ml-3">
<div
class="opacity-50 mr-2"
invisible="not carbon_in_is_manual"
>Undefined</div>
<div
style="font-weight: bold;"
invisible="carbon_in_is_manual"
>Undefined</div>

<field
name="carbon_in_is_manual"
nolabel="1"
widget="boolean_toggle"
class=""
style="margin-left: 8px;"
/>
<field name="carbon_in_mode" invisible="1" />

<div
class="opacity-50"
invisible="carbon_in_is_manual"
>Set</div>
<div
style="font-weight: bold;"
invisible="not carbon_in_is_manual"
>Set</div>
</div>
<field
name="carbon_in_fallback_reference"
widget="reference"
invisible="carbon_in_is_manual"
/>
<field
name="carbon_in_factor_id"
string="Emission Factor"
invisible="not carbon_in_is_manual"
required="carbon_in_is_manual"
/>
</group>
</group>
</xpath>
</field>
</record>

<record id="sustainability_product_supplierinfo_tree_view" model="ir.ui.view">
<field name="name">sustainability.product.supplierinfo.tree.view</field>
<field name="model">product.supplierinfo</field>
<field name="inherit_id" ref="purchase.product_supplierinfo_tree_view2" />
<field name="arch" type="xml">
<xpath expr="//tree" position="inside">
<field column_invisible="1" name="carbon_allowed_factor_ids" />
<field
name="carbon_in_value"
readonly="carbon_in_factor_id"
force_save="1"
/>
<button
type="object"
name="action_see_carbon_origin"
icon="fa-question-circle"
context="{'carbon_type': 'carbon_in'}"
title="See CO2e value origin"
readonly="carbon_in_factor_id"
/>
<button
type="object"
name="action_recompute_carbon"
icon="fa-refresh"
context="{'carbon_type': 'carbon_in'}"
title="Re-compute CO2e value"
readonly="carbon_in_factor_id"
name="carbon_in_factor_id"
string="Emission Factor"
optional="show"
widget="many2one"
/>
<field name="carbon_in_factor_id" string="Emission Factor" />
</xpath>
</field>
</record>
Expand Down

0 comments on commit f0e2310

Please sign in to comment.