Commit inicial: fuentes MBS ERP (Genero 6) + .gitignore + docs/SETUP_PC_MBS.md
This commit is contained in:
@@ -0,0 +1,521 @@
|
||||
{
|
||||
-------------------------------------------------------------------------------
|
||||
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.
|
||||
================================================================================
|
||||
}
|
||||
Reference in New Issue
Block a user