Skip to content

Commit fb2242d

Browse files
committed
[REF] engenere_partner_sales_info: split main method
1 parent 54d7c44 commit fb2242d

File tree

1 file changed

+171
-148
lines changed

1 file changed

+171
-148
lines changed

engenere_partner_sales_info/models/res_partner.py

Lines changed: 171 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -123,176 +123,199 @@ def _compute_analysis_message(self):
123123
for partner in self:
124124
partner.analysis_message = message
125125

126-
def _compute_sales_info(self):
127-
"""Compute stats for sale orders and invoices."""
126+
def _get_analysis_months(self):
127+
"""Retorna o número de meses configurados para análise."""
128128
config_param = self.env["ir.config_parameter"].sudo()
129-
months = config_param.get_param(
130-
"engenere_partner_sales_info.default_analysis_months", 24
129+
return int(
130+
config_param.get_param(
131+
"engenere_partner_sales_info.default_analysis_months", 24
132+
)
131133
)
132-
analysis_months = int(months)
133-
start_date = fields.Date.context_today(self) - relativedelta(
134-
months=analysis_months
134+
135+
def _get_start_date(self, analysis_months):
136+
"""Calcula a data de início com base nos meses de análise."""
137+
return fields.Date.context_today(self) - relativedelta(months=analysis_months)
138+
139+
def _group_records_by_partner(self, records):
140+
"""Agrupa registros por partner_id."""
141+
grouped = defaultdict(list)
142+
for record in records:
143+
grouped[record.partner_id.id].append(record)
144+
return grouped
145+
146+
def _compute_record_stats(self, records, date_extractor, amount_extractor):
147+
"""Calcula estatísticas comuns para uma lista de registros."""
148+
if not records:
149+
return None
150+
151+
sorted_records = sorted(records, key=lambda r: date_extractor(r))
152+
dates = [date_extractor(r) for r in sorted_records]
153+
amounts = [amount_extractor(r) for r in sorted_records]
154+
count = len(sorted_records)
155+
total = sum(amounts)
156+
average = total / count if count else 0
157+
158+
avg_no_outliers = average
159+
if count >= 3:
160+
mean = average
161+
variance = sum((x - mean) ** 2 for x in amounts) / count
162+
std_dev = math.sqrt(variance)
163+
filtered = [
164+
x
165+
for x in amounts
166+
if (mean - 1.5 * std_dev) <= x <= (mean + 1.5 * std_dev)
167+
]
168+
avg_no_outliers = sum(filtered) / len(filtered) if filtered else mean
169+
170+
avg_time_between = 0.0
171+
if count >= 2:
172+
total_days = sum((dates[i] - dates[i - 1]).days for i in range(1, count))
173+
avg_time_between = total_days / (count - 1)
174+
175+
today = fields.Date.context_today(self)
176+
return {
177+
"last_record": sorted_records[-1],
178+
"last_date": dates[-1] if dates else None,
179+
"count": count,
180+
"total": total,
181+
"average": average,
182+
"avg_no_outliers": avg_no_outliers,
183+
"avg_time_between": avg_time_between,
184+
"days_since_last": (today - dates[-1]).days if dates else 0,
185+
}
186+
187+
def _update_sales_fields(self, stats):
188+
"""Atualiza campos de vendas com base nas estatísticas."""
189+
self.update(
190+
{
191+
"last_order_id": stats["last_record"].id if stats else False,
192+
"last_order_date": stats["last_date"],
193+
"last_order_status": stats["last_record"].state if stats else False,
194+
"order_count": stats["count"] or 0,
195+
"total_ordered": stats["total"] or 0,
196+
"average_ordered": stats["average"] or 0,
197+
"average_ordered_no_discrepancies": stats["avg_no_outliers"] or 0,
198+
"average_time_between_orders": stats["avg_time_between"] or 0,
199+
"days_since_last_order": stats["days_since_last"] or 0,
200+
}
135201
)
136-
partners = self.filtered(lambda p: p.customer_rank > 0)
137-
partner_ids = partners.ids
138202

139-
# Sale Orders
203+
def _update_invoice_fields(self, stats):
204+
"""Atualiza campos de faturas com base nas estatísticas."""
205+
self.update(
206+
{
207+
"last_invoice_id": stats["last_record"].id if stats else False,
208+
"last_invoice_date": stats["last_date"],
209+
"invoice_count": stats["count"] or 0,
210+
"total_invoiced": stats["total"] or 0,
211+
"average_invoiced": stats["average"] or 0,
212+
"average_invoiced_no_discrepancies": stats["avg_no_outliers"] or 0,
213+
"average_time_between_invoices": stats["avg_time_between"] or 0,
214+
"days_since_last_invoice": stats["days_since_last"] or 0,
215+
}
216+
)
217+
218+
def _reset_sales_fields(self):
219+
"""Reseta campos relacionados a vendas."""
220+
self.update(
221+
{
222+
"last_order_id": False,
223+
"last_order_date": False,
224+
"last_order_status": False,
225+
"order_count": 0,
226+
"total_ordered": 0,
227+
"average_ordered": 0,
228+
"average_ordered_no_discrepancies": 0,
229+
"average_time_between_orders": 0,
230+
"days_since_last_order": 0,
231+
}
232+
)
233+
234+
def _reset_invoice_fields(self):
235+
"""Reseta campos relacionados a faturas."""
236+
self.update(
237+
{
238+
"last_invoice_id": False,
239+
"last_invoice_date": False,
240+
"invoice_count": 0,
241+
"total_invoiced": 0,
242+
"average_invoiced": 0,
243+
"average_invoiced_no_discrepancies": 0,
244+
"average_time_between_invoices": 0,
245+
"days_since_last_invoice": 0,
246+
}
247+
)
248+
249+
def _compute_sales_info(self):
250+
"""Calcula principais métricas de vendas e faturas."""
251+
analysis_months = self._get_analysis_months()
252+
start_date = self._get_start_date(analysis_months)
253+
customer_partners = self.filtered(lambda p: p.customer_rank > 0)
254+
255+
# Processar pedidos de venda
140256
sale_orders = self.env["sale.order"].search(
141257
[
142-
("partner_id", "in", partner_ids),
258+
("partner_id", "in", customer_partners.ids),
143259
("state", "!=", "cancel"),
144260
("date_order", ">=", start_date),
145261
]
146262
)
147-
from_so = defaultdict(list)
148-
for so in sale_orders:
149-
from_so[so.partner_id.id].append(so)
263+
sales_group = self._group_records_by_partner(sale_orders)
150264

151-
# Invoices
265+
# Processar faturas
152266
invoices = self.env["account.move"].search(
153267
[
154-
("partner_id", "in", partner_ids),
268+
("partner_id", "in", customer_partners.ids),
155269
("move_type", "=", "out_invoice"),
156270
("state", "=", "posted"),
157271
("reversed_entry_id", "=", False),
158272
("invoice_date", ">=", start_date),
159273
]
160274
)
161-
from_inv = defaultdict(list)
162-
for inv in invoices:
163-
from_inv[inv.partner_id.id].append(inv)
275+
invoices_group = self._group_records_by_partner(invoices)
164276

165-
for partner in partners:
166-
so_list = sorted(from_so.get(partner.id, []), key=lambda x: x.date_order)
167-
if so_list:
168-
partner.last_order_id = so_list[-1].id
169-
partner.last_order_date = so_list[-1].date_order.date()
170-
partner.last_order_status = so_list[-1].state
171-
count_so = len(so_list)
172-
total_so = sum(o.amount_total for o in so_list)
173-
avg_so = total_so / count_so if count_so else 0
174-
if count_so >= 3:
175-
amounts_so = [o.amount_total for o in so_list]
176-
mean_so = total_so / count_so
177-
var_so = sum((x - mean_so) ** 2 for x in amounts_so) / count_so
178-
std_dev_so = math.sqrt(var_so)
179-
lower_so = mean_so - 1.5 * std_dev_so
180-
upper_so = mean_so + 1.5 * std_dev_so
181-
filtered_so = [x for x in amounts_so if lower_so <= x <= upper_so]
182-
avg_no_disc_so = (
183-
(sum(filtered_so) / len(filtered_so))
184-
if filtered_so
185-
else mean_so
186-
)
187-
else:
188-
avg_no_disc_so = avg_so
189-
190-
avg_time_so = 0
191-
if count_so >= 2:
192-
dates_so = [o.date_order.date() for o in so_list]
193-
total_days_so = 0
194-
for idx in range(1, len(dates_so)):
195-
total_days_so += (dates_so[idx] - dates_so[idx - 1]).days
196-
avg_time_so = total_days_so / (count_so - 1)
197-
198-
partner.order_count = count_so
199-
partner.total_ordered = total_so
200-
partner.average_ordered = avg_so
201-
partner.average_ordered_no_discrepancies = avg_no_disc_so
202-
partner.average_time_between_orders = avg_time_so
203-
today = fields.Date.context_today(self)
204-
partner.days_since_last_order = (
205-
(today - so_list[-1].date_order.date()).days
206-
if so_list[-1].date_order
207-
else 0
208-
)
277+
for partner in customer_partners:
278+
# Processar vendas
279+
so_stats = self._compute_record_stats(
280+
sales_group.get(partner.id, []),
281+
lambda r: r.date_order.date(),
282+
lambda r: r.amount_total,
283+
)
284+
if so_stats:
285+
partner._update_sales_fields(so_stats)
209286
else:
210-
partner.last_order_id = False
211-
partner.last_order_date = False
212-
partner.last_order_status = False
213-
partner.order_count = 0
214-
partner.total_ordered = 0
215-
partner.average_ordered = 0
216-
partner.average_ordered_no_discrepancies = 0
217-
partner.average_time_between_orders = 0
218-
partner.days_since_last_order = 0
287+
partner._reset_sales_fields()
219288

220-
inv_list = sorted(
221-
from_inv.get(partner.id, []), key=lambda x: x.invoice_date
289+
# Processar faturas
290+
inv_stats = self._compute_record_stats(
291+
invoices_group.get(partner.id, []),
292+
lambda r: r.invoice_date,
293+
lambda r: r.amount_total,
222294
)
223-
if inv_list:
224-
partner.last_invoice_id = inv_list[-1].id
225-
partner.last_invoice_date = inv_list[-1].invoice_date
226-
count_inv = len(inv_list)
227-
total_inv = sum(i.amount_total for i in inv_list)
228-
avg_inv = total_inv / count_inv if count_inv else 0
229-
if count_inv >= 3:
230-
amounts_inv = [i.amount_total for i in inv_list]
231-
mean_inv = total_inv / count_inv
232-
var_inv = sum((x - mean_inv) ** 2 for x in amounts_inv) / count_inv
233-
std_dev_inv = math.sqrt(var_inv)
234-
lower_inv = mean_inv - 1.5 * std_dev_inv
235-
upper_inv = mean_inv + 1.5 * std_dev_inv
236-
filtered_inv = [
237-
x for x in amounts_inv if lower_inv <= x <= upper_inv
238-
]
239-
avg_no_disc_inv = (
240-
(sum(filtered_inv) / len(filtered_inv))
241-
if filtered_inv
242-
else mean_inv
243-
)
244-
else:
245-
avg_no_disc_inv = avg_inv
246-
247-
avg_time_inv = 0
248-
if count_inv >= 2:
249-
dates_inv = [i.invoice_date for i in inv_list]
250-
total_days_inv = 0
251-
for i in range(1, len(dates_inv)):
252-
total_days_inv += (dates_inv[i] - dates_inv[i - 1]).days
253-
avg_time_inv = total_days_inv / (count_inv - 1)
254-
255-
partner.invoice_count = count_inv
256-
partner.total_invoiced = total_inv
257-
partner.average_invoiced = avg_inv
258-
partner.average_invoiced_no_discrepancies = avg_no_disc_inv
259-
partner.average_time_between_invoices = avg_time_inv
260-
today = fields.Date.context_today(self)
261-
partner.days_since_last_invoice = (
262-
(today - partner.last_invoice_date).days
263-
if partner.last_invoice_date
264-
else 0
265-
)
295+
if inv_stats:
296+
partner._update_invoice_fields(inv_stats)
266297
else:
267-
partner.last_invoice_id = False
268-
partner.last_invoice_date = False
269-
partner.invoice_count = 0
270-
partner.total_invoiced = 0
271-
partner.average_invoiced = 0
272-
partner.average_invoiced_no_discrepancies = 0
273-
partner.average_time_between_invoices = 0
274-
partner.days_since_last_invoice = 0
298+
partner._reset_invoice_fields()
275299

276-
# Reset stats for non-customers
277-
for partner in self - partners:
278-
partner.update(
279-
{
280-
"last_order_id": False,
281-
"last_order_date": False,
282-
"last_order_status": False,
283-
"order_count": 0,
284-
"total_ordered": 0,
285-
"average_ordered": 0,
286-
"average_ordered_no_discrepancies": 0,
287-
"average_time_between_orders": 0,
288-
"days_since_last_order": 0,
289-
"last_invoice_date": False,
290-
"invoice_count": 0,
291-
"total_invoiced": 0,
292-
"average_invoiced": 0,
293-
"average_invoiced_no_discrepancies": 0,
294-
"average_time_between_invoices": 0,
295-
"last_invoice_id": False,
296-
"days_since_last_invoice": 0,
297-
}
298-
)
300+
# Resetar parceiros que não são clientes
301+
(self - customer_partners).write(
302+
{
303+
"last_order_id": False,
304+
"last_order_date": False,
305+
"last_order_status": False,
306+
"order_count": 0,
307+
"total_ordered": 0,
308+
"average_ordered": 0,
309+
"average_ordered_no_discrepancies": 0,
310+
"average_time_between_orders": 0,
311+
"days_since_last_order": 0,
312+
"last_invoice_date": False,
313+
"invoice_count": 0,
314+
"total_invoiced": 0,
315+
"average_invoiced": 0,
316+
"average_invoiced_no_discrepancies": 0,
317+
"average_time_between_invoices": 0,
318+
"last_invoice_id": False,
319+
"days_since_last_invoice": 0,
320+
}
321+
)

0 commit comments

Comments
 (0)