En este post vamos a ver cómo calcular el total vendido en un período de fechas agrupado por mes, ya sea para mostrar un reporte o una gráfica de barras o líneas.
Básicamente veremos el algoritmo y ejemplos de código para sacar el desglose del total vendido por mes en el año o en cualquier período de tiempo, tomando los datos de una base de datos, agrupando, ordenando, sumando, etcétera.
No importa cuál motor de base de datos utilices ni el lenguaje de programación. Veremos el algoritmo para sumar el total vendido por mes.
Necesitamos consultar la suma del total o monto agrupado por mes ordenado por mes. Para esto necesitamos haber guardado antes:
Yo recomiendo guardar la fecha completa y luego obtener el mes, así podemos consultar esto por períodos, por ejemplo, el total vendido en un año pero agrupado por mes.
La consulta queda así:
select mes, sum(monto) from tabla group by mes;
Ahora veamos algunos ejemplos y cómo extraer el total de mes o presentarlo en el cliente.
Vamos a crear una tabla muy simple donde solo guardamos fecha y monto de la venta. En este caso no registramos la hora, solo la fecha. Creamos y llenamos la tabla:
CREATE TABLE ventas(
fecha varchar(255) NOT NULL,
monto decimal(9, 2) NOT NULL
);
INSERT INTO
ventas(fecha, monto)
VALUES
("2023-02-12", 25.9),
("2023-03-12", 11.52),
("2023-03-12", 69.5),
("2023-02-14", 524.22),
("2023-01-12", 77.7),
("2023-03-12", 25),
("2023-04-12", 875),
("2023-03-09", 32),
("2023-05-14", 857),
("2023-05-12", 5);
Para obtener la suma de lo que se ha vendido en el año pero agrupado por mes la consulta quedaría así:
SELECT
sum(monto) AS total,
MONTH(fecha) AS mes
FROM
ventas
WHERE
fecha >= "2023-01-01"
AND fecha <= "2023-12-31"
GROUP BY
mes
ORDER BY
mes;
Con la siguiente salida mostrando el número de mes y la sumatoria de los montos, o el total recaudado:
total mes
77.70 1
550.12 2
138.02 3
875.00 4
862.00 5
Obviamente tú puedes ordenar por mes o total, además de obtener también el año. Una vez que tienes el número de mes ya puedes obtener el nombre del mismo o hacer cualquier otra cosa.
Ese ejemplo que te mostré fue con MySQL, y a partir de aquí puedes usar cualquier lenguaje de programación para ejecutar esa consulta. Sigue leyendo para ver cómo mostrar una gráfica con esos datos.
Veamos un ejemplo con SQLite3. Yo he dividido los datos, por un lado tengo órdenes (es decir, órdenes o pedidos de un sistema de restaurantes) y por el otro tengo los pagos.
Primero veamos las órdenes que es en donde se guarda la fecha
:
id,id_usuario,id_mesa,fecha_solicitud
23,1,1,2023-02-25T12:37:07
24,1,1,2023-02-25T12:41:45
25,1,1,2023-02-26T09:48:45
26,1,1,2023-02-26T09:55:13
27,1,1,2023-02-26T10:09:12
28,1,1,2023-02-26T14:58:28
29,1,1,2023-01-26T15:05:04
30,1,1,2023-02-23T12:06:09
31,1,1,2023-02-02T12:24:08
32,1,1,2023-02-02T12:25:52
33,1,2,2023-01-30T12:36:50
34,1,1,2023-04-22T15:24:18
35,1,1,2023-01-30T21:01:00
36,1,1,2023-02-27T11:43:53
37,1,1,2023-01-31T18:47:06
38,1,2,2023-02-01T10:00:30
39,1,2,2023-02-01T10:01:27
40,1,2,2023-02-01T10:59:01
41,1,2,2023-02-01T11:00:55
42,1,1,2023-02-01T11:26:28
43,1,2,2023-02-01T11:36:43
44,1,1,2023-02-02T08:55:21
45,1,1,2023-02-02T09:30:38
46,1,1,2023-02-02T10:05:20
47,1,1,2023-02-02T10:05:36
48,1,1,2023-02-02T10:06:02
49,1,1,2023-02-02T17:52:08
50,1,2,2023-02-02T20:42:09
51,1,2,2023-02-02T21:27:46
52,1,2,2023-02-02T21:36:06
53,2,2,2023-02-02T21:39:00
54,2,1,2023-02-03T11:04:23
55,2,1,2023-02-03T11:40:04
56,2,2,2023-02-03T11:40:15
Ahora veamos los pagos:
id,id_orden,id_metodo_pago,monto
1,23,4,40.0
2,24,4,270.0
3,25,4,15.0
4,26,4,20.0
5,27,4,15.0
6,28,4,24.0
7,29,4,20.0
8,30,4,15.0
9,31,4,15.0
10,33,4,20.0
11,32,4,20.0
12,34,4,20.0
13,35,4,255.0
14,36,4,1254.0
15,38,4,20.0
16,39,5,15.0
17,40,4,15.0
18,41,4,40.0
19,37,4,15.0
20,43,4,20.0
21,42,4,2140.0
22,44,4,20.0
23,45,4,1500.0
24,46,6,2000.0
25,47,4,2015.0
26,48,5,15000.0
27,50,4,30.0
28,51,4,7500.0
29,52,4,300000.0
30,53,4,15.0
31,49,4,20.0
32,54,4,20.0
33,55,4,20.0
34,56,4,20.0
Los campos importantes aquí son monto
de la tabla pagos
, y fecha_solicitud
de la tabla ordenes
.
Como ya tengo la fecha, debo calcular el mes y eso se puede hacer en SQLite con strftime usando el formato %m
entonces mi consulta queda así:
SELECT
strftime("%m", ordenes.fecha_solicitud) AS mes,
coalesce(sum(pagos.monto), 0) AS total_vendido
FROM
pagos
INNER JOIN ordenes ON ordenes.id = pagos.id_orden
AND ordenes.fecha_solicitud >= "2023-01-01T00:00:00"
AND ordenes.fecha_solicitud <= "2023-12-31T23:59:59"
GROUP BY
mes
ORDER BY
mes
Por cierto, aquí uso coalesce en caso de que no haya ventas en ese mes, aunque sería poco probable porque si no hay ventas el mes ni siquiera aparecería; de igual manera lo dejo por buenas prácticas.
Estoy haciendo un inner join
porque son dos tablas. Ya te mostré anteriormente un ejemplo más simple.
Nota importante: en este caso estoy filtrando las fechas para que solo sean del año 2023. Si tú usas otro formato de fechas tienes que cambiar el rango. Yo uso ese formato porque me permite comparar las cadenas lexicográficamente y luego crear objetos de tipo Date
con JavaScript sin problemas.
Eso me dará una salida como la siguiente:
mes,total_vendido
01,310.0
02,332098.0
04,20.0
Ya tengo el número de mes y el total vendido en ese mes o mejor dicho; el mes y la suma de los totales de ese mes. Con esto ya podemos mostrar reportes y gráficas.
Te voy a enseñar un ejemplo específico que yo uso para mostrar el total vendido en un año agrupado por mes usando datos de SQL y chart.js.
Yo uso Golang en el servidor. Mi función queda así:
func obtenerTotalVendidoEnPeriodoAgrupadoPorMetodoDePago(fechaInicio, fechaFin string) ([]EtiquetaConTotal, error) {
diasConTotalVendido := []EtiquetaConTotal{}
bd, err := obtenerBD()
if err != nil {
return diasConTotalVendido, err
}
defer bd.Close()
filas, err := bd.Query(`SELECT
strftime("%m", ordenes.fecha_solicitud) AS mes,
coalesce(sum(pagos.monto), 0) AS total_vendido
FROM
pagos
INNER JOIN ordenes ON ordenes.id = pagos.id_orden
AND ordenes.fecha_solicitud >= ?
AND ordenes.fecha_solicitud <= ?
GROUP BY
mes
ORDER BY
mes`, fechaInicio, fechaFin)
if err != nil {
return diasConTotalVendido, err
}
var diaConTotalVendido EtiquetaConTotal
for filas.Next() {
err = filas.Scan(&diaConTotalVendido.Etiqueta, &diaConTotalVendido.Total)
if err != nil {
return diasConTotalVendido, err
}
diasConTotalVendido = append(diasConTotalVendido, diaConTotalVendido)
}
return diasConTotalVendido, nil
}
Luego expongo eso en una API creada con gorilla/mux y la consumo con JavaScript:
enrutador.HandleFunc("/total_vendido_agrupado_por_mes", func(w http.ResponseWriter, r *http.Request) {
responderHttpConFuncion(w, r, func() (interface{}, error) {
valores, err := validarParametrosGetDePeticionHttp(r, []string{"fechaInicio", "fechaFin"})
if err != nil {
return nil, err
}
fechaInicio, fechaFin := valores[0], valores[1]
return obtenerTotalVendidoEnPeriodoAgrupadoPorMes(fechaInicio, fechaFin)
})
}).Methods(http.MethodGet).Name("Escritorio: ver total vendido en período de fechas agrupado por mes")
Luego la obtengo en el lado del cliente y hago la gráfica convirtiendo el número de mes para que sea el label
, y el total para que sea la data
:
this.grafica = new Chart(document.querySelector("#graficaMes"), {
type: "line",
data: {
labels: diasConTotal.map((dato) => {
const fecha = new Date(fechaInicioMes);
fecha.setDate(dato.etiqueta);
return formateadorMes(fecha);
}),
datasets: [
{
label: "Recaudado",
data: diasConTotal.map((dato) => dato.total),
backgroundColor: ConstantesService.BACKGROUND_COLORS,
borderColor: ConstantesService.BORDER_COLORS,
borderWidth: 1,
},
],
},
options: GraficasService.obtenerOpciones(),
});
Con el siguiente resultado:
Espero que con estos ejemplos puedas crear tus propios reportes y gráficas.
Solo es cuestión de encontrar la consulta adecuada según tu motor de base de datos y luego mostrar esos datos de la manera que prefieras, ya sea en una tabla, en una gráfica de barras, circular o lo que tú prefieras.
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.