22.03.26
This commit is contained in:
@@ -175,6 +175,7 @@ def register():
|
||||
purchase.AssayUnit,
|
||||
purchase.PayableRule,
|
||||
purchase.PenaltyRule,
|
||||
purchase.PenaltyRuleTier,
|
||||
purchase.ConcentrateTerm,
|
||||
backtoback.Backtoback,
|
||||
dimension.AnalyticDimension,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
8
modules/purchase_trade/view/penalty_rule_form.xml
Normal file
8
modules/purchase_trade/view/penalty_rule_form.xml
Normal 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>
|
||||
7
modules/purchase_trade/view/penalty_rule_tier_tree.xml
Normal file
7
modules/purchase_trade/view/penalty_rule_tier_tree.xml
Normal 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>
|
||||
5
modules/purchase_trade/view/penalty_rule_tree.xml
Normal file
5
modules/purchase_trade/view/penalty_rule_tree.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<tree editable="1">
|
||||
<field name="name"/>
|
||||
<field name="element"/>
|
||||
<field name="tiers"/>
|
||||
</tree>
|
||||
Reference in New Issue
Block a user