from odoo import api, fields, models class AccPriorityGroup(models.Model): _name = "acc.priority.group" _description = "Groupe de clés de répartition" _rec_name = "display_name" _order = "sequence, id" # ------------------------------------------------------ # Fields declaration # ------------------------------------------------------ acc_operation_id = fields.Many2one("acc.operation", "Opération", required=True) sequence = fields.Integer(required=True, default=1) type_algo = fields.Selection( [ ("prorata", "Répartition au prorata"), ("quotepart", "Répartition par quote-part"), ], string="Répartition au sein du groupe", default="prorata", required=True, ) display_name = fields.Char( compute="_compute_display_name", store=True, readonly=True ) acc_priority_group_counter_ids = fields.One2many( comodel_name="acc.priority.group.counter", inverse_name="acc_priority_group_id", string="Affectation de compteur par groupes de priorité", required=True, ) counter_datas = fields.Json(compute="_compute_counter_datas") total_share = fields.Float(compute="_compute_total_share", store=True) # ------------------------------------------------------ # SQL Constraints # ------------------------------------------------------ # ------------------------------------------------------ # Default methods # ------------------------------------------------------ # ------------------------------------------------------ # Computed fields / Search Fields # ------------------------------------------------------ @api.depends("acc_priority_group_counter_ids", "acc_priority_group_counter_ids.acc_counter_share") def _compute_total_share(self): for record in self: record.total_share = sum( record.acc_priority_group_counter_ids.mapped("acc_counter_share") ) @api.depends("type_algo", "sequence") def _compute_display_name(self): for prio_group in self: prio_group.display_name = f"Priorité {str(prio_group.sequence)}" def _compute_counter_datas(self): for priority in self: priority.counter_datas = ( priority.acc_priority_group_counter_ids.acc_counter_id.mapped( lambda q: { "name": q.name, "partner": q.partner_id.name, "street": q.street, } ) ) # ------------------------------------------------------ # Onchange / Constraints # ------------------------------------------------------ # ------------------------------------------------------ # CRUD methods (ORM overrides) # ------------------------------------------------------ def write(self, vals): seq = vals.get("sequence") if seq is not None: vals["sequence"] = seq + 1 res = super().write(vals) return res @api.model def unlink(self, _id): self = self.env["acc.priority.group"].browse(_id) current_operation_id = self.env.context.get("active_id") existing_groups = self.env["acc.priority.group"].search( [("acc_operation_id", "=", current_operation_id), ("id", "!=", _id)] ) seq = 0 for group in existing_groups: group.sequence = seq seq += 1 return super().unlink() @api.model_create_multi def create(self, vals_list): """ si c est le premier groupe cree on y affecte tout les compteurs """ current_operation_id = self.env.context.get("active_id") existing_groups = self.env["acc.priority.group"].search( [ ("acc_operation_id", "=", current_operation_id), ] ) res = super().create(vals_list) if existing_groups: res.sequence = max(existing_groups.mapped("sequence")) return res counters_to_affect = self.env["acc.counter"].search( [ ("acc_operation_id", "=", res.acc_operation_id.id), ("type", "in", ["del", "del_inj"]), ] ) for counter in counters_to_affect: res.add_counter(counter_id=counter) return res def add_counter(self, counter_id): self.env["acc.priority.group.counter"].create( { "acc_priority_group_id": self.id, "acc_operation_id": self.acc_operation_id.id, "acc_counter_id": counter_id.id, "counter_street": counter_id.street, "counter_owner": counter_id.partner_id.name, } ) # ------------------------------------------------------ # Actions # ------------------------------------------------------ def compute(self, data=None): compute_algo = { "prorata": self._prorata, "quotepart": self._quotepart, } d = compute_algo[self.type_algo](data) return d def get_conso_sum(self, data_slot): conso_sum = 0.0 for counter in self.acc_priority_group_counter_ids.acc_counter_id.mapped( "name" ): conso_sum += data_slot.get("conso").get(counter, 0) return conso_sum def _prorata(self, data): for slot in data: remaining_prod = data.get(slot).get("prod_totale") total_conso = self.get_conso_sum(data.get(slot)) total_affecte = 0.0 if not data.get(slot).get("affect"): data[slot]["affect"] = {} for counter in self.acc_priority_group_counter_ids.acc_counter_id: # un compteur peut être présent dans deux groupes de priorité already_affected = data.get(slot).get("affect").get(counter, 0) conso_k = data.get(slot).get("conso").get(counter.name) # si le compteur rentre en cours de periode il figure dans le # groupe de repartition mais pas dans les données brutes conso_k = None # dans ce cas on affecte 0 if remaining_prod <= 0 or total_conso == 0 or conso_k is None: newly_affected = 0 sum_affected = already_affected + 0.0 else: part_a_affecter = (remaining_prod * conso_k) / total_conso sum_affected = min(conso_k, (already_affected + part_a_affecter)) newly_affected = sum_affected - already_affected data[slot]["affect"][counter] = sum_affected total_affecte += newly_affected # on met à jour la prod restant à affecter data[slot]["prod_totale"] = remaining_prod - total_affecte # possiblement on peut avoir affecté plus que la production total_affecte=496.00000000000006 return data def _quotepart(self, data): total_share = self.total_share for slot in data: remaining_prod = data.get(slot).get("prod_totale") total_affecte = 0.0 if not data.get(slot).get("affect"): data[slot]["affect"] = {} for priority_group_counter in self.acc_priority_group_counter_ids: counter = priority_group_counter.acc_counter_id already_affected = data.get(slot).get("affect").get(counter, 0) counter_share = priority_group_counter.acc_counter_share conso_k = data.get(slot).get("conso").get(counter.name) if ( remaining_prod <= 0 or total_share == 0 or conso_k is None or counter_share == 0 ): newly_affected = 0 sum_affected = already_affected + 0.0 else: part_a_affecter = (remaining_prod * counter_share) / total_share sum_affected = min(conso_k, (already_affected + part_a_affecter)) newly_affected = sum_affected - already_affected data[slot]["affect"][counter] = sum_affected total_affecte += newly_affected data[slot]["prod_totale"] = remaining_prod - total_affecte return data