ayuda en consulta sql (generacion de informe)
resulta que tengo un software que genera variados informes de las ventas que este mismo realiza...
informe de ventas por cliente, ventas por vendedor, ventas anuales y ventas por producto.
en este ultimo me entrega datos erroneos, especificamente un valor mas alto al correspondiente.
les dejo el procedimiento almacenado que genera ese informe
estructura de las tablas
editado codigo comentado y resumido solo a boletas de venta
Pantallazos de Software
[img=http://img213.imageshack.us/img213/7987/informevendedor.th.png]
espero que me puedan ayudar... me he craneado bastante con esto y no se como arreglarlo..
saludos.. y gracias de antemano
resulta que tengo un software que genera variados informes de las ventas que este mismo realiza...
informe de ventas por cliente, ventas por vendedor, ventas anuales y ventas por producto.
en este ultimo me entrega datos erroneos, especificamente un valor mas alto al correspondiente.
les dejo el procedimiento almacenado que genera ese informe
estructura de las tablas
editado codigo comentado y resumido solo a boletas de venta
Código:
--sintaxis de tablas
--MAE = maestra (encabezado)
--DET = detalle
--ej. mae_boleta_Venta : guarda encabezados de las boletas de venta
--det_boleta_venta : guarda las lineas de detalle de las boletas de venta
--sintaxis para los campos de cada tabla
--InicialesDeNombreDeTabla_DescripcionCampo
--ej. mps_id_producto_servicio
--mps viene de la tabla Mae_Producto_Servicio
--sintaxis del procedimiento almacenado (sp_tipo_nombre)
--tipos de procedimientos almacenados
--sel : seleccion
--ins : insercion
--del : delete
-- he estado realizando las pruebas con los 2 primeros parametros en 0
-- y con distintas fechas de inicio y final
alter PROCEDURE [dbo].[sp_SEL_InfoVentasPorProducto]
@IdProducto numeric, --parametro recibido para filtrar por un producto en especifico, cuando no se filtra recibe un 0
@IdCategoria numeric, --parametro recibido para filtrar por una categoria de producto en especifico, cuando no se filtra recibe un 0
@FechasDesde datetime, --parametro recibido desde el formulario para filtrar las ventas (fecha inicio)
@FechasHasta datetime --parametro recibido desde el formulario para filtrar las ventas (fecha final)
AS
declare @MontoBoletas decimal (26,8) --variable que almacena el monto total neto de boletas de venta por producto
declare @CantidadProductoBOV numeric --variable almacena cantidad vendida de productos en boleta
--los montos netos deben ser calculados, ya que los precios de venta se almacenan con iva
set @FechasHasta = dateadd(dd,1,@FechasHasta)
--los documentos de venta cuentan con un descuento general (encabezado) y un descuento por linea de detalle
--los siguientes 2 updates de las tablas mae_boleta_venta (encabezado boleta) y det_boleta_venta (glosa detalle
-- de boleta) calculan el factor de descuento del encabezado que le corresponde a cada detalle para saber
-- realmente a cuanto se vendio el producto efectivamente
--solo por asegurarme vuelvo a calcular el subtotal bruto de la boleta, correspondiente al total con descuento mas el descuento
update mae_boleta_venta
set mbv_subtotal_bruto = mbv_total + mbv_monto_descuento
--el descuento de encabezado es un factor que se calcula dividiendo el monto de descuento general de la boleta por el subtotal de la boleta,
--el subtotal de la boleta corresponde a la suma de todos los totales de las lineas o tuplas del detalle de la boleta
--el total de la linea, corresponde a el subtotal de la linea menos el descuento de la linea
--el subtotal de la linea corresponde a la cantidad vendida por el valor unitario bruto de producto
--procedo a calcular el factor que le corresponde a cada linea por descuento general de boleta
update det_boleta_venta
set dbv_descuento_encabezado = mae_boleta_venta.mbv_monto_descuento / mae_boleta_venta.mbv_subtotal_bruto
from det_boleta_venta
join mae_boleta_venta
on det_boleta_venta.dbv_id_boleta_venta = mae_boleta_venta.mbv_id_boleta_venta
where mbv_subtotal_bruto > 0
----------------------------------------------------------------------------------
IF(@IdCategoria=0 AND @IdProducto > 0) --si no se ingreso categoria de producto y si se ingreso id de producto en formulario
BEGIN
--asigno al monto de las boletas el neto de la suma de los detalles con el descuento por encabezado y ya aplicado mediante el calculo
set @MontoBoletas = (SELECT sum(((D.dbv_total*100)/(b.mbv_porcentaje_iva + 100))* (1 - isnull(d.dbv_descuento_encabezado,0)))
FROM MAE_BOLETA_VENTA B --tabla maestra de la boleta (encabezado)
JOIN DET_BOLETA_VENTA D --tabla detalles de boleta (almacena todas las lineas de productos vendidos)
ON (B.mbv_id_boleta_venta = D.dbv_id_boleta_venta)
JOIN MAE_PRODUCTO_SERVICIO P
ON (P.mps_id_producto_SErvicio = D.dbv_id_producto_servicio)
WHERE (D.dbv_id_producto_servicio = @IdProducto OR @IdProducto=0) --filtro segun el id de producto ingresado
AND (B.mbv_fecha>=@FechasDesde AND B.mbv_fecha<=@FechasHasta) --filtro segun rango de fechas ingresadas
AND (P.mps_id_categoria_producto_servicio = @IdCategoria OR @IdCategoria=0) --filtro segun id de categorias de producto ingresado
AND B.mbv_nula = 0) --selecciono solo las boletas que no son nulas
set @CantidadProductoBOV = (SELECT SUM(D.dbv_cantidad) --sumo la cantidad de productos en el detalle y la asigno a la variable
FROM MAE_BOLETA_VENTA B
JOIN DET_BOLETA_VENTA D
ON (B.mbv_id_boleta_venta = D.dbv_id_boleta_venta)
JOIN MAE_PRODUCTO_SERVICIO P
ON (P.mps_id_producto_SErvicio = D.dbv_id_producto_servicio)
WHERE (D.dbv_id_producto_servicio = @IdProducto OR @IdProducto=0) --filtro segun el id de producto ingresado
AND (B.mbv_fecha>=@FechasDesde AND B.mbv_fecha<=@FechasHasta) --filtro segun rango de fechas ingresado
AND (p.mps_id_categoria_producto_servicio = @IdCategoria OR @IdCategoria=0) --filtro segun el id de categoria de producto ingresado
AND B.mbv_nula = 0) --selecciono solo las boletas que no son nulas
if(@MontoBoletas is null) --si el monto de las boletas es nulo le asigno un 0
set @MontoBoletas = 0
if(@CantidadProductoBOV is null) --si la cantidad de productos en boleta es nulo le asigno un 0
set @MontoBoletas = 0
SELECT
MAE_PRODUCTO_SERVICIO.mps_id_producto_servicio AS IdProducto, --id producto para manejo interno de consultas (llave primaria)
MAE_PRODUCTO_SERVICIO.mps_descripcion AS NombreProducto, --el nombre del producto p ej.: 'Coca Cola'
MAE_PRODUCTO_SERVICIO.mps_codigo_barra AS CodigoProducto, --codigo producto manejado por usuario de sw (codigo barras)
isnull(@MontoBoletas,0) AS MontoBoletas,
isnull(@MontoFacturas,0) AS MontoFacturas,
isnull(@MontoNotaCreditoVenta,0) AS MontoNotaCreditoVenta,
isnull(@MontoTotalVentas,0) AS MontoTotalVentas,
isnull(@CantidadProducto,0) as Cantidad,
isnull(mps_precio_venta,0) as PrecioVenta --precio bruto unitario del producto
FROM MAE_PRODUCTO_SERVICIO --table donde se guardan todos los datos que corresponden al producto
WHERE
(MAE_PRODUCTO_SERVICIO.mps_id_producto_servicio = @IdProducto or @IdProducto=0) AND --que el id del producto sea igual al que me ingresaron o el parametro venga en 0 lo
---que hace que el informe liste todos los productos y no un producto especifico
(MAE_PRODUCTO_SERVICIO.mps_id_categoria_producto_servicio=@IdCategoria OR @IdCategoria=0) --y que este en la categoria ingresada o que no filtre categorias
END
IF(@IdCategoria>=0 AND @IdProducto=0) --si no me ingresaron id de producto y me ingresaron o no un id de categoria de producto
BEGIN
declare @TotalProdCat numeric -- variable para almecenar la cantidad de tuplas en #TempIdProd
declare @Contador numeric --contador para recorrer la tabla #TempIdProd
declare @IdProd numeric --variable auxiliar para filtr
declare @PrecioVenta numeric --almacena el precio del producto
CREATE TABLE #TempVentasProd ( --tabla temporal para almacenar los detalles de la venta del producto
IdProducto numeric, --pk del producto
NombreProducto varchar(55), --descripcion del producto
CodigoProducto varchar(55), --codigo de barras del producto
MontoBoletas decimal(26,8), -- monto neto vendido del producto en boletas
MontoTotalVentas decimal (26,8), -- monto neto vendido del producto incluye facturas, notas de credito y boletas (en este caso es irrelevante xq solo mostrate la consulta que tiene q ver con las boletas)
Cantidad numeric, --cantidad vendida del producto correspondiente entre boletas, facturas y notas de credito.
PrecioVenta numeric --precio unitario bruto del producto
)
CREATE TABLE #TempIdProd --almacena id o pk de los productos vendidos
(
id int IDENTITY (1, 1), --genero un id autoincremental para poder usarlo como identificador de lineas
IdProd numeric, --campo para guardar el id del producto encontrado
)
INSERT #TempIdProd --guarda los id de los productos vendidos
SELECT DISTINCT DET_BOLETA_VENTA.dbv_id_producto_servicio --trae las distintas pk de producto en detalles de boleta
FROM MAE_PRODUCTO_SERVICIO --desde la tabla maestra de productos
JOIN DET_BOLETA_VENTA --unida con el detalle de la boleta de venta (productos vendidos)
ON MAE_PRODUCTO_SERVICIO.mps_id_producto_servicio = DET_BOLETA_VENTA.dbv_id_producto_servicio
JOIN MAE_BOLETA_VENTA --y unida con la cabecera de la boleta donde se vendio el producto
ON DET_BOLETA_VENTA.dbv_id_boleta_venta = MAE_BOLETA_VENTA.mbv_id_boleta_venta
WHERE (DET_BOLETA_VENTA.dbv_id_producto_servicio = @IdProducto OR @IdProducto=0) --donde el id del producto este en el detalle de la boleta
AND (MAE_BOLETA_VENTA.mbv_fecha>=@FechasDesde AND MAE_BOLETA_VENTA.mbv_fecha<=@FechasHasta) --y la boleta haya sido generada en el rango de fecha ingresado por usuario
AND (MAE_PRODUCTO_SERVICIO.mps_id_categoria_producto_servicio = @IdCategoria OR @IdCategoria=0) --y que el producto este en la categoria seleccionada
AND mbv_nula = 0 --donde la boleta no este anulada (mbv_nula es un bit donde 0 significa que la boleta no ha sido anulada)
set @TotalProdCat= (Select count(*) from #TempIdProd) --total de lineas en tabla temporal #TempIdProd
set @Contador=1
--mientras el contador sea menor a la cantidad total de registros en #TempIdProd
WHILE (@Contador <= @TotalProdCat) --recorre la tabla temporal #TempIdProd
BEGIN
--extrae el id de producto correspondiente a la linea segun indique el contador
set @IdProd = (Select IdProd from #TempIdProd where id=@Contador)
--asigno el neto de la suma de las lineas de detalle de la boleta sin el descuento que corresponde por el encabezado
--a la variable '@MontoBoletas'
set @MontoBoletas = (SELECT sum(((D.dbv_total*100)/(b.mbv_porcentaje_iva + 100)) --el neto de la suma de las lineas de detalle de la boleta
*(1- isnull(d.dbv_descuento_encabezado,0))) --calcula el valor segun lo que le corresponde de descuento por el encabezado de la boleta
FROM DET_BOLETA_VENTA D --tabla que almacena los detalles de la boleta de venta
JOIN MAE_BOLETA_VENTA b --unida a la tabla del encabezado de boleta de venta
ON (b.mbv_id_boleta_Venta = D.dbv_id_boleta_venta) --las uno por su clave primaria y foranea respectivamente correspondiente al id interno de la boleta de venta
WHERE (D.dbv_id_producto_servicio = @IdProd) --y filtro segun el id del producto ...
AND (b.mbv_fecha>=@FechasDesde AND b.mbv_fecha<=@FechasHasta) -- ...segun el rango de fechas ingresados...
AND b.mbv_nula = 0) --...y que la boleta no este anulada.
--asigno la cantidad de productos
set @CantidadProductoBOV = (SELECT SUM(DET_BOLETA_VENTA.dbv_cantidad) --suma la cantidad de productos vendidos segun lo que indica la linea de detalle
FROM DET_BOLETA_VENTA --detalle de boleta de venta
join MAE_BOLETA_VENTA --unido con el encabezado
on DET_BOLETA_VENTA.dbv_id_boleta_venta = MAE_BOLETA_VENTA.mbv_id_boleta_venta --a traves de la clave primaria del encabezado de la boleta de venta
WHERE (DET_BOLETA_VENTA.dbv_id_producto_servicio = @IdProd) --filtrado por el producto que me ingresaron a traves de formulario
AND (MAE_BOLETA_VENTA.mbv_fecha>=@FechasDesde AND MAE_BOLETA_VENTA.mbv_fecha<=@FechasHasta) --y que haya sido vendido en el rango de fechas indicado por el usuario
AND mbv_nula = 0 ) --donde la boleta no hata sido anulada
if(@MontoBoletas is null)
begin
set @MontoBoletas = 0 --si el monto de boletas es nulo le asigno un 0
end
if(@CantidadProductoBOV is null)
begin
set @CantidadProductoBOV = 0 --y si la cantidad es nula asigno un 0
end
set @MontoTotalVentas = @MontoBoletas -- se asigna a @MontoTotalVentas todo lo que se vendio del producto entre
--boletas, facturas y notas de credito.. en este caso solo lo mostrare con boletas para q veas una consulta mas corta
set @CantidadProducto = @CantidadProductoBOV -- se asigna el total de productos vendidos (en este caso solo boletas por lo explicado anteriormente)
set @PrecioVenta = (SELECT isnull(mps_precio_venta,0) -- consulta el precio unitario bruto de venta del producto, y si es nulo le asigna un 0
FROM MAE_PRODUCTO_SERVICIO -- de la tabla maestra de productos
WHERE mps_id_producto_servicio=@IdProd) --donde el id del producto sea igual al almacenado en la tabla temporal
INSERT #TempVentasProd --guarda los montos de venta segun el producto
SELECT
MAE_PRODUCTO_SERVICIO.mps_id_producto_servicio, --pk de producto
MAE_PRODUCTO_SERVICIO.mps_descripcion, --nombre de producto
MAE_PRODUCTO_SERVICIO.mps_codigo_barra, --codigo de producto
isnull(@MontoBoletas,0) AS MontoBoletas, --monto vendido del producto en boletas (neto)
isnull(@MontoTotalVentas,0) AS MontoTotalVentas, --monto total vendido del producto (neto)
isnull(@CantidadProducto,0) as Cantidad, --cantidad de productos vendidos
isnull(@PrecioVenta,0) as PrecioVenta --precio unitario de venta de producto (bruto)
FROM MAE_PRODUCTO_SERVICIO
WHERE (MAE_PRODUCTO_SERVICIO.mps_id_producto_servicio = @IdProd) --filtra segun el id que se este filtrando entre los productos vendidos
set @Contador = @Contador + 1 --suma 1 al contador para poder avanzar una tupla en #TempIdProd
END
SELECT DISTINCT * --selecciona los distintos registros que se guardaron en la tabla y con eso genera el informe
FROM #TempVentasProd
DROP TABLE #TempIdProd --borra la tabla temporal
DROP TABLE #TempVentasProd --bora la tbla temporal que almacena el informe (quedo cargado en grilla del formulario)
END
Pantallazos de Software
[img=http://img213.imageshack.us/img213/7987/informevendedor.th.png]
espero que me puedan ayudar... me he craneado bastante con esto y no se como arreglarlo..
saludos.. y gracias de antemano