Instruccion de Johnny: todos los directorios de fuentes de inventario deben migrarse de la carpeta temporal PROYECTOS a la carpeta real PROYECTO, ya que PROYECTOS sera eliminada. Se elimino tambien un archivo suelto sin relacion llamado "ipdir" que existia en PROYECTO (del commit inicial) y bloqueaba el nombre de la carpeta. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
792 lines
30 KiB
Plaintext
792 lines
30 KiB
Plaintext
{
|
|
-------------------------------------------------------------------------------
|
|
PROGRAMA : INPRRP013
|
|
OBJETIVO : REPORTE DIAS DE INVENTARIOS
|
|
PROGRAMADOR : Ing. Juan Francisco Soto
|
|
FECHA REALIZACION : Agosto 19, 1992
|
|
MODIFICADO POR : Ing. Juan Fco. Soto
|
|
FECHA ULT. MODIFICA : Agosto 20, 2026.
|
|
DESCRIPCION : Agregar Columna Costo Standard para cantidades mayores a
|
|
una cantidad de dias especificada.
|
|
|
|
AGOSTO 20, 2026 - ANALISIS DE MARGEN Y ROTACION:
|
|
1. MARGEN DE BENEFICIO: se calcula el precio de venta
|
|
REAL promedio del periodo (ponderado por cantidad,
|
|
neto del descuento de cabecera) desde vetb00002/
|
|
vetb00003, y el margen % sobre precio:
|
|
margen = (precio - costo) / precio * 100
|
|
El costo es el costo estandar (iptb00004), igual que
|
|
antes. Se movio el calculo del costo ANTES del calculo
|
|
del valor de exceso para que ese valor use el costo
|
|
real (antes usaba costo = 0).
|
|
2. ROTACION: igual que en ipprrp014:
|
|
rotacion = consumo / existencia
|
|
rotacion_anual = rotacion * (365 / dias del periodo)
|
|
En este reporte el consumo son las SALIDAS del periodo
|
|
(movs 30,31,55,56,40), que es la base correcta para la
|
|
rotacion de inventario.
|
|
3. CLASIFICACION por cuadrante (margen x rotacion):
|
|
ESTRELLA = alto margen + alta rotacion
|
|
VOLUMEN = bajo margen + alta rotacion
|
|
NICHO = alto margen + baja rotacion
|
|
REVISAR = bajo margen + baja rotacion (candidato
|
|
a descontinuar / renegociar costo)
|
|
SIN COSTO = producto sin costo estandar cargado
|
|
Umbrales configurables (g_margen_min, g_rot_min).
|
|
4. Se agregan valor_inv (existe*costo), ventas_valor
|
|
(consumo*precio) y ganancia (consumo*(precio-costo))
|
|
para poder totalizar el margen PONDERADO por CLASE en
|
|
el disenio .4rp (grupo por cod_n).
|
|
NOTA: el layout .4rp debe actualizarse en GST arrastrando
|
|
los nuevos campos (ver guia adjunta).
|
|
-------------------------------------------------------------------------------
|
|
}
|
|
GLOBALS "ipprgb000.4gl"
|
|
|
|
DEFINE sale CHAR(1)
|
|
DEFINE p_ano CHAR(4)
|
|
DEFINE fecha2 DATE
|
|
DEFINE dias1 SMALLINT,
|
|
m_meses SMALLINT,
|
|
conmes,valor1 DECIMAL(12,2)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Umbrales de clasificacion (ajustables aqui, o promover a campos del form).
|
|
# g_margen_min : margen % sobre precio a partir del cual se considera "alto"
|
|
# g_rot_min : rotacion ANUALIZADA (veces/anio) a partir de la cual es "alta"
|
|
# ------------------------------------------------------------------------------
|
|
DEFINE g_margen_min DECIMAL(6,2)
|
|
DEFINE g_rot_min DECIMAL(6,2)
|
|
|
|
# Acumulador por CLASE para el reporte Resumen (una fila por cod_n).
|
|
# A nivel de modulo para que emite_clase() y ipprrp013() lo compartan.
|
|
DEFINE rc RECORD
|
|
cod_n INTEGER,
|
|
nombre_clase CHAR(30),
|
|
categoria CHAR(30),
|
|
c_prod INTEGER,
|
|
c_estrella INTEGER,
|
|
c_revisar INTEGER,
|
|
c_valor_inv DECIMAL(16,2),
|
|
c_ventas DECIMAL(16,2),
|
|
c_ganancia DECIMAL(16,2),
|
|
c_margen DECIMAL(12,2),
|
|
c_rot_prom DECIMAL(12,4)
|
|
END RECORD
|
|
DEFINE rc_rot_sum DECIMAL(16,4)
|
|
|
|
MAIN
|
|
DEFER INTERRUPT
|
|
CALL ARG_VAL(1) RETURNING usuarios
|
|
CALL ARG_VAL(2) RETURNING clave
|
|
|
|
CONNECT to "smarmotech" USER usuarios USING clave
|
|
SELECT a.* INTO p_companias.* FROM companias a
|
|
|
|
CALL ipprrp013()
|
|
END MAIN
|
|
FUNCTION ipprrp013()
|
|
|
|
# Variable que toma el valor de los dias que se va a valorar el reporte
|
|
DEFINE c_dias INTEGER
|
|
# Combo para elegir Detallado / Resumen
|
|
DEFINE cbtipo ui.ComboBox
|
|
DEFINE prev_cod_n INTEGER
|
|
|
|
# Variables varias para el control de busqueda de las informaciones
|
|
DEFINE selec1,select_bal string
|
|
|
|
# Record para el consumo de los materiales
|
|
DEFINE consume RECORD
|
|
cod_n SMALLINT,
|
|
cod_grupo SMALLINT,
|
|
cod_tipo SMALLINT,
|
|
cod_sec SMALLINT,
|
|
existe DECIMAL(14,2)
|
|
END RECORD
|
|
|
|
# Record para la impresion del reporte, estas variables se validan con el
|
|
# select que esta en la variable selec
|
|
|
|
DEFINE estadis RECORD
|
|
cod_n LIKE intb00001.cod_n,
|
|
cod_grupo LIKE intb00001.cod_grupo,
|
|
cod_tipo LIKE intb00001.cod_tipo,
|
|
cod_sec LIKE intb00001.cod_sec,
|
|
descrip_esp LIKE intb00001.descrip_esp,
|
|
unidad_med LIKE iptb00002.unidad_med,
|
|
consumo LIKE iptb00006.cantidad_2,
|
|
grupo CHAR(30),
|
|
tipo CHAR(30),
|
|
categoria CHAR(30),
|
|
fecha date,
|
|
existe DEC(12,4),
|
|
valor DECIMAL(12,4),
|
|
costo DECIMAL(12,4),
|
|
dias INTEGER,
|
|
pcodigo char(25),
|
|
cantidad_orden,pcantidadConduce,transito,existencia,reservado,disponible DEC(12,2),
|
|
# --- Analisis margen / rotacion (Agosto 2026) ---
|
|
precio DEC(12,4), -- precio venta real prom. neto del periodo
|
|
margen DEC(12,2), -- % sobre precio = (precio-costo)/precio*100
|
|
rotacion DEC(12,4), -- consumo / existencia
|
|
rotacion_anual DEC(12,4), -- rotacion * (365 / dias del periodo)
|
|
valor_inv DEC(14,2), -- existencia * costo (valor inventario)
|
|
ventas_valor DEC(14,2), -- consumo * precio (venta del periodo)
|
|
ganancia DEC(14,2), -- consumo * (precio - costo)
|
|
clasificacion CHAR(12) -- ESTRELLA/VOLUMEN/NICHO/REVISAR/SIN COSTO
|
|
END RECORD
|
|
DEFINE handler om.SaxDocumentHandler, -- return value from fgl_report_commitCurrentSettings()
|
|
r_filename STRING, -- filename of Report Design Document including .4rp extension
|
|
r_output STRING, -- output format option
|
|
preview INTEGER -- TRUE/FALSE, to set preview option
|
|
# WHENEVER ERROR CONTINUE
|
|
|
|
OPTIONS
|
|
FORM LINE 8,
|
|
ERROR LINE 23,
|
|
COMMENT LINE 21
|
|
|
|
OPEN FORM ipfmrp013 FROM "ipfmrp013"
|
|
DISPLAY FORM ipfmrp013
|
|
#CALL pantalla()
|
|
DISPLAY "ipprrp013" AT 4,3 ATTRIBUTE(RED)
|
|
DISPLAY "Dias de Inventarios" AT 6,33 ATTRIBUTE(BLACK)
|
|
LET tipo_papel = 2
|
|
CALL msgrp000(tipo_papel)
|
|
|
|
# Umbrales de clasificacion margen/rotacion (ver DEFINE arriba)
|
|
LET g_margen_min = 25.00
|
|
LET g_rot_min = 2.00
|
|
|
|
|
|
INPUT BY NAME fecha1,fecha2,c_dias,m_meses,tipo_reporte
|
|
|
|
BEFORE INPUT
|
|
# Llena el combo Detallado / Resumen (mismo patron que categorias())
|
|
LET cbtipo = ui.ComboBox.forName("formonly.tipo_reporte")
|
|
IF cbtipo IS NOT NULL THEN
|
|
CALL cbtipo.clear()
|
|
CALL cbtipo.addItem("D","Detallado (por producto)")
|
|
CALL cbtipo.addItem("R","Resumen (por clase)")
|
|
END IF
|
|
# Por defecto el reporte DETALLADO
|
|
IF tipo_reporte IS NULL OR tipo_reporte = " " THEN
|
|
LET tipo_reporte = "D"
|
|
END IF
|
|
DISPLAY BY NAME tipo_reporte
|
|
|
|
AFTER FIELD c_dias
|
|
IF c_dias is null THEN
|
|
LET numero_msg = 16
|
|
CALL msg(numero_msg)
|
|
NEXT FIELD c_dias
|
|
END IF
|
|
|
|
AFTER FIELD fecha1
|
|
IF fecha1 IS NULL THEN
|
|
LET numero_msg = 16
|
|
CALL msg(numero_msg)
|
|
NEXT FIELD fecha1
|
|
END IF
|
|
|
|
AFTER FIELD fecha2
|
|
IF fecha2 IS NULL THEN
|
|
LET numero_msg = 16
|
|
CALL msg(numero_msg)
|
|
NEXT FIELD fecha2
|
|
END IF
|
|
|
|
AFTER FIELD m_meses
|
|
IF m_meses IS NULL THEN
|
|
LET numero_msg = 16
|
|
CALL msg(numero_msg)
|
|
NEXT FIELD m_meses
|
|
END IF
|
|
|
|
AFTER FIELD tipo_reporte
|
|
IF tipo_reporte IS NULL OR tipo_reporte = " " THEN
|
|
LET tipo_reporte = "D"
|
|
DISPLAY BY NAME tipo_reporte
|
|
END IF
|
|
END INPUT
|
|
|
|
IF int_flag THEN
|
|
LET numero_msg = 2
|
|
CALL msg(numero_msg)
|
|
LET int_flag = false
|
|
RETURN
|
|
END IF
|
|
|
|
|
|
LET dias1 = fecha2 - fecha1
|
|
LET dias1 = dias1 + 1
|
|
DISPLAY BY NAME dias1
|
|
SELECT MAX(a.ano) INTO p_ano
|
|
FROM prdtable a
|
|
WHERE a.fecha_corte <= fecha2
|
|
|
|
#LET p_ano = "2001"
|
|
#LET p_ano1 = "2002"
|
|
|
|
|
|
#LET p_ano = year(fecha2)
|
|
|
|
CONSTRUCT criterio ON x.cod_categoria,a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_sec
|
|
FROM cod_cat,cod_n,cod_grupo,cod_tipo,cod_sec
|
|
BEFORE CONSTRUCT
|
|
CALL categorias()
|
|
AFTER CONSTRUCT
|
|
IF int_flag THEN
|
|
LET numero_msg = 2
|
|
CALL msg(numero_msg)
|
|
LET int_flag = FALSE
|
|
RETURN
|
|
END IF
|
|
EXIT CONSTRUCT
|
|
END CONSTRUCT
|
|
|
|
DELETE FROM tmpipprrp013
|
|
|
|
# CREACION TABLA TEMPORAL
|
|
LET select_bal =
|
|
{ "SELECT a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_Sec,a.descrip_esp,a.unidad_med,SUM(b.cantidad_2),",
|
|
"d.descripcion,f.descripcion ",
|
|
"FROM ",
|
|
"iptb00034 x INNER JOIN
|
|
iptb00035 x1 ON x.cod_categoria = x1.cod_cat RIGHT OUTER JOIN",
|
|
" iptb00002 a LEFT OUTER JOIN iptb00006 b ON a.cod_n = b.cod_n AND ",
|
|
" a.cod_grupo = b.cod_grupo AND a.cod_tipo = b.cod_tipo AND a.cod_sec = b.cod_sec ",
|
|
" AND b.cod_mov in (30,31,55,56,40) ",
|
|
" AND CONVERT(DATE,b.fecha,103) between ? AND ? ",
|
|
",iptb00029 d,iptb00024 f",
|
|
" WHERE ",
|
|
criterio CLIPPED,
|
|
|
|
" AND a.cod_grupo = d.cod_grupo and",
|
|
" a.cod_tipo = f.cod_tipo and",
|
|
" b.status_t IS NULL AND ",
|
|
" a.status_t is null ",
|
|
" GROUP BY a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_Sec,a.descrip_esp,",
|
|
" f.descripcion,d.descripcion,a.unidad_med ",
|
|
"ORDER BY a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_Sec"
|
|
106-1818
|
|
}
|
|
" SELECT a.cod_n, a.cod_grupo, a.cod_tipo, a.cod_sec, a.descrip_esp, a.unidad_med, ISNULL(SUM(b.cantidad_2),0), d.descripcion, f.descripcion,x.descripcion
|
|
FROM iptb00034 x INNER JOIN
|
|
iptb00035 x1 ON x.cod_categoria = x1.cod_cat RIGHT OUTER JOIN
|
|
iptb00002 AS a LEFT OUTER JOIN
|
|
iptb00006 AS b ON a.cod_n = b.cod_n AND a.cod_grupo = b.cod_grupo AND a.cod_tipo = b.cod_tipo AND a.cod_sec = b.cod_sec AND b.cod_mov IN (30, 31, 55, 56, 40) AND b.fecha BETWEEN
|
|
? AND ? INNER JOIN
|
|
iptb00029 AS d ON a.cod_grupo = d.cod_grupo INNER JOIN
|
|
iptb00024 AS f ON a.cod_tipo = f.cod_tipo ON x1.cod_n = a.cod_n
|
|
WHERE ", criterio CLIPPED, " and (b.status_t IS NULL) AND (a.status_t IS NULL)
|
|
GROUP BY a.cod_n, a.cod_grupo, a.cod_tipo, a.cod_sec, a.descrip_esp, f.descripcion, d.descripcion, a.unidad_med,x.descripcion
|
|
ORDER BY a.cod_n, a.cod_grupo, a.cod_tipo, a.cod_sec"
|
|
|
|
PREPARE comando2 FROM select_bal
|
|
DECLARE accion CURSOR FOR comando2
|
|
OPEN accion USING fecha1,fecha2
|
|
|
|
# Busqueda de los costos de los materiales
|
|
LET selec1 =
|
|
"SELECT max(a.mes_ini),max(a.mes_fin),a.cod_n,a.cod_grupo,a.cod_tipo,",
|
|
" a.cod_sec,SUM(a.material+a.labor+a.gastos) ",
|
|
"FROM ",
|
|
" iptb00034 x INNER JOIN iptb00035 x1 ON x.cod_categoria = x1.cod_cat RIGHT OUTER JOIN ",
|
|
"iptb00004 a ON a.cod_n = x1.cod_n ",
|
|
"WHERE ",
|
|
" a.ano = ? AND ", criterio CLIPPED,
|
|
"GROUP BY a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_sec ORDER BY a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_sec"
|
|
|
|
DISPLAY "<< Estoy Buscando Costos... Espere >>"
|
|
AT 19,14 ATTRIBUTE (REVERSE,BOLD)
|
|
|
|
PREPARE comando FROM selec1
|
|
DECLARE busca3 SCROLL CURSOR FOR comando
|
|
OPEN busca3 USING p_ano
|
|
{
|
|
LET select_bal =
|
|
"SELECT a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_sec,ISNULL(sum(a.cantidad_2),0) ",
|
|
"FROM ",
|
|
" iptb00034 x LEFT OUTER JOIN iptb00035 x1 ON x.cod_categoria = x1.cod_cat RIGHT OUTER JOIN ",
|
|
"iptb00006 a ON a.cod_n = x1.cod_n WHERE ",
|
|
" a.status_t is null and a.fecha <= ? AND ",criterio CLIPPED,
|
|
" GROUP BY a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_sec ORDER BY a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_sec"
|
|
|
|
DISPLAY "<< Estoy Buscando Balances Iniciales >>"
|
|
AT 19,14 ATTRIBUTE (REVERSE,BOLD)
|
|
|
|
PREPARE busca_balan FROM select_bal
|
|
DECLARE consu SCROLL CURSOR FOR busca_balan
|
|
OPEN consu USING fecha2
|
|
}
|
|
# Elige el disenio .4rp segun la opcion del formulario:
|
|
# D = Detallado (una linea por producto) -> ipprrp013.4rp
|
|
# R = Resumen (subtotales por CLASE cod_n) -> ipprrp013r.4rp
|
|
IF tipo_reporte = "R" THEN
|
|
LET r_filename = "ipprrp013r.4rp"
|
|
ELSE
|
|
LET r_filename = "ipprrp013.4rp"
|
|
END IF
|
|
|
|
LET idx = 1
|
|
LET prev_cod_n = -999999
|
|
FOREACH accion INTO estadis.*
|
|
|
|
LET estadis.pcodigo = estadis.cod_n USING "&&&&","-",
|
|
estadis.cod_grupo using "&&&&","-",
|
|
estadis.cod_tipo using "&&&&","-",
|
|
estadis.cod_sec using "&&&&&&"
|
|
|
|
IF int_flag THEN
|
|
LET numero_msg = 2
|
|
CALL msg(numero_msg)
|
|
LET int_flag = FALSE
|
|
LET estadis.consumo = 0
|
|
LET estadis.existe = 0
|
|
# Busca el consumo de los materiales
|
|
LET sale = "N"
|
|
RETURN
|
|
END IF
|
|
IF idx =1 THEN
|
|
iF fgl_report_loadCurrentSettings(r_filename) THEN -- load the .4rp file
|
|
CALL seleccionarSalida() RETURNING r_output
|
|
LET preview=1
|
|
CALL fgl_report_selectDevice(r_output) -- changing default
|
|
CALL fgl_report_selectPreview(preview) -- changing default
|
|
LET handler = fgl_report_commitCurrentSettings() -- commit changes
|
|
ELSE
|
|
EXIT PROGRAM
|
|
END IF
|
|
--run the report
|
|
IF handler IS NOT NULL THEN -- report engine was configured ok
|
|
IF tipo_reporte = "R" THEN
|
|
START REPORT resumen TO XML HANDLER HANDLER
|
|
ELSE
|
|
START REPORT estadistica TO XML HANDLER HANDLER
|
|
END IF
|
|
ELSE
|
|
EXIT FOREACH
|
|
END IF
|
|
END IF
|
|
|
|
{LET sale = "N"
|
|
WHILE sale != "S"
|
|
FETCH consu INTO consume.*
|
|
IF status = notfound THEN
|
|
LET sale = "S"
|
|
LET STATUS = 0
|
|
EXIT WHILE
|
|
END IF
|
|
|
|
|
|
IF consume.cod_n = estadis.cod_n AND
|
|
consume.cod_grupo = estadis.cod_grupo AND
|
|
consume.cod_Tipo = estadis.cod_tipo AND
|
|
consume.cod_sec = estadis.cod_sec THEN
|
|
|
|
LET estadis.existe = consume.existe
|
|
LET sale = "S"
|
|
EXIT WHILE
|
|
END IF
|
|
END WHILE
|
|
}
|
|
|
|
# Busqueda del costo del material
|
|
# Se multiplica por -1 por que el movimiento 36 es negativo
|
|
IF estadis.consumo < 0 THEN
|
|
LET estadis.consumo = estadis.consumo * -1
|
|
END IF
|
|
CALL disponibles(estadis.cod_n,estadis.cod_grupo,estadis.cod_tipo,estadis.cod_sec,fecha2)
|
|
RETURNING estadis.cantidad_orden,estadis.pcantidadConduce,estadis.transito,estadis.existencia,estadis.reservado,
|
|
valororden
|
|
|
|
LET estadis.existe = estadis.existencia
|
|
|
|
# ---- COSTO ESTANDAR (movido aqui para que el valor de exceso Y el margen
|
|
# usen el costo real; antes se calculaba despues y quedaba en cero) ----
|
|
LET estadis.costo = 0
|
|
SELECT AVG(a.material+a.labor+a.gastos) INTO estadis.costo FROM iptb00004 a
|
|
WHERE a.ano = YEAR(TODAY) AND a.mes_fin = 12
|
|
AND a.cod_n = estadis.cod_n AND a.cod_grupo = estadis.cod_grupo
|
|
AND a.cod_tipo = estadis.cod_tipo AND a.cod_sec = estadis.cod_sec
|
|
AND a.status_t IS NULL
|
|
IF estadis.costo IS NULL THEN LET estadis.costo = 0 END IF
|
|
|
|
# Calculo para los dias de produccion
|
|
IF estadis.existe > 0 THEN
|
|
LET estadis.cantidad_orden = estadis.cantidad_orden - estadis.pcantidadConduce
|
|
LET estadis.disponible=estadis.existe -estadis.cantidad_orden - estadis.reservado
|
|
LET estadis.dias = (estadis.existe / estadis.consumo) * dias1
|
|
ELSE
|
|
LET estadis.dias =0
|
|
END IF
|
|
|
|
|
|
IF estadis.disponible <= 0 THEN
|
|
CONTINUE FOREACH
|
|
END IF
|
|
IF estadis.dias < 0 THEN
|
|
LET estadis.dias = 0
|
|
END IF
|
|
|
|
# Calculo para el exedente de la existencia apartir de los dias de exeso
|
|
# especificado
|
|
|
|
LET estadis.valor = 0
|
|
IF estadis.dias >= c_dias THEN
|
|
IF estadis.dias > 0 THEN
|
|
LET valor1 = (estadis.existe * c_dias)/estadis.dias
|
|
LET estadis.valor = (estadis.existe - valor1) * estadis.costo
|
|
ELSE
|
|
LET estadis.valor = 0
|
|
END IF
|
|
END IF
|
|
|
|
# ============================================================================
|
|
# MARGEN DE BENEFICIO Y ROTACION
|
|
# ============================================================================
|
|
# Precio de venta REAL promedio del periodo, ponderado por cantidad y neto del
|
|
# descuento de cabecera (porc_desc). Fuente: facturas vetb00002 + detalle
|
|
# vetb00003 con fecha_factura dentro del rango [fecha1, fecha2].
|
|
LET estadis.precio = 0
|
|
SELECT SUM(d.cantidad * d.precio * (1 - (ISNULL(h.porc_desc,0)/100.0)))
|
|
/ NULLIF(SUM(d.cantidad),0)
|
|
INTO estadis.precio
|
|
FROM vetb00003 d, vetb00002 h
|
|
WHERE d.factura = h.factura
|
|
AND d.cod_n = estadis.cod_n AND d.cod_grupo = estadis.cod_grupo
|
|
AND d.cod_tipo = estadis.cod_tipo AND d.cod_sec = estadis.cod_sec
|
|
AND h.status_t IS NULL AND d.status_t IS NULL
|
|
AND h.fecha_factura BETWEEN fecha1 AND fecha2
|
|
IF estadis.precio IS NULL THEN LET estadis.precio = 0 END IF
|
|
|
|
# Margen % sobre precio = (precio - costo) / precio * 100
|
|
IF estadis.precio > 0 AND estadis.costo > 0 THEN
|
|
LET estadis.margen = ((estadis.precio - estadis.costo) / estadis.precio) * 100
|
|
ELSE
|
|
LET estadis.margen = 0
|
|
END IF
|
|
|
|
# Rotacion (misma formula que ipprrp014): consumo / existencia
|
|
# anualizada = rotacion * (365 / dias del periodo) -- 365.0 evita div. entera
|
|
IF estadis.existe > 0 AND estadis.consumo > 0 THEN
|
|
LET estadis.rotacion = estadis.consumo / estadis.existe
|
|
LET estadis.rotacion_anual = estadis.rotacion * (365.0 / dias1)
|
|
ELSE
|
|
LET estadis.rotacion = 0
|
|
LET estadis.rotacion_anual = 0
|
|
END IF
|
|
|
|
# Valores para totalizar el margen PONDERADO por CLASE en el .4rp
|
|
LET estadis.valor_inv = estadis.existe * estadis.costo
|
|
LET estadis.ventas_valor = estadis.consumo * estadis.precio
|
|
LET estadis.ganancia = estadis.consumo * (estadis.precio - estadis.costo)
|
|
|
|
# Clasificacion por cuadrante (margen x rotacion)
|
|
CASE
|
|
WHEN estadis.costo <= 0
|
|
LET estadis.clasificacion = "SIN COSTO"
|
|
WHEN estadis.margen >= g_margen_min AND estadis.rotacion_anual >= g_rot_min
|
|
LET estadis.clasificacion = "ESTRELLA"
|
|
WHEN estadis.margen < g_margen_min AND estadis.rotacion_anual >= g_rot_min
|
|
LET estadis.clasificacion = "VOLUMEN"
|
|
WHEN estadis.margen >= g_margen_min AND estadis.rotacion_anual < g_rot_min
|
|
LET estadis.clasificacion = "NICHO"
|
|
OTHERWISE
|
|
LET estadis.clasificacion = "REVISAR"
|
|
END CASE
|
|
|
|
IF estadis.existe > 0 THEN
|
|
IF tipo_reporte = "R" THEN
|
|
# --- Agregacion por CLASE: al cambiar cod_n, emite la clase anterior ---
|
|
IF prev_cod_n <> estadis.cod_n THEN
|
|
IF prev_cod_n <> -999999 THEN
|
|
CALL emite_clase()
|
|
END IF
|
|
# Nueva clase: reinicia acumuladores
|
|
LET prev_cod_n = estadis.cod_n
|
|
LET rc.cod_n = estadis.cod_n
|
|
LET rc.categoria = estadis.categoria
|
|
LET rc.nombre_clase = NULL
|
|
SELECT a.producto INTO rc.nombre_clase
|
|
FROM iptb00020 a WHERE a.cod_n = estadis.cod_n
|
|
IF rc.nombre_clase IS NULL THEN LET rc.nombre_clase = " " END IF
|
|
LET rc.c_prod = 0
|
|
LET rc.c_estrella = 0
|
|
LET rc.c_revisar = 0
|
|
LET rc.c_valor_inv = 0
|
|
LET rc.c_ventas = 0
|
|
LET rc.c_ganancia = 0
|
|
LET rc_rot_sum = 0
|
|
END IF
|
|
LET rc.c_prod = rc.c_prod + 1
|
|
LET rc.c_valor_inv = rc.c_valor_inv + estadis.valor_inv
|
|
LET rc.c_ventas = rc.c_ventas + estadis.ventas_valor
|
|
LET rc.c_ganancia = rc.c_ganancia + estadis.ganancia
|
|
LET rc_rot_sum = rc_rot_sum + estadis.rotacion_anual
|
|
IF estadis.clasificacion = "REVISAR" THEN LET rc.c_revisar = rc.c_revisar + 1 END IF
|
|
IF estadis.clasificacion = "ESTRELLA" THEN LET rc.c_estrella = rc.c_estrella + 1 END IF
|
|
ELSE
|
|
OUTPUT TO REPORT estadistica(estadis.*,dias1,c_dias)
|
|
END IF
|
|
END IF
|
|
LET idx = idx +1
|
|
LET estadis.consumo = 0
|
|
LET estadis.existe = 0
|
|
LET estadis.precio = 0
|
|
LET estadis.margen = 0
|
|
LET estadis.rotacion = 0
|
|
LET estadis.rotacion_anual = 0
|
|
END FOREACH
|
|
IF idx = 1 THEN
|
|
CALL fgl_winmessage("INFO","NO EXISTEN INFORMACIONES CON ESTOS PARAMETROS","INFO")
|
|
ELSE
|
|
IF tipo_reporte = "R" THEN
|
|
# Emite la ultima clase acumulada
|
|
IF prev_cod_n <> -999999 THEN
|
|
CALL emite_clase()
|
|
END IF
|
|
FINISH REPORT resumen
|
|
ELSE
|
|
FINISH REPORT estadistica
|
|
END IF
|
|
END IF
|
|
#RUN imprime
|
|
|
|
CLEAR SCREEN
|
|
|
|
END FUNCTION
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Calcula el margen ponderado y la rotacion promedio de la clase acumulada en
|
|
# 'rc' y la envia al reporte 'resumen'. Usada por ipprrp013() al cambiar de
|
|
# CLASE y al terminar el FOREACH.
|
|
# ------------------------------------------------------------------------------
|
|
FUNCTION emite_clase()
|
|
IF rc.c_ventas > 0 THEN
|
|
LET rc.c_margen = (rc.c_ganancia / rc.c_ventas) * 100
|
|
ELSE
|
|
LET rc.c_margen = 0
|
|
END IF
|
|
IF rc.c_prod > 0 THEN
|
|
LET rc.c_rot_prom = rc_rot_sum / rc.c_prod
|
|
ELSE
|
|
LET rc.c_rot_prom = 0
|
|
END IF
|
|
OUTPUT TO REPORT resumen(rc.*)
|
|
END FUNCTION
|
|
|
|
REPORT estadistica(x,dias2,c_dias)
|
|
DEFINE x RECORD
|
|
cod_n LIKE intb00001.cod_n,
|
|
cod_grupo LIKE intb00001.cod_grupo,
|
|
cod_tipo LIKE intb00001.cod_tipo,
|
|
cod_sec LIKE intb00001.cod_sec,
|
|
descrip_esp LIKE intb00001.descrip_esp,
|
|
unidad_med LIKE iptb00002.unidad_med,
|
|
consumo LIKE iptb00006.cantidad_1,
|
|
grupo CHAR(30),
|
|
tipo CHAR(30),
|
|
categoria CHAR(30),
|
|
fecha date,
|
|
existe DEC(12,4),
|
|
valor DECIMAL(12,3),
|
|
costo DECIMAL(12,4),
|
|
dias INTEGER,
|
|
pcodigo char(25),
|
|
cantidad_orden,pcantidadConduce,transito,existencia,reservado,disponible DEC(12,2),
|
|
# --- Analisis margen / rotacion (Agosto 2026) - debe reflejar estadis ---
|
|
precio DEC(12,4),
|
|
margen DEC(12,2),
|
|
rotacion DEC(12,4),
|
|
rotacion_anual DEC(12,4),
|
|
valor_inv DEC(14,2),
|
|
ventas_valor DEC(14,2),
|
|
ganancia DEC(14,2),
|
|
clasificacion CHAR(12)
|
|
END RECORD,
|
|
cantidad_rec,cantidad_or,tcantidad DEC(12,4),
|
|
fecha_v,fecha_oc DATE
|
|
|
|
DEFINE c_dias SMALLINT,
|
|
dias_oc INTEGER
|
|
DEFINE m_total DECIMAL(10,2)
|
|
DEFINE dias2 SMALLINT
|
|
DEFINE hora CHAR(5)
|
|
DEFINE t_total,t_valor DECIMAL(12,2),
|
|
chorden STRING,
|
|
porden INTEGER,
|
|
fecha DATE,
|
|
pcosto dec(12,4),
|
|
total_registros smallint,
|
|
total_grupo smallint,
|
|
nombre CHAR(50),
|
|
xsuplidor VARCHAR(100)
|
|
|
|
FORMAT
|
|
FIRST PAGE HEADER
|
|
LET hora = time
|
|
LET fecha = today
|
|
LET total_registros = 0
|
|
LET tcantidad = 0
|
|
LET fecha_v = NULL
|
|
LET fecha_oc = NULL
|
|
|
|
BEFORE GROUP OF x.cod_grupo
|
|
let total_grupo = 0
|
|
BEFORE GROUP OF x.cod_tipo
|
|
|
|
LET hora = time
|
|
SELECT a.* INTO p_companias.* FROM companias a WHERE a.cod_comp = 1
|
|
LET lj = (120 - LENGTH(p_companias.nombre CLIPPED))/2
|
|
|
|
|
|
|
|
|
|
ON EVERY ROW
|
|
LET total_registros = total_registros +1
|
|
LET total_grupo = total_grupo +1
|
|
LET nombre_ant = nombre
|
|
|
|
# BUSCA ULTIMA FECHA DE COMPRA Y VENTA DEL PRODUCTO
|
|
LET fecha_v = NULL
|
|
SELECT convert(char(10),MAX(a.fecha_factura),103) INTO fecha_v
|
|
FROM vetb00003 d,vetb00002 a
|
|
WHERE d.factura = a.factura AND
|
|
d.cod_n=x.cod_n AND d.cod_grupo=x.cod_grupo AND
|
|
d.cod_tipo = x.cod_tipo AND d.cod_sec = x.cod_sec AND
|
|
a.status_t is NULL
|
|
LET fecha_oc = NULL
|
|
SELECT ISNULL(convert(char(10),MAX(a.fech_oc),103)," ") INTO fecha_oc
|
|
FROM cotb00015 d,cotb00014 a
|
|
WHERE d.num_oc = a.num_oc AND
|
|
d.cod_n=x.cod_n AND d.cod_grupo=x.cod_grupo AND
|
|
d.cod_tipo = x.cod_tipo AND d.cod_sec = x.cod_sec AND
|
|
d.status_t is NULL
|
|
LET tcantidad =0
|
|
SELECT SUM(d.cantidad) INTO tcantidad
|
|
FROM cotb00015 d,cotb00014 a
|
|
WHERE d.num_oc = a.num_oc AND
|
|
d.cod_n=x.cod_n AND d.cod_grupo=x.cod_grupo AND
|
|
d.cod_tipo = x.cod_tipo AND d.cod_sec = x.cod_sec AND
|
|
a.fech_oc = fecha_oc AND
|
|
d.status_t is NULL
|
|
|
|
LET xsuplidor =null
|
|
SELECT MAX(c.nom_sp) INTO xsuplidor
|
|
FROM cotb00015 d,cotb00014 a,cotb00001 c
|
|
WHERE d.num_oc = a.num_oc AND
|
|
d.cod_n=x.cod_n AND d.cod_grupo=x.cod_grupo AND
|
|
d.cod_tipo = x.cod_tipo AND d.cod_sec = x.cod_sec AND
|
|
a.fech_oc = fecha_oc AND
|
|
a.cod_sp = c.cod_sp AND
|
|
a.cod_sp_sec = c.cod_sp_sec AND
|
|
d.status_t is NULL
|
|
|
|
LET conmes = 0
|
|
|
|
IF t_total IS NULL THEN
|
|
LET t_total = 0
|
|
END IF
|
|
IF t_valor is null THEN
|
|
LET t_valor = 0
|
|
END IF
|
|
{ COLOQUE LA BUSQUEDA DE LA ORDEN PARA SUSTITUIR EL QUERY POR LA ULTIMA CANTIDAD COMPRADA
|
|
DECLARE busca_oc CURSOR FOR
|
|
SELECT a.num_oc,a.cantidad FROM cotb00015 a, cotb00014 b
|
|
WHERE a.num_oc = b.num_oc AND
|
|
a.cod_n = x.cod_n AND
|
|
a.cod_grupo = x.cod_grupo AND
|
|
a.cod_tipo = x.cod_tipo AND
|
|
a.cod_sec = x.cod_sec AND
|
|
b.cierre = "N" AND
|
|
b.status_T IS NULL
|
|
|
|
LET chorden = NULL
|
|
LET tcantidad = 0
|
|
FOREACH busca_oc INTO porden,cantidad_rec
|
|
IF cantidad_rec IS NULL THEN
|
|
LET cantidad_rec = 0
|
|
END IF
|
|
|
|
LET tcantidad = tcantidad + cantidad_Rec
|
|
LET chorden = porden using "<<<<<<",", ",chorden CLIPPED
|
|
|
|
END FOREACH
|
|
}
|
|
LET m_total = x.existe * x.costo
|
|
LET cantidad_or = tcantidad + x.existe
|
|
|
|
IF x.consumo > 0 THEN
|
|
LET dias_oc = (cantidad_or/x.consumo) * dias1
|
|
END IF
|
|
|
|
LET conmes = x.consumo / m_meses
|
|
|
|
PRINTX x.*,fecha,hora,fecha1,fecha2,fecha_oc,chorden,tcantidad,
|
|
p_companias.*,pcosto,total_registros,total_grupo,fecha_v
|
|
INSERT INTO tmpipprrp013 VALUES (fecha1,fecha2,x.categoria,xsuplidor,x.grupo,x.pcodigo,x.descrip_esp,x.existe,x.disponible,x.cantidad_orden,
|
|
x.dias,x.consumo,fecha_oc,x.costo,fecha_v)
|
|
|
|
|
|
|
|
END REPORT
|
|
|
|
# ==============================================================================
|
|
# REPORTE RESUMEN POR CLASE (cod_n) -- version PLANA (sin grupos)
|
|
# ------------------------------------------------------------------------------
|
|
# Recibe UN registro ya agregado por CLASE (lo arma el FOREACH principal) y emite
|
|
# una linea por clase via ON EVERY ROW. El gran total se acumula aqui y se
|
|
# muestra en el pie del disenio ipprrp013r.4rp (patron identico al footer de
|
|
# 'total_registros' del reporte de detalle, por eso no usa grupos ni triggers).
|
|
# ==============================================================================
|
|
REPORT resumen(r)
|
|
DEFINE r RECORD
|
|
cod_n INTEGER,
|
|
nombre_clase CHAR(30),
|
|
categoria CHAR(30),
|
|
c_prod INTEGER,
|
|
c_estrella INTEGER,
|
|
c_revisar INTEGER,
|
|
c_valor_inv DECIMAL(16,2),
|
|
c_ventas DECIMAL(16,2),
|
|
c_ganancia DECIMAL(16,2),
|
|
c_margen DECIMAL(12,2),
|
|
c_rot_prom DECIMAL(12,4)
|
|
END RECORD
|
|
|
|
DEFINE hora CHAR(5),
|
|
fecha DATE,
|
|
g_prod INTEGER,
|
|
g_valor_inv, g_ventas, g_ganancia DECIMAL(16,2),
|
|
g_margen DECIMAL(12,2)
|
|
|
|
FORMAT
|
|
FIRST PAGE HEADER
|
|
LET hora = TIME
|
|
LET fecha = TODAY
|
|
SELECT a.* INTO p_companias.* FROM companias a WHERE a.cod_comp = 1
|
|
LET lj = (120 - LENGTH(p_companias.nombre CLIPPED))/2
|
|
LET g_prod = 0
|
|
LET g_valor_inv = 0
|
|
LET g_ventas = 0
|
|
LET g_ganancia = 0
|
|
|
|
ON EVERY ROW
|
|
LET g_prod = g_prod + r.c_prod
|
|
LET g_valor_inv = g_valor_inv + r.c_valor_inv
|
|
LET g_ventas = g_ventas + r.c_ventas
|
|
LET g_ganancia = g_ganancia + r.c_ganancia
|
|
IF g_ventas > 0 THEN
|
|
LET g_margen = (g_ganancia / g_ventas) * 100
|
|
ELSE
|
|
LET g_margen = 0
|
|
END IF
|
|
PRINTX r.*,
|
|
g_prod, g_valor_inv, g_ventas, g_ganancia, g_margen,
|
|
p_companias.*, fecha, hora, fecha1, fecha2
|
|
|
|
END REPORT
|