This commit is contained in:
2026-03-22 17:03:58 +01:00
parent 7dba25c6d7
commit dfdaf7a1cd
6 changed files with 106 additions and 9 deletions

View File

@@ -175,6 +175,7 @@ def register():
purchase.AssayUnit,
purchase.PayableRule,
purchase.PenaltyRule,
purchase.PenaltyRuleTier,
purchase.ConcentrateTerm,
backtoback.Backtoback,
dimension.AnalyticDimension,

View File

@@ -778,23 +778,83 @@ class PayableRule(ModelSQL, ModelView):
('grade_minus', 'Grade minus deduction'),
('min_of_both', 'Min(% of grade, grade - deduction)'),
], "Method")
min_payable = fields.Numeric("Floor (min payable)") # ex: le "min -1" du Cu
min_payable = fields.Numeric("Floor (min payable)")
def compute_payable_quantity(self, grade):
"""
Retourne la quantité payable dans l'unité du grade.
grade : Decimal (ex: Decimal('26.862'))
"""
grade = Decimal(str(grade))
if self.payable_method == 'percent':
result = grade * self.payable_percent / Decimal(100)
elif self.payable_method == 'grade_minus':
result = grade - self.deduction_value
elif self.payable_method == 'min_of_both':
by_percent = grade * self.payable_percent / Decimal(100)
by_deduction = grade - self.deduction_value
result = min(by_percent, by_deduction)
if self.min_payable is not None:
result = max(result, self.min_payable)
return result
class PenaltyRuleTier(ModelSQL, ModelView):
"Penalty Rule Tier"
__name__ = 'penalty.rule.tier'
rule = fields.Many2One('penalty.rule', "Rule", ondelete='CASCADE')
threshold_from = fields.Numeric("From")
threshold_to = fields.Numeric("To") # None = pas de plafond
threshold_unit = fields.Many2One('assay.unit', "Unit")
deduction_per_unit = fields.Numeric("Deduction / unit")
penalty_value = fields.Numeric("Penalty Value (USD/DMT)")
def compute_tier_penalty(self, grade, dry_weight_dmt):
"""
Retourne la pénalité USD pour ce palier uniquement.
grade : Decimal teneur brute de l'élément
dry_weight_dmt: Decimal poids sec en DMT
"""
grade = Decimal(str(grade))
dry_weight_dmt = Decimal(str(dry_weight_dmt))
# Grade en dessous du seuil bas → ce palier ne s'applique pas
if grade <= self.threshold_from:
return Decimal(0)
# Excès au-dessus du seuil bas, plafonné au seuil haut si existant
excess_top = grade if self.threshold_to is None else min(grade, self.threshold_to)
excess = excess_top - self.threshold_from
# USD/DMT × DMT
return (excess * self.penalty_value * dry_weight_dmt).quantize(Decimal('0.01'))
class PenaltyRule(ModelSQL, ModelView):
"Penalty Rule"
__name__ = 'penalty.rule'
_rec_name = 'name'
name = fields.Char("Name")
element = fields.Many2One('assay.element', "Element")
tiers = fields.One2Many('penalty.rule.tier', 'rule', "Tiers")
threshold = fields.Numeric("Treshold")
threshold_unit = fields.Many2One('assay.unit',"Unit")
def compute_penalty(self, grade, dry_weight_dmt):
"""
Retourne la pénalité totale USD en cumulant tous les paliers traversés.
grade : Decimal teneur brute de l'élément
dry_weight_dmt: Decimal poids sec en DMT
"""
grade = Decimal(str(grade))
dry_weight_dmt = Decimal(str(dry_weight_dmt))
step = fields.Numeric("Step")
penalty_value = fields.Numeric("Penalty Value")
currency = fields.Many2One('currency.currency',"Curr")
unit = fields.Many2One('product.uom',"Unit")
total = Decimal(0)
for tier in self.tiers:
total += tier.compute_tier_penalty(grade, dry_weight_dmt)
return total.quantize(Decimal('0.01'))
class ConcentrateTerm(ModelSQL, ModelView):
"Concentrate Term"

View File

@@ -211,6 +211,22 @@ this repository contains the full copyright notices and license terms. -->
<field name="name">payable_rule_form</field>
</record>
<record model="ir.ui.view" id="penalty_rule_view_form">
<field name="model">penalty.rule</field>
<field name="type">form</field>
<field name="name">penalty_rule_form</field>
</record>
<record model="ir.ui.view" id="penalty_rule_view_tree">
<field name="model">penalty.rule</field>
<field name="type">tree</field>
<field name="name">penalty_rule_tree</field>
</record>
<record model="ir.ui.view" id="penalty_rule_tier_view_tree">
<field name="model">penalty.rule.tier</field>
<field name="type">tree</field>
<field name="name">penalty_rule_tier_tree</field>
</record>
<menuitem
name="Pnl Report"
parent="purchase_trade.menu_global_reporting"

View File

@@ -0,0 +1,8 @@
<form col="4">
<label name="name"/>
<field name="name"/>
<label name="element"/>
<field name="element"/>
<newline/>
<field name="tiers" colspan="4"/>
</form>

View File

@@ -0,0 +1,7 @@
<tree editable="1">
<field name="threshold_from"/>
<field name="threshold_to"/>
<field name="threshold_unit"/>
<field name="deduction_per_unit"/>
<field name="penalty_value"/>
</tree>

View File

@@ -0,0 +1,5 @@
<tree editable="1">
<field name="name"/>
<field name="element"/>
<field name="tiers"/>
</tree>