Calcular totales de ventas usando defaultdict

Total de Ventas Agrupado por Cliente y Mes

Total de Ventas Agrupado por Cliente y Mes

Imaginemos que queremos calcular el total de ventas agrupado por cliente y por mes. La idea es obtener los totales mensuales para cada cliente usando una estructura de datos que combine defaultdict​ y lambda​ para crear diccionarios anidados.

Usamos defaultdict​ para crear un diccionario de diccionarios donde cada cliente tiene un subdiccionario con el total de ventas para cada mes.

from collections import defaultdict
from odoo import models, fields, api
from datetime import datetime

class SalesReport(models.Model):
    _name = 'sales.report'
    _description = 'Sales Report Grouped by Client and Month'

    @api.model
    def calculate_sales_totals_by_client_and_month(self):
        # Crear una estructura de defaultdict donde cada cliente tiene un
        # defaultdict para meses
        sales_totals = defaultdict(lambda: defaultdict(float))

        # Obtener todas las facturas confirmadas
        invoices = self.env['account.move'].search([
            ('state', '=', 'posted'),
            ('move_type', '=', 'out_invoice')
        ])

        for invoice in invoices:
            client_id = invoice.partner_id.id
            amount = invoice.amount_total

            # Obtener el mes de la factura en formato Año-Mes
            invoice_date = fields.Date.from_string(invoice.invoice_date)
            month_key = invoice_date.strftime('%Y-%m')

            # Acumular ventas usando defaultdict con estructuras anidadas
            sales_totals[client_id][month_key] += amount

        # Salida mostrando las ventas totales por cliente y por mes
        for client_id, monthly_totals in sales_totals.items():
            print(f"\nCliente {client_id} - Totales Mensuales:")
            for month, total in monthly_totals.items():
                print(f"  Mes {month}: Total de Ventas: {total}")


Usamos defaultdict(lambda: defaultdict(float))​, que crea automáticamente un subdiccionario defaultdict(float)​ cada vez que un nuevo client_id​ se encuentra. Esto permite agrupar fácilmente los montos de ventas por mes sin tener que verificar manualmente la existencia de la clave del mes para cada cliente.

Cada invoice se procesa extrayendo el client_id​ y el amount_total​. Luego, se calcula el month_key en formato 'YYYY-MM'​ usando strftime​, que será la clave para los totales de ventas mensuales.

En la línea sales_totals[client_id][month_key] += amount​, el uso de defaultdict​ simplifica la operación, ya que los subdiccionarios de cada cliente se crean automáticamente y los valores de ventas se inicializan a 0.0.


Salida de ejemplo:

Cliente 1 - Totales Mensuales:
  Mes 2024-01: Total de Ventas: 5000.0
  Mes 2024-02: Total de Ventas: 3000.0

Cliente 2 - Totales Mensuales:
  Mes 2024-01: Total de Ventas: 2000.0
  Mes 2024-03: Total de Ventas: 4500.0


Beneficios del enfoque con defaultdict

Esta estructura es muy flexible y fácil de leer, y defaultdict​ permite omitir verificaciones repetitivas. Esto es útil en casos de informes de ventas o análisis de datos donde necesitamos obtener agregaciones en múltiples niveles, como cliente y mes.

Iniciar sesión para dejar un comentario