Files
MBS/PROYECTOS/ipdir/ipprrp014.4gl
T

522 lines
19 KiB
Plaintext

{
-------------------------------------------------------------------------------
PROGRAMA : IPPRRP014 (OPTIMIZADO)
OBJETIVO : REPORTE DIAS DE INVENTARIOS - GERENTE DE MARCAS
PROGRAMADOR : Ing. Juan Francisco Soto
FECHA REALIZACION : Agosto 19, 1992
FECHA ULT. MODIFICA : Julio 07, 2026
DESCRIPCION : OPTIMIZACION DE RENDIMIENTO:
1. Ultima venta, ultima compra y cantidad comprada se
calculan en UN SOLO query (LEFT JOIN a subconsultas
agregadas) en lugar de 3 SELECTs por producto dentro
del REPORT. Se elimino la tautologia
d.numero_orden = d.numero_orden.
2. Se elimino DISPLAY por fila y el refresh de progreso
se hace cada 50 filas (cada refresh es un round-trip
al GDC).
3. Se elimino el SELECT a companias dentro de
BEFORE GROUP OF (ya esta cargado en MAIN).
4. CORRECCION FORMULA ROTACION:
Antes: rotacion = existencia / consumo (cobertura)
Ahora: rotacion = consumo / existencia
(indice de rotacion estandar: veces que el
inventario rota en el periodo). Se agrega ademas
la rotacion anualizada = rotacion * (365/dias).
DIAS INV se mantiene igual (formula correcta).
-------------------------------------------------------------------------------
}
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),
representa VARCHAR(100),
num_representa INT,
chexistencia CHAR(2),
progreso, cuenta_reg, cuenta INT
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
# Variables varias para el control de busqueda de las informaciones
DEFINE selec1, select_bal STRING
# Datos precalculados por SQL (antes se buscaban con 3 SELECTs por fila)
DEFINE
w_fecha_v DATE,
w_fecha_oc DATE,
w_tcantidad DEC(12, 4)
DEFINE rotacion_anual DEC(12, 2)
# Record para la impresion del reporte
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 DEC(12, 4),
grupo CHAR(30),
tipo CHAR(30),
codCategoria INT,
categoria CHAR(30),
fecha_v, fecha_oc DATE,
tcantidad DEC(12, 4),
existe DEC(12, 4),
valor DECIMAL(12, 4),
costo DECIMAL(12, 4),
dias INTEGER,
pcodigo CHAR(25),
cantidad_orden,
pcantidadConduce,
transito,
existencia,
reservado,
disponible,
precio,
rotacion
DEC(12, 2)
END RECORD
DEFINE
handler om.SaxDocumentHandler,
r_filename STRING,
r_output STRING,
preview INTEGER
OPTIONS FORM LINE 8, ERROR LINE 23, COMMENT LINE 21
OPEN FORM ipfmrp014 FROM "ipfmrp014"
DISPLAY FORM ipfmrp014
DISPLAY "ipprrp014" AT 4, 3 ATTRIBUTE(RED)
DISPLAY "Dias de Inventarios Gerente Marcas" AT 6, 33 ATTRIBUTE(BLACK)
INPUT BY NAME fecha1, fecha2, c_dias, m_meses, chexistencia
BEFORE INPUT
LET progreso = 10
LET c_dias = 0
DISPLAY BY NAME c_dias, progreso
SELECT DISTINCT RTRIM(a.nom1_emp) + ' ' + RTRIM(a.apell1_emp),
a.num_emp
INTO representa, num_representa
FROM adtb00003 a, seg0001 b
WHERE a.num_emp = b.num_emp
AND b.usuario = usuarios
AND a.gerentemarcas = 'SI'
IF STATUS = NOTFOUND THEN
CALL fgl_winmessage(
"INFO",
"USTED NO ES GERENTE DE MARCAS, NO PUEDE GENERAR ESTE INFORME",
"INFO")
EXIT PROGRAM
END IF
DISPLAY BY NAME representa
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 OR m_meses = 0 THEN
LET numero_msg = 16
CALL msg(numero_msg)
NEXT FIELD m_meses
END IF
AFTER INPUT
IF int_flag THEN
LET numero_msg = 2
CALL msg(numero_msg)
LET int_flag = FALSE
RETURN
END IF
IF chexistencia IS NULL THEN
NEXT FIELD chexistencia
END IF
END INPUT
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
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
# Conteo para la barra de progreso (sin ORDER BY, innecesario en un COUNT)
LET select_bal =
"SELECT COUNT(*)
FROM iptb00002 a INNER JOIN
iptb00035 x1 ON a.cod_n = x1.cod_n INNER JOIN
iptb00029 d ON a.cod_grupo = d.cod_grupo INNER JOIN
iptb00034 x ON x.cod_categoria = x1.cod_cat where ",
criterio CLIPPED,
" and a.num_emp = ",
num_representa
PREPARE comando1 FROM select_bal
EXECUTE comando1 INTO cuenta_reg
# ==============================================================================
# QUERY PRINCIPAL OPTIMIZADO (CONJUNTO, NO FILA POR FILA)
# ------------------------------------------------------------------------------
# Las subconsultas uv (ultima venta) y uc (ultima compra + cantidad) se agregan
# UNA sola vez por GROUP BY y se unen por LEFT JOIN. SQL Server recorre las
# tablas de historico una vez en total, en lugar de una vez por cada producto.
# Se corrigio la condicion muerta "d.numero_orden = d.numero_orden".
# ==============================================================================
LET select_bal =
"SELECT a.cod_n, a.cod_grupo, a.cod_tipo, a.cod_sec, a.descrip_esp,",
" a.unidad_med, '0', d.descripcion, ' ', x.cod_categoria, x.descripcion,",
" convert(char(10), uv.fecha_v, 103),",
" convert(char(10), uc.fecha_oc, 103),",
" ISNULL(uc.tcantidad, 0)",
" FROM iptb00002 a",
" INNER JOIN iptb00035 x1 ON a.cod_n = x1.cod_n",
" INNER JOIN iptb00029 d ON a.cod_grupo = d.cod_grupo",
" INNER JOIN iptb00034 x ON x.cod_categoria = x1.cod_cat",
" LEFT JOIN (",
" SELECT dv.cod_n, dv.cod_grupo, dv.cod_tipo, dv.cod_sec,",
" MAX(fv.fecha_factura) AS fecha_v",
" FROM vetb00003 dv",
" INNER JOIN vetb00002 fv ON dv.factura = fv.factura",
" WHERE fv.status_t IS NULL",
" GROUP BY dv.cod_n, dv.cod_grupo, dv.cod_tipo, dv.cod_sec",
" ) uv ON uv.cod_n = a.cod_n AND uv.cod_grupo = a.cod_grupo",
" AND uv.cod_tipo = a.cod_tipo AND uv.cod_sec = a.cod_sec",
" LEFT JOIN (",
" SELECT dc.cod_n, dc.cod_grupo, dc.cod_tipo, dc.cod_sec,",
" MAX(oc.fech_oc) AS fecha_oc,",
" SUM(dc.cantidad) AS tcantidad",
" FROM cotb00015 dc",
" INNER JOIN cotb00014 oc ON dc.num_oc = oc.num_oc",
" WHERE dc.status_t IS NULL",
" GROUP BY dc.cod_n, dc.cod_grupo, dc.cod_tipo, dc.cod_sec",
" ) uc ON uc.cod_n = a.cod_n AND uc.cod_grupo = a.cod_grupo",
" AND uc.cod_tipo = a.cod_tipo AND uc.cod_sec = a.cod_sec",
" WHERE ",
criterio CLIPPED,
" AND a.num_emp = ",
num_representa,
" ORDER BY x.cod_categoria, a.cod_n, a.cod_grupo, a.cod_tipo, a.cod_sec"
PREPARE comando2 FROM select_bal
DECLARE accion CURSOR FOR comando2
LET r_filename = "ipprrp014.4rp"
LET idx = 1
DISPLAY progreso TO progreso
CALL ui.Interface.refresh()
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
LET sale = "N"
RETURN
END IF
IF idx = 1 THEN
IF fgl_report_loadCurrentSettings(r_filename) THEN
CALL seleccionarSalida() RETURNING r_output
LET preview = 1
CALL fgl_report_selectDevice(r_output)
CALL fgl_report_selectPreview(preview)
CALL fgl_report_configurexlsxdevice(null,null,null,false,false,null,1)
LET handler = fgl_report_commitCurrentSettings()
ELSE
EXIT PROGRAM
END IF
IF handler IS NOT NULL THEN
START REPORT estadistica TO XML HANDLER handler
ELSE
EXIT FOREACH
END IF
END IF
# Busqueda de existencias / consumo (pendiente de convertir a conjunto,
# ver nota al final del programa)
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
IF chexistencia = 'NO' AND estadis.existencia <= 0 THEN
LET idx = idx + 1
CONTINUE FOREACH
END IF
LET estadis.consumo = estadis.pcantidadConduce
IF estadis.consumo < 0 THEN
LET estadis.consumo = estadis.consumo * -1
END IF
LET estadis.existe = estadis.existencia
# 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
IF estadis.consumo > 0 THEN
LET estadis.dias = (estadis.existe / estadis.consumo) * dias1
# ==============================================================
# FORMULA CORREGIDA: indice de rotacion = consumo / existencia
# (veces que rota el inventario en el periodo).
# La formula anterior (existe/consumo) era la COBERTURA.
# Nota: idealmente usar existencia PROMEDIO del periodo:
# (existencia a fecha1 + existencia a fecha2) / 2
# ==============================================================
LET estadis.rotacion = estadis.consumo / estadis.existe
LET rotacion_anual = estadis.rotacion * (365 / dias1)
ELSE
LET estadis.rotacion = 0
LET rotacion_anual = 0
END IF
ELSE
LET estadis.dias = 0
LET estadis.rotacion = 0
LET rotacion_anual = 0
END IF
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
LET estadis.costo = 0
OUTPUT TO REPORT estadistica(estadis.*, dias1, c_dias, rotacion_anual)
LET idx = idx + 1
LET estadis.consumo = 0
LET estadis.existe = 0
LET estadis.dias = 0
LET estadis.rotacion = 0
# Refrescar progreso solo cada 50 filas: cada refresh es un
# round-trip al GDC y hacerlo por fila cuesta minutos.
IF idx MOD 50 = 0 THEN
LET progreso = (idx / cuenta_reg) * 100
DISPLAY progreso TO progreso
CALL ui.Interface.refresh()
END IF
END FOREACH
IF idx = 1 THEN
CALL fgl_winmessage(
"INFO", "NO EXISTEN INFORMACIONES CON ESTOS PARAMETROS", "INFO")
ELSE
LET progreso = 100
DISPLAY progreso TO progreso
FINISH REPORT estadistica
END IF
END FUNCTION
REPORT estadistica(x, dias2, c_dias, rot_anual)
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),
codCategoria INT,
categoria CHAR(30),
fecha_v, fecha_oc DATE,
tcantidad DEC(12, 4),
existe DEC(12, 4),
valor DECIMAL(12, 3),
costo DECIMAL(12, 4),
dias INTEGER,
pcodigo CHAR(25),
cantidad_orden,
pcantidadConduce,
transito,
existencia,
reservado,
disponible,
precio,
rotacion
DEC(12, 2)
END RECORD,
tcantidad DEC(12, 4),
rot_anual DEC(12, 2)
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),
cantidad_or DEC(12, 4)
FORMAT
FIRST PAGE HEADER
LET hora = TIME
LET fecha = TODAY
LET total_registros = 0
LET lj = (120 - LENGTH(p_companias.nombre CLIPPED)) / 2
PRINTX dias1
BEFORE GROUP OF x.cod_grupo
LET total_grupo = 0
LET hora = TIME
# OPTIMIZACION: se elimino el SELECT a companias por cada grupo;
# p_companias ya se carga una sola vez en MAIN.
ON EVERY ROW
LET total_registros = total_registros + 1
LET total_grupo = total_grupo + 1
LET nombre_ant = nombre
# OPTIMIZACION: fecha_v, fecha_oc y tcantidad ya vienen
# calculadas desde el query principal (LEFT JOIN agregado).
# Antes aqui se ejecutaban 3 SELECTs por cada producto.
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
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
IF m_meses > 0 THEN
LET conmes = x.consumo / m_meses
END IF
PRINTX x.cod_n,
x.cod_grupo,
x.cod_tipo,
x.cod_sec,
x.descrip_esp,
x.consumo,
x.existe,
x.dias,
x.rotacion,
rot_anual,
x.unidad_med,
fecha,
hora,
fecha1,
fecha2,
x.fecha_oc,
chorden,
x.tcantidad,
p_companias.*,
pcosto,
total_registros,
total_grupo,
x.fecha_v
END REPORT
{
================================================================================
NOTA DE OPTIMIZACION PENDIENTE - disponibles()
--------------------------------------------------------------------------------
El unico trabajo por-fila que queda es CALL disponibles() (en ipprgb000.4gl),
que probablemente ejecuta varios SELECT SUM sobre las tablas de movimientos por
cada producto. Para llevar el reporte de minutos a segundos, ese calculo tambien
debe convertirse a conjunto: un solo query con GROUP BY por
cod_n/cod_grupo/cod_tipo/cod_sec (o una tabla temporal #existencias) unido por
LEFT JOIN al query principal, igual que se hizo con ultima venta/compra.
================================================================================
}