1144 lines
38 KiB
Plaintext
1144 lines
38 KiB
Plaintext
IMPORT os
|
|
|
|
GLOBALS
|
|
"veprgb000.4gl"
|
|
DEFINE arr_ofertas DYNAMIC ARRAY OF RECORD
|
|
id int,
|
|
cod_n smallint,
|
|
cod_grupo smallint,
|
|
cod_tipo smallint,
|
|
cod_sec smallint,
|
|
descripcion VARCHAR(100),
|
|
unidad_med VARCHAR(4),
|
|
porc_desc DEC(12,2),
|
|
fecha_inicial,fecha_final DATE
|
|
END RECORD,
|
|
datos_c RECORD
|
|
documento INT,
|
|
fecha DATE,
|
|
descripcion_oferta VARCHAR(60),
|
|
porc_desc1 DEC(12,2),
|
|
fecha1 DATETIME YEAR TO MINUTE,
|
|
fecha2 DATETIME YEAR TO MINUTE
|
|
END RECORD
|
|
DEFINE xus_crea,xus_mod VARCHAR(50),
|
|
xfech_crea,xfech_mod DATETIME YEAR TO MINUTE,
|
|
adiciona BOOLEAN,
|
|
porc_Desc1 DEC(12,2),
|
|
no_registros INT
|
|
DEFINE arr_raw DYNAMIC ARRAY OF RECORD
|
|
c1,c2,c3,c4,c5,c6,c7,c8 STRING
|
|
END RECORD
|
|
DEFINE arr_fields DYNAMIC ARRAY OF STRING
|
|
DEFINE arr_shared DYNAMIC ARRAY OF STRING
|
|
DEFINE g_diag STRING
|
|
|
|
MAIN
|
|
|
|
|
|
DEFER INTERRUPT
|
|
CALL STARTLOG("veprmt023.log")
|
|
CALL ARG_VAL(1) RETURNING usuarios
|
|
CALL ARG_VAL(2) RETURNING clave
|
|
|
|
CONNECT TO "smarmotech" AS "MSSQL" USER usuarios USING clave
|
|
|
|
CALL veprmt023()
|
|
|
|
END MAIN
|
|
FUNCTION veprmt023()
|
|
OPEN WINDOW w1 AT 1,1 WITH FORM "vefmmt023"
|
|
|
|
MENU
|
|
ON ACTION nuevo
|
|
LET adiciona = TRUE
|
|
CALL veprad23()
|
|
ON ACTION modificar
|
|
LET adiciona = FALSE
|
|
CALL veprmf023()
|
|
ON ACTION salir
|
|
EXIT MENU
|
|
END MENU
|
|
|
|
CLOSE WINDOW w1
|
|
END FUNCTION
|
|
FUNCTION veprad23()
|
|
|
|
DIALOG ATTRIBUTES(UNBUFFERED, FIELD ORDER FORM)
|
|
INPUT BY NAME datos_c.*
|
|
BEFORE INPUT
|
|
|
|
LET datos_c.fecha = TODAY
|
|
DISPLAY BY NAME datos_c.fecha
|
|
NEXT FIELD descripcion_oferta
|
|
AFTER INPUT
|
|
IF datos_c.fecha2 < datos_c.fecha1 THEN
|
|
CALL fgl_winmessage("ERROR","FECHA FINAL NO PUEDE SER MENOR A LA INICIAL","INFO")
|
|
NEXT FIELD fecha1
|
|
END IF
|
|
END INPUT
|
|
CONSTRUCT criterio ON a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_sec,a.descrip_esp FROM
|
|
codn,codgrupo,codtipo,codsec,descrip_esp
|
|
ON ACTION ejecutabusqueda
|
|
CALL buscarCriterio()
|
|
AFTER CONSTRUCT
|
|
END CONSTRUCT
|
|
INPUT ARRAY arr_ofertas FROM sofertas.* ATTRIBUTE(WITHOUT DEFAULTS)
|
|
AFTER FIELD cod_sec
|
|
IF arr_ofertas[arr_curr()].cod_sec IS NOT NULL THEN
|
|
CALL busca_producto(arr_ofertas[arr_curr()].cod_n,arr_ofertas[arr_curr()].cod_grupo,
|
|
arr_ofertas[arr_curr()].cod_tipo,arr_ofertas[arr_curr()].cod_sec) RETURNING arr_ofertas[arr_curr()].descripcion
|
|
DISPLAY arr_ofertas[arr_curr()].descripcion TO sofertas[scr_line()].descripcion
|
|
END IF
|
|
ON ACTION guardar
|
|
IF fgl_winquestion("GUARDAR","ESTAS SEGURO DE GUARDAR ESTAS INFORMACIONES?","YES","YES|NO","QUESTION",0) THEN
|
|
CALL actualiza()
|
|
END IF
|
|
ON ACTION copiar
|
|
FOR idx = 1 TO arr_ofertas.getLength()
|
|
LET arr_ofertas[idx].fecha_inicial=datos_c.fecha1
|
|
LET arr_ofertas[idx].fecha_final=datos_c.fecha2
|
|
LET arr_ofertas[idx].porc_desc=datos_c.porc_desc1
|
|
END FOR
|
|
ON ACTION importar
|
|
CALL importar_hoja()
|
|
AFTER INPUT
|
|
IF INT_FLAG THEN
|
|
LET INT_FLAG = FALSE
|
|
CALL msg(2)
|
|
EXIT PROGRAM
|
|
END IF
|
|
FOR idx = 1 TO arr_ofertas.getLength()
|
|
IF arr_ofertas[idx].cod_sec IS NOT NULL THEN
|
|
IF arr_ofertas[idx].porc_desc IS NULL THEN
|
|
CALL msg(16)
|
|
NEXT FIELD porc_desc
|
|
END IF
|
|
IF arr_ofertas[idx].fecha_inicial IS NULL THEN
|
|
CALL msg(16)
|
|
NEXT FIELD fecha_inicial
|
|
END IF
|
|
IF arr_ofertas[idx].fecha_final IS NULL THEN
|
|
CALL msg(16)
|
|
NEXT FIELD fecha_final
|
|
END IF
|
|
|
|
|
|
END IF
|
|
END FOR
|
|
END INPUT
|
|
ON ACTION busca_productos
|
|
CONSTRUCT criterio ON a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_sec FROM cod_n,cod_grupo,cod_tipo,cod_sec
|
|
BEFORE CONSTRUCT
|
|
CALL arr_ofertas.clear()
|
|
END CONSTRUCT
|
|
|
|
LET selec = "SELECT a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_sec,a.descrip_esp,a.unidad_med FROM iptb00002 a WHERE a.status_t IS NULL and ",
|
|
criterio CLIPPED
|
|
|
|
PREPARE comando FROM selec
|
|
DECLARE busca CURSOR FOR comando
|
|
|
|
LET idx = 1
|
|
FOREACH busca INTO arr_ofertas[idx].cod_n,arr_ofertas[idx].cod_grupo,arr_ofertas[idx].cod_tipo,arr_ofertas[idx].cod_sec,
|
|
arr_ofertas[idx].descripcion,arr_ofertas[idx].unidad_med
|
|
LET arr_ofertas[idx].id=idx
|
|
LET idx = idx + 1
|
|
END FOREACH
|
|
LET no_registros=arr_ofertas.getLength()
|
|
DISPLAY BY NAME no_registros
|
|
ON ACTION CANCEL
|
|
EXIT PROGRAM
|
|
|
|
END DIALOG
|
|
END FUNCTION
|
|
|
|
FUNCTION veprmf023()
|
|
|
|
CONSTRUCT BY NAME criterio on a.documento,a.fecha,a.descripcion_oferta
|
|
ON ACTION CANCEL
|
|
LET INT_FLAG = FALSE
|
|
EXIT PROGRAM
|
|
END CONSTRUCT
|
|
|
|
LET selec = #"SELECT a.documento,CONVERT(CHAR(10),a.fecha,103),a.descripcion_oferta,' ',' ',' ',a.us_crea,a.fech_crea ,a.us_mod,a.fech_mod FROM vetb00032 a ",
|
|
"SELECT a.documento,a.fecha,a.descripcion_oferta FROM vetb00032 a ",
|
|
" WHERE ",criterio CLIPPED
|
|
|
|
PREPARE comando2 FROM selec
|
|
DECLARE busca_c SCROLL CURSOR FOR comando2
|
|
OPEN busca_c
|
|
|
|
FETCH FIRST busca_c INTO datos_c.*#,xus_crea,xfech_crea,xus_mod,xfech_mod
|
|
CALL desplega_info()
|
|
MENU
|
|
ON ACTION siguiente
|
|
FETCH NEXT busca_c INTO datos_c.*,xus_crea,xfech_crea,xus_mod,xfech_mod
|
|
CALL desplega_info()
|
|
|
|
ON ACTION anterior
|
|
FETCH previous busca_c INTO datos_c.*,xus_crea,xfech_crea,xus_mod,xfech_mod
|
|
CALL desplega_info()
|
|
|
|
|
|
ON ACTION primero
|
|
FETCH FIRST busca_c INTO datos_c.*,xus_crea,xfech_crea,xus_mod,xfech_mod
|
|
CALL desplega_info()
|
|
|
|
|
|
ON ACTION ultimo
|
|
FETCH LAST busca_c INTO datos_c.*,xus_crea,xfech_crea,xus_mod,xfech_mod
|
|
CALL desplega_info()
|
|
|
|
ON ACTION escoger
|
|
INPUT BY NAME datos_c.* WITHOUT DEFAULTS
|
|
BEFORE INPUT
|
|
|
|
DISPLAY BY NAME datos_c.fecha
|
|
NEXT FIELD descripcion_oferta
|
|
AFTER INPUT
|
|
IF datos_c.fecha2 < datos_c.fecha1 THEN
|
|
CALL fgl_winmessage("ERROR","FECHA FINAL NO PUEDE SER MENOR A LA INICIAL","INFO")
|
|
NEXT FIELD fecha1
|
|
END IF
|
|
END INPUT
|
|
|
|
INPUT ARRAY arr_ofertas FROM sofertas.* ATTRIBUTE(WITHOUT DEFAULTS)
|
|
ON ACTION copiar
|
|
FOR idx = 1 TO arr_ofertas.getLength()
|
|
LET arr_ofertas[idx].fecha_inicial=datos_c.fecha1
|
|
LET arr_ofertas[idx].fecha_final=datos_c.fecha2
|
|
LET arr_ofertas[idx].porc_desc=datos_c.porc_desc1
|
|
END FOR
|
|
|
|
AFTER FIELD cod_sec
|
|
IF arr_ofertas[arr_curr()].cod_sec IS NOT NULL THEN
|
|
CALL busca_producto(arr_ofertas[arr_curr()].cod_n,arr_ofertas[arr_curr()].cod_grupo,
|
|
arr_ofertas[arr_curr()].cod_tipo,arr_ofertas[arr_curr()].cod_sec) RETURNING arr_ofertas[arr_curr()].descripcion
|
|
DISPLAY arr_ofertas[arr_curr()].descripcion TO sofertas[scr_line()].descripcion
|
|
END IF
|
|
AFTER FIELD fecha_final
|
|
IF arr_ofertas[arr_curr()].cod_sec IS NOT NULL THEN
|
|
IF arr_ofertas[arr_curr()].fecha_final < arr_ofertas[arr_curr()].fecha_inicial THEN
|
|
CALL fgl_winmessage("INFO","FECHA FINAL NO PUEDE SER MENOR A LA INCIAL","INFO")
|
|
NEXT FIELD fecha_final
|
|
END IF
|
|
END IF
|
|
ON ACTION guardar ATTRIBUTE(TEXT="Salvar",image="save")
|
|
IF fgl_winquestion("GUARDAR","ESTAS SEGURO DE GUARDAR ESTAS INFORMACIONES?","YES","YES|NO","QUESTION",0) THEN
|
|
CALL actualiza()
|
|
END IF
|
|
AFTER INPUT
|
|
IF INT_FLAG THEN
|
|
LET INT_FLAG = FALSE
|
|
CALL msg(2)
|
|
EXIT PROGRAM
|
|
END IF
|
|
FOR idx = 1 TO arr_ofertas.getLength()
|
|
IF arr_ofertas[idx].cod_sec IS NOT NULL THEN
|
|
IF arr_ofertas[idx].porc_desc IS NULL THEN
|
|
CALL msg(16)
|
|
NEXT FIELD porc_desc
|
|
END IF
|
|
IF arr_ofertas[idx].fecha_inicial IS NULL THEN
|
|
CALL msg(16)
|
|
NEXT FIELD fecha_inicial
|
|
END IF
|
|
IF arr_ofertas[idx].fecha_final IS NULL THEN
|
|
CALL msg(16)
|
|
NEXT FIELD fecha_final
|
|
END IF
|
|
|
|
|
|
END IF
|
|
END FOR
|
|
END INPUT
|
|
|
|
|
|
ON ACTION salir
|
|
EXIT MENU
|
|
END MENU
|
|
END FUNCTION
|
|
FUNCTION actualiza()
|
|
DEFINE vfdesc DATE
|
|
|
|
FOR idx = 1 TO arr_ofertas.getLength()
|
|
IF arr_ofertas[idx].cod_sec IS NOT NULL THEN
|
|
IF arr_ofertas[idx].porc_desc IS NULL THEN
|
|
CALL msg(16)
|
|
RETURN
|
|
END IF
|
|
IF arr_ofertas[idx].fecha_inicial IS NULL THEN
|
|
CALL msg(16)
|
|
RETURN
|
|
END IF
|
|
IF arr_ofertas[idx].fecha_final IS NULL THEN
|
|
CALL msg(16)
|
|
RETURN
|
|
END IF
|
|
|
|
|
|
END IF
|
|
END FOR
|
|
BEGIN WORK
|
|
IF adiciona THEN
|
|
SELECT MAX(a.documento) INTO datos_c.documento FROM vetb00032 a
|
|
IF datos_c.documento IS NULL THEN
|
|
LET datos_c.documento = 0
|
|
END IF
|
|
LET datos_c.documento = datos_c.documento+1
|
|
|
|
# la fecha del documento no admite nulos (p.ej. carga por Excel)
|
|
IF datos_c.fecha IS NULL THEN
|
|
LET datos_c.fecha = TODAY
|
|
END IF
|
|
IF datos_c.descripcion_oferta IS NULL OR datos_c.descripcion_oferta = " " THEN
|
|
LET datos_c.descripcion_oferta = "OFERTAS CARGADAS DESDE EXCEL"
|
|
END IF
|
|
|
|
INSERT INTO vetb00032 (documento,fecha,descripcion_oferta,us_crea,fech_crea)
|
|
VALUES (datos_c.documento,datos_c.fecha,datos_c.descripcion_oferta,usuarios,getdate())
|
|
ELSE
|
|
|
|
DELETE FROM vetb00033 WHERE documento =datos_c.documento
|
|
|
|
UPDATE vetb00032 SET descripcion_oferta = datos_c.descripcion_oferta,
|
|
us_mod = usuarios,
|
|
fech_mod = getdate()
|
|
WHERE documento = datos_c.documento
|
|
END IF
|
|
FOR idx = 1 TO arr_ofertas.getLength()
|
|
IF arr_ofertas[idx].cod_Sec IS NOT NULL THEN
|
|
# 'id' es IDENTITY: NO se inserta (lo genera SQL Server).
|
|
# 'precio' es NOT NULL en vetb00033: se guarda 0 (esta carga es solo de descuentos).
|
|
# La relacion entre cabecera y detalle es por 'documento'.
|
|
INSERT INTO vetb00033 (documento,cod_n,cod_grupo,cod_tipo,cod_sec,porc_desc,fecha_inicial,fecha_final,precio)
|
|
VALUES (datos_c.documento,arr_ofertas[idx].cod_n,arr_ofertas[idx].cod_grupo,arr_ofertas[idx].cod_tipo,
|
|
arr_ofertas[idx].cod_sec,
|
|
arr_ofertas[idx].porc_desc,arr_ofertas[idx].fecha_inicial,arr_ofertas[idx].fecha_final,0)
|
|
|
|
# fecha_desc = fecha_final + 1 dia: el sistema evalua la oferta con
|
|
# "fecha < fecha_desc", asi el ultimo dia de la oferta queda incluido.
|
|
LET vfdesc = arr_ofertas[idx].fecha_final + 1
|
|
|
|
UPDATE iptb00002 SET porc_desc = arr_ofertas[idx].porc_desc,fecha_desc = vfdesc,us_mod = usuarios,fech_mod = getdate()
|
|
WHERE cod_n = arr_ofertas[idx].cod_n AND cod_grupo = arr_ofertas[idx].cod_grupo AND cod_tipo = arr_ofertas[idx].cod_tipo AND
|
|
cod_sec = arr_ofertas[idx].cod_sec
|
|
END IF
|
|
END FOR
|
|
COMMIT WORK
|
|
CALL fgl_winmessage("INFO","ACTUALIZACION EXITOSA","INFO")
|
|
CALL arr_ofertas.clear()
|
|
END FUNCTION
|
|
FUNCTION busca_producto(xcodn,xcodgrupo,xcodtipo,xcodsec)
|
|
DEFINE xcodn,xcodgrupo,xcodtipo,xcodsec SMALLINT,
|
|
descripcion VARCHAR(100)
|
|
SELECT a.descrip_esp INTO descripcion FROM iptb00002 a
|
|
WHERE a.cod_n = xcodn AND a.cod_grupo = xcodgrupo AND
|
|
a.cod_tipo = xcodtipo AND a.cod_sec = xcodsec AND a.status_t IS NULL
|
|
|
|
RETURN descripcion
|
|
END FUNCTION
|
|
FUNCTION desplega_info()
|
|
DISPLAY BY NAME datos_c.*
|
|
DISPLAY xus_crea TO us_crea
|
|
DISPLAY xus_mod TO us_mod
|
|
DISPLAY xfech_crea TO fech_crea
|
|
DISPLAY xfech_mod TO fech_mod
|
|
DECLARE busca_c1 CURSOR FOR
|
|
SELECT a.id,a.cod_n,a.cod_grupo,a.cod_tipo,a.cod_sec,b.descrip_esp,b.unidad_med,a.porc_desc,a.fecha_inicial,a.fecha_final
|
|
FROM vetb00033 a,iptb00002 b
|
|
WHERE a.documento = datos_c.documento AND
|
|
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
|
|
|
|
LET idx = 1
|
|
FOREACH busca_c1 INTO arr_ofertas[idx].id, arr_ofertas[idx].cod_n,arr_ofertas[idx].cod_grupo,arr_ofertas[idx].cod_tipo,arr_ofertas[idx].cod_sec,
|
|
arr_ofertas[idx].descripcion,arr_ofertas[idx].unidad_med,arr_ofertas[idx].porc_desc,arr_ofertas[idx].fecha_inicial,
|
|
arr_ofertas[idx].fecha_final
|
|
LET arr_ofertas[idx].id=idx
|
|
LET idx = idx + 1
|
|
END FOREACH
|
|
|
|
END FUNCTION
|
|
FUNCTION buscarCriterio()
|
|
|
|
LET selec = "SELECT '1',a.cod_n,a.cod_grupo, a.cod_tipo ,a.cod_sec,a.descrip_esp,a.unidad_med,a.porc_desc,
|
|
' ',a.fecha_desc
|
|
FROM iptb00002 a WHERE a.status_t IS NULL AND ",criterio CLIPPED
|
|
|
|
|
|
PREPARE comando FROM selec
|
|
DECLARE buscaProd CURSOR FOR comando
|
|
|
|
LET idx = 1
|
|
FOREACH buscaProd INTO arr_ofertas[idx].*
|
|
LET idx = idx + 1
|
|
END FOREACH
|
|
|
|
END FUNCTION
|
|
|
|
#=====================================================================
|
|
# CARGA MASIVA DE OFERTAS DESDE UNA HOJA (Excel .xlsx o .csv)
|
|
# --------------------------------------------------------------------
|
|
# El usuario sube el archivo, se lee al arreglo arr_ofertas y luego,
|
|
# al presionar GUARDAR, se ejecuta actualiza(), que crea el documento
|
|
# de oferta (vetb00032 / vetb00033) y actualiza iptb00002 en las
|
|
# columnas porc_desc y fecha_desc (fecha de termino).
|
|
#
|
|
# Columnas esperadas de la plantilla (en este orden):
|
|
# 1 cod_n 2 cod_grupo 3 cod_tipo 4 cod_sec
|
|
# 5 % descuento 6 fecha_inicial (opcional) 7 fecha_final (termino)
|
|
# 8 descripcion (informativa; el sistema la toma de la base)
|
|
#
|
|
# El % se interpreta como entero (20 = 20%). Si el valor viene <= 1
|
|
# (ej. 0.20 de las hojas anteriores) se multiplica x100 automaticamente.
|
|
#=====================================================================
|
|
FUNCTION importar_hoja()
|
|
DEFINE filepath, ext, mensaje STRING
|
|
DEFINE d_tipos, f_tipos STRING
|
|
DEFINE dir_dest, dir_tmp, base_tmp STRING
|
|
DEFINE ok SMALLINT
|
|
|
|
LET d_tipos = "Hojas de oferta"
|
|
LET f_tipos = "*.xlsx *.csv *.txt"
|
|
LET mensaje = "Elige la hoja de ofertas (Excel .xlsx o CSV)"
|
|
LET filepath = NULL
|
|
|
|
CALL ui.Interface.frontCall("standard","openfile",
|
|
[".", d_tipos, f_tipos, mensaje],[filepath])
|
|
|
|
IF filepath IS NULL OR filepath.getLength() = 0 THEN
|
|
RETURN
|
|
END IF
|
|
|
|
LET ext = downshift(os.Path.extension(filepath))
|
|
IF ext <> "xlsx" AND ext <> "csv" AND ext <> "txt" THEN
|
|
CALL fgl_winmessage("IMPORTAR",
|
|
"El archivo debe ser .xlsx o .csv","stop")
|
|
RETURN
|
|
END IF
|
|
|
|
# carpeta temporal en el servidor
|
|
LET base_tmp = fgl_getenv("TEMP")
|
|
IF base_tmp IS NULL OR base_tmp.getLength() = 0 THEN
|
|
LET base_tmp = fgl_getenv("DBTEMP")
|
|
END IF
|
|
IF base_tmp IS NULL OR base_tmp.getLength() = 0 THEN
|
|
LET base_tmp = "."
|
|
END IF
|
|
|
|
LET dir_dest = base_tmp CLIPPED, "/oferta_", usuarios CLIPPED, ".", ext
|
|
|
|
# trae el archivo del cliente (GDC) al servidor
|
|
CALL fgl_getfile(filepath, dir_dest)
|
|
|
|
CALL arr_raw.clear()
|
|
|
|
IF ext = "xlsx" THEN
|
|
LET dir_tmp = base_tmp CLIPPED, "/ofertaxlsx_", usuarios CLIPPED
|
|
LET ok = leer_xlsx(dir_dest, dir_tmp)
|
|
IF NOT ok THEN
|
|
LET mensaje = "No se pudo leer el .xlsx.\n", g_diag CLIPPED,
|
|
"\n\nComo alternativa, guarde la hoja como CSV y suba el .csv."
|
|
CALL fgl_winmessage("IMPORTAR", mensaje, "stop")
|
|
RETURN
|
|
END IF
|
|
ELSE
|
|
CALL leer_csv(dir_dest)
|
|
END IF
|
|
|
|
CALL procesar_raw()
|
|
|
|
# si se cargaron ofertas, pedir la descripcion del documento
|
|
IF no_registros > 0 THEN
|
|
CALL pedir_descripcion()
|
|
END IF
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Solicita la descripcion de la oferta para el documento (vetb00032)
|
|
#---------------------------------------------------------------------
|
|
FUNCTION pedir_descripcion()
|
|
DEFINE resp, defecto STRING
|
|
|
|
LET defecto = datos_c.descripcion_oferta
|
|
IF defecto IS NULL OR defecto.trim().getLength() = 0 THEN
|
|
LET defecto = "OFERTAS ", TODAY USING "dd/mm/yyyy"
|
|
END IF
|
|
|
|
LET resp = fgl_winprompt(0, 0, "Descripcion de la oferta:", defecto CLIPPED, 60, 0)
|
|
|
|
IF resp IS NULL OR resp.trim().getLength() = 0 THEN
|
|
LET datos_c.descripcion_oferta = defecto
|
|
ELSE
|
|
LET datos_c.descripcion_oferta = resp
|
|
END IF
|
|
DISPLAY BY NAME datos_c.descripcion_oferta
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Lectura de archivo CSV (separador , o ; autodetectado)
|
|
#---------------------------------------------------------------------
|
|
FUNCTION leer_csv(pfile)
|
|
DEFINE pfile STRING
|
|
DEFINE ch base.Channel
|
|
DEFINE linea, delim STRING
|
|
DEFINE first SMALLINT
|
|
DEFINE n INT
|
|
|
|
LET ch = base.Channel.create()
|
|
CALL ch.openFile(pfile, "r")
|
|
LET first = TRUE
|
|
LET delim = ","
|
|
|
|
WHILE NOT ch.isEof()
|
|
LET linea = ch.readLine()
|
|
IF linea IS NULL THEN EXIT WHILE END IF
|
|
LET linea = linea.trim()
|
|
IF linea.getLength() = 0 THEN CONTINUE WHILE END IF
|
|
IF first THEN
|
|
IF linea.getIndexOf(";",1) > 0 THEN
|
|
LET delim = ";"
|
|
ELSE
|
|
LET delim = ","
|
|
END IF
|
|
LET first = FALSE
|
|
END IF
|
|
CALL parse_csv_line(linea, delim)
|
|
LET n = arr_raw.getLength() + 1
|
|
LET arr_raw[n].c1 = celda(1)
|
|
LET arr_raw[n].c2 = celda(2)
|
|
LET arr_raw[n].c3 = celda(3)
|
|
LET arr_raw[n].c4 = celda(4)
|
|
LET arr_raw[n].c5 = celda(5)
|
|
LET arr_raw[n].c6 = celda(6)
|
|
LET arr_raw[n].c7 = celda(7)
|
|
LET arr_raw[n].c8 = celda(8)
|
|
# cuando el separador es ';' el decimal suele venir con coma
|
|
IF delim = ";" AND arr_raw[n].c5.getIndexOf(",",1) > 0 THEN
|
|
LET arr_raw[n].c5 = replace_str(arr_raw[n].c5, ",", ".")
|
|
END IF
|
|
END WHILE
|
|
CALL ch.close()
|
|
END FUNCTION
|
|
|
|
FUNCTION celda(pi)
|
|
DEFINE pi INT
|
|
IF pi >= 1 AND pi <= arr_fields.getLength() THEN
|
|
RETURN arr_fields[pi]
|
|
END IF
|
|
RETURN ""
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Separa una linea CSV en arr_fields, respetando comillas dobles
|
|
#---------------------------------------------------------------------
|
|
FUNCTION parse_csv_line(pline, pdelim)
|
|
DEFINE pline, pdelim STRING
|
|
DEFINE i, len INT
|
|
DEFINE c STRING
|
|
DEFINE inq SMALLINT
|
|
DEFINE sb base.StringBuffer
|
|
|
|
CALL arr_fields.clear()
|
|
LET sb = base.StringBuffer.create()
|
|
LET len = pline.getLength()
|
|
LET inq = FALSE
|
|
LET i = 1
|
|
|
|
WHILE i <= len
|
|
LET c = pline.getCharAt(i)
|
|
IF inq THEN
|
|
IF c = "\"" THEN
|
|
IF i < len AND pline.getCharAt(i+1) = "\"" THEN
|
|
CALL sb.append("\"")
|
|
LET i = i + 1
|
|
ELSE
|
|
LET inq = FALSE
|
|
END IF
|
|
ELSE
|
|
CALL sb.append(c)
|
|
END IF
|
|
ELSE
|
|
IF c = "\"" THEN
|
|
LET inq = TRUE
|
|
ELSE
|
|
IF c = pdelim THEN
|
|
CALL arr_fields.appendElement()
|
|
LET arr_fields[arr_fields.getLength()] = sb.toString()
|
|
CALL sb.clear()
|
|
ELSE
|
|
CALL sb.append(c)
|
|
END IF
|
|
END IF
|
|
END IF
|
|
LET i = i + 1
|
|
END WHILE
|
|
|
|
CALL arr_fields.appendElement()
|
|
LET arr_fields[arr_fields.getLength()] = sb.toString()
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Lectura de archivo .xlsx (es un ZIP): se descomprime con 'tar' y se
|
|
# leen sheet1.xml + sharedStrings.xml directamente. Devuelve TRUE/FALSE
|
|
#---------------------------------------------------------------------
|
|
FUNCTION leer_xlsx(pfile, pdir)
|
|
DEFINE pfile, pdir STRING
|
|
DEFINE cmd, psheet, pshared, plog STRING
|
|
DEFINE bs, winroot, psexe, tarexe STRING
|
|
DEFINE l_ok SMALLINT
|
|
|
|
LET g_diag = ""
|
|
|
|
# el archivo subido debe haber llegado al servidor
|
|
IF NOT os.Path.exists(pfile) THEN
|
|
LET g_diag = "El archivo no llego al servidor: ", pfile CLIPPED
|
|
RETURN FALSE
|
|
END IF
|
|
|
|
LET psheet = pdir CLIPPED, "/xl/worksheets/sheet1.xml"
|
|
LET pshared = pdir CLIPPED, "/xl/sharedStrings.xml"
|
|
LET plog = pdir CLIPPED, ".log"
|
|
|
|
# Ejecutables por RUTA COMPLETA: el entorno del runtime de Genero no
|
|
# incluye las carpetas de Windows en el PATH, por lo que 'powershell'
|
|
# y 'tar' a secas dan "is not recognized". (bs = backslash via ASCII 92,
|
|
# porque en fglcomp 3.20 el backslash es caracter de escape.)
|
|
LET bs = ASCII 92
|
|
LET winroot = fgl_getenv("SystemRoot")
|
|
IF winroot IS NULL OR winroot.getLength() = 0 THEN
|
|
LET winroot = "C:", bs, "Windows"
|
|
END IF
|
|
LET psexe = winroot CLIPPED, bs, "System32", bs, "WindowsPowerShell",
|
|
bs, "v1.0", bs, "powershell.exe"
|
|
LET tarexe = winroot CLIPPED, bs, "System32", bs, "tar.exe"
|
|
|
|
# borra el log de intentos anteriores
|
|
IF os.Path.exists(plog) THEN
|
|
LET l_ok = os.Path.delete(plog)
|
|
END IF
|
|
|
|
# 1er intento: PowerShell + .NET ZipFile (funciona en Windows Server 2012+).
|
|
# La ruta del .exe NO se encomilla (no tiene espacios); asi el comando no
|
|
# empieza con comilla y cmd.exe no elimina las comillas del -Command.
|
|
# La salida de error se captura en un log para poder diagnosticar.
|
|
LET cmd = psexe CLIPPED, " -NoProfile -ExecutionPolicy Bypass -Command \"",
|
|
"Remove-Item -Recurse -Force '", pdir CLIPPED, "' -ErrorAction SilentlyContinue; ",
|
|
"Add-Type -AssemblyName System.IO.Compression.FileSystem; ",
|
|
"[System.IO.Compression.ZipFile]::ExtractToDirectory('", pfile CLIPPED,
|
|
"','", pdir CLIPPED, "')\"",
|
|
" 2> \"", plog CLIPPED, "\""
|
|
RUN cmd
|
|
|
|
# 2do intento: tar (Windows 10 / Server 2019+)
|
|
IF NOT os.Path.exists(psheet) THEN
|
|
IF NOT os.Path.exists(pdir) THEN
|
|
LET l_ok = os.Path.mkdir(pdir)
|
|
END IF
|
|
LET cmd = tarexe CLIPPED, " -xf \"", pfile CLIPPED, "\" -C \"", pdir CLIPPED,
|
|
"\" 2>> \"", plog CLIPPED, "\""
|
|
RUN cmd
|
|
END IF
|
|
|
|
IF NOT os.Path.exists(psheet) THEN
|
|
IF os.Path.exists(plog) THEN
|
|
LET g_diag = "No se pudo descomprimir en el servidor.\nDetalle: ",
|
|
leer_archivo_texto(plog) CLIPPED
|
|
ELSE
|
|
LET g_diag = "No se pudo ejecutar la descompresion en el servidor ",
|
|
"(PowerShell/tar no disponibles para el usuario del servicio).\nCarpeta: ",
|
|
pdir CLIPPED
|
|
END IF
|
|
RETURN FALSE
|
|
END IF
|
|
|
|
CALL arr_shared.clear()
|
|
IF os.Path.exists(pshared) THEN
|
|
CALL leer_shared(pshared)
|
|
END IF
|
|
|
|
CALL leer_sheet(psheet)
|
|
RETURN TRUE
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Carga la tabla de cadenas compartidas (sharedStrings.xml)
|
|
#---------------------------------------------------------------------
|
|
FUNCTION leer_shared(pfile)
|
|
DEFINE pfile, content, seg, txt STRING
|
|
DEFINE a, b, pos INT
|
|
|
|
LET content = leer_archivo_texto(pfile)
|
|
LET pos = 1
|
|
WHILE TRUE
|
|
LET a = content.getIndexOf("<si>", pos)
|
|
IF a = 0 THEN EXIT WHILE END IF
|
|
LET b = content.getIndexOf("</si>", a)
|
|
IF b = 0 THEN EXIT WHILE END IF
|
|
LET seg = content.subString(a, b+4)
|
|
LET txt = extraer_textos_t(seg)
|
|
CALL arr_shared.appendElement()
|
|
LET arr_shared[arr_shared.getLength()] = txt
|
|
LET pos = b + 5
|
|
END WHILE
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Recorre las filas <row> de la hoja
|
|
#---------------------------------------------------------------------
|
|
FUNCTION leer_sheet(pfile)
|
|
DEFINE pfile, content, rowseg STRING
|
|
DEFINE rpos, ra, tagend, rb INT
|
|
|
|
LET content = leer_archivo_texto(pfile)
|
|
LET rpos = 1
|
|
WHILE TRUE
|
|
LET ra = content.getIndexOf("<row", rpos)
|
|
IF ra = 0 THEN EXIT WHILE END IF
|
|
LET tagend = content.getIndexOf(">", ra)
|
|
IF tagend = 0 THEN EXIT WHILE END IF
|
|
IF content.getCharAt(tagend-1) = "/" THEN
|
|
LET rpos = tagend + 1
|
|
CONTINUE WHILE
|
|
END IF
|
|
LET rb = content.getIndexOf("</row>", tagend)
|
|
IF rb = 0 THEN EXIT WHILE END IF
|
|
IF rb-1 >= tagend+1 THEN
|
|
LET rowseg = content.subString(tagend+1, rb-1)
|
|
ELSE
|
|
LET rowseg = ""
|
|
END IF
|
|
CALL procesar_row(rowseg)
|
|
LET rpos = rb + 6
|
|
END WHILE
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Procesa las celdas <c> de una fila y las coloca en arr_raw
|
|
#---------------------------------------------------------------------
|
|
FUNCTION procesar_row(pinner)
|
|
DEFINE pinner STRING
|
|
DEFINE cpos, ca, tagend, ce, colidx, n INT
|
|
DEFINE opentag, inner, letras, tp, val STRING
|
|
|
|
LET n = arr_raw.getLength() + 1
|
|
LET arr_raw[n].c1 = "" LET arr_raw[n].c2 = ""
|
|
LET arr_raw[n].c3 = "" LET arr_raw[n].c4 = ""
|
|
LET arr_raw[n].c5 = "" LET arr_raw[n].c6 = ""
|
|
LET arr_raw[n].c7 = "" LET arr_raw[n].c8 = ""
|
|
|
|
LET cpos = 1
|
|
WHILE TRUE
|
|
LET ca = pinner.getIndexOf("<c", cpos)
|
|
IF ca = 0 THEN EXIT WHILE END IF
|
|
LET tagend = pinner.getIndexOf(">", ca)
|
|
IF tagend = 0 THEN EXIT WHILE END IF
|
|
LET opentag = pinner.subString(ca, tagend)
|
|
IF pinner.getCharAt(tagend-1) = "/" THEN
|
|
LET inner = ""
|
|
LET cpos = tagend + 1
|
|
ELSE
|
|
LET ce = pinner.getIndexOf("</c>", tagend)
|
|
IF ce = 0 THEN EXIT WHILE END IF
|
|
IF ce-1 >= tagend+1 THEN
|
|
LET inner = pinner.subString(tagend+1, ce-1)
|
|
ELSE
|
|
LET inner = ""
|
|
END IF
|
|
LET cpos = ce + 4
|
|
END IF
|
|
|
|
LET letras = solo_letras(attr_val(opentag, "r"))
|
|
LET colidx = letras_a_indice(letras)
|
|
LET tp = attr_val(opentag, "t")
|
|
LET val = valor_celda(inner, tp)
|
|
|
|
CASE colidx
|
|
WHEN 1 LET arr_raw[n].c1 = val
|
|
WHEN 2 LET arr_raw[n].c2 = val
|
|
WHEN 3 LET arr_raw[n].c3 = val
|
|
WHEN 4 LET arr_raw[n].c4 = val
|
|
WHEN 5 LET arr_raw[n].c5 = val
|
|
WHEN 6 LET arr_raw[n].c6 = val
|
|
WHEN 7 LET arr_raw[n].c7 = val
|
|
WHEN 8 LET arr_raw[n].c8 = val
|
|
END CASE
|
|
END WHILE
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Devuelve el valor de un atributo (nombre="valor") de una etiqueta
|
|
#---------------------------------------------------------------------
|
|
FUNCTION attr_val(ptag, pname)
|
|
DEFINE ptag, pname, key STRING
|
|
DEFINE a, b INT
|
|
|
|
LET key = pname CLIPPED, "=\""
|
|
LET a = ptag.getIndexOf(key, 1)
|
|
IF a = 0 THEN RETURN "" END IF
|
|
LET a = a + key.getLength()
|
|
LET b = ptag.getIndexOf("\"", a)
|
|
IF b = 0 OR b-1 < a THEN RETURN "" END IF
|
|
RETURN ptag.subString(a, b-1)
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Deja solo las letras iniciales de una referencia de celda (A12 -> A)
|
|
#---------------------------------------------------------------------
|
|
FUNCTION solo_letras(pref)
|
|
DEFINE pref, res, c STRING
|
|
DEFINE i INT
|
|
|
|
LET res = ""
|
|
FOR i = 1 TO pref.getLength()
|
|
LET c = pref.getCharAt(i)
|
|
IF (c >= "A" AND c <= "Z") OR (c >= "a" AND c <= "z") THEN
|
|
LET res = res CLIPPED, c
|
|
ELSE
|
|
EXIT FOR
|
|
END IF
|
|
END FOR
|
|
RETURN res
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Convierte letras de columna a indice (A=1, B=2, ... AA=27)
|
|
#---------------------------------------------------------------------
|
|
FUNCTION letras_a_indice(pletras)
|
|
DEFINE pletras, c, abc STRING
|
|
DEFINE i, idxc, res INT
|
|
|
|
LET abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
LET res = 0
|
|
LET pletras = upshift(pletras)
|
|
FOR i = 1 TO pletras.getLength()
|
|
LET c = pletras.getCharAt(i)
|
|
LET idxc = abc.getIndexOf(c, 1)
|
|
IF idxc = 0 THEN RETURN 0 END IF
|
|
LET res = res * 26 + idxc
|
|
END FOR
|
|
RETURN res
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Obtiene el valor textual de una celda (resuelve cadenas compartidas)
|
|
#---------------------------------------------------------------------
|
|
FUNCTION valor_celda(pinner, ptp)
|
|
DEFINE pinner, ptp, raw STRING
|
|
DEFINE a, gt, b, sidx INT
|
|
|
|
IF ptp = "inlineStr" THEN
|
|
RETURN extraer_textos_t(pinner)
|
|
END IF
|
|
|
|
LET a = pinner.getIndexOf("<v", 1)
|
|
IF a = 0 THEN RETURN "" END IF
|
|
LET gt = pinner.getIndexOf(">", a)
|
|
IF gt = 0 THEN RETURN "" END IF
|
|
LET b = pinner.getIndexOf("</v>", gt)
|
|
IF b = 0 OR b-1 < gt+1 THEN RETURN "" END IF
|
|
LET raw = xml_unescape(pinner.subString(gt+1, b-1))
|
|
|
|
IF ptp = "s" THEN
|
|
LET sidx = raw
|
|
IF sidx+1 >= 1 AND sidx+1 <= arr_shared.getLength() THEN
|
|
RETURN arr_shared[sidx+1]
|
|
END IF
|
|
RETURN ""
|
|
END IF
|
|
RETURN raw
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Extrae y concatena el texto de todos los nodos <t> de un segmento
|
|
#---------------------------------------------------------------------
|
|
FUNCTION extraer_textos_t(pseg)
|
|
DEFINE pseg, inner STRING
|
|
DEFINE sb base.StringBuffer
|
|
DEFINE pos, a, gt, b INT
|
|
|
|
LET sb = base.StringBuffer.create()
|
|
LET pos = 1
|
|
WHILE TRUE
|
|
LET a = pseg.getIndexOf("<t", pos)
|
|
IF a = 0 THEN EXIT WHILE END IF
|
|
LET gt = pseg.getIndexOf(">", a)
|
|
IF gt = 0 THEN EXIT WHILE END IF
|
|
IF pseg.getCharAt(gt-1) = "/" THEN
|
|
LET pos = gt + 1
|
|
CONTINUE WHILE
|
|
END IF
|
|
LET b = pseg.getIndexOf("</t>", gt)
|
|
IF b = 0 THEN EXIT WHILE END IF
|
|
IF b-1 >= gt+1 THEN
|
|
LET inner = pseg.subString(gt+1, b-1)
|
|
ELSE
|
|
LET inner = ""
|
|
END IF
|
|
CALL sb.append(xml_unescape(inner))
|
|
LET pos = b + 4
|
|
END WHILE
|
|
RETURN sb.toString()
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Desescapa entidades XML basicas
|
|
#---------------------------------------------------------------------
|
|
FUNCTION xml_unescape(ps)
|
|
DEFINE ps, s STRING
|
|
LET s = ps
|
|
LET s = replace_str(s, "<", "<")
|
|
LET s = replace_str(s, ">", ">")
|
|
LET s = replace_str(s, """, "\"")
|
|
LET s = replace_str(s, "'", "'")
|
|
LET s = replace_str(s, "&", "&")
|
|
RETURN s
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Reemplaza todas las ocurrencias de una subcadena
|
|
#---------------------------------------------------------------------
|
|
FUNCTION replace_str(ps, po, pn)
|
|
DEFINE ps, po, pn STRING
|
|
DEFINE sb base.StringBuffer
|
|
LET sb = base.StringBuffer.create()
|
|
CALL sb.append(ps)
|
|
CALL sb.replace(po, pn, 0)
|
|
RETURN sb.toString()
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Lee un archivo de texto completo a una cadena
|
|
#---------------------------------------------------------------------
|
|
FUNCTION leer_archivo_texto(pfile)
|
|
DEFINE pfile, linea STRING
|
|
DEFINE ch base.Channel
|
|
DEFINE sb base.StringBuffer
|
|
|
|
LET sb = base.StringBuffer.create()
|
|
LET ch = base.Channel.create()
|
|
CALL ch.openFile(pfile, "r")
|
|
WHILE NOT ch.isEof()
|
|
LET linea = ch.readLine()
|
|
IF linea IS NULL THEN EXIT WHILE END IF
|
|
CALL sb.append(linea)
|
|
CALL sb.append(" ")
|
|
END WHILE
|
|
CALL ch.close()
|
|
RETURN sb.toString()
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Valida / normaliza arr_raw y llena arr_ofertas
|
|
#---------------------------------------------------------------------
|
|
FUNCTION procesar_raw()
|
|
DEFINE i, k, errores, sinprod, sinfecha INT
|
|
DEFINE vcodn, vcodgrupo, vcodtipo, vcodsec INT
|
|
DEFINE vporc DEC(12,2)
|
|
DEFINE vfini, vffin DATE
|
|
DEFINE vdesc VARCHAR(100)
|
|
DEFINE vunidad VARCHAR(4)
|
|
DEFINE msgtxt STRING
|
|
|
|
CALL arr_ofertas.clear()
|
|
LET k = 0
|
|
LET errores = 0
|
|
LET sinprod = 0
|
|
LET sinfecha = 0
|
|
|
|
FOR i = 1 TO arr_raw.getLength()
|
|
# salta encabezados / titulos / filas sin codigos validos
|
|
IF NOT es_entero(arr_raw[i].c1) OR NOT es_entero(arr_raw[i].c2)
|
|
OR NOT es_entero(arr_raw[i].c3) OR NOT es_entero(arr_raw[i].c4) THEN
|
|
CONTINUE FOR
|
|
END IF
|
|
|
|
LET vcodn = arr_raw[i].c1
|
|
LET vcodgrupo = arr_raw[i].c2
|
|
LET vcodtipo = arr_raw[i].c3
|
|
LET vcodsec = arr_raw[i].c4
|
|
|
|
# el producto debe existir y estar activo
|
|
LET vdesc = NULL
|
|
LET vunidad = NULL
|
|
SELECT a.descrip_esp, a.unidad_med INTO vdesc, vunidad
|
|
FROM iptb00002 a
|
|
WHERE a.cod_n = vcodn AND a.cod_grupo = vcodgrupo
|
|
AND a.cod_tipo = vcodtipo AND a.cod_sec = vcodsec
|
|
AND a.status_t IS NULL
|
|
IF vdesc IS NULL THEN
|
|
LET sinprod = sinprod + 1
|
|
LET errores = errores + 1
|
|
CONTINUE FOR
|
|
END IF
|
|
|
|
# porciento de descuento
|
|
LET vporc = normaliza_porc(arr_raw[i].c5)
|
|
IF vporc IS NULL OR vporc <= 0 THEN
|
|
LET errores = errores + 1
|
|
CONTINUE FOR
|
|
END IF
|
|
|
|
# fechas
|
|
LET vfini = parse_fecha(arr_raw[i].c6)
|
|
IF vfini IS NULL THEN
|
|
LET vfini = TODAY
|
|
END IF
|
|
LET vffin = parse_fecha(arr_raw[i].c7)
|
|
IF vffin IS NULL THEN
|
|
LET sinfecha = sinfecha + 1
|
|
LET errores = errores + 1
|
|
CONTINUE FOR
|
|
END IF
|
|
IF vffin < vfini THEN
|
|
LET errores = errores + 1
|
|
CONTINUE FOR
|
|
END IF
|
|
|
|
LET k = k + 1
|
|
LET arr_ofertas[k].id = k
|
|
LET arr_ofertas[k].cod_n = vcodn
|
|
LET arr_ofertas[k].cod_grupo = vcodgrupo
|
|
LET arr_ofertas[k].cod_tipo = vcodtipo
|
|
LET arr_ofertas[k].cod_sec = vcodsec
|
|
LET arr_ofertas[k].descripcion = vdesc
|
|
LET arr_ofertas[k].unidad_med = vunidad
|
|
LET arr_ofertas[k].porc_desc = vporc
|
|
LET arr_ofertas[k].fecha_inicial = vfini
|
|
LET arr_ofertas[k].fecha_final = vffin
|
|
END FOR
|
|
|
|
LET no_registros = arr_ofertas.getLength()
|
|
DISPLAY BY NAME no_registros
|
|
|
|
# el documento requiere fecha (NOT NULL); si el flujo de carga no la fijo,
|
|
# se asume la fecha de hoy y se muestra al usuario
|
|
IF datos_c.fecha IS NULL THEN
|
|
LET datos_c.fecha = TODAY
|
|
DISPLAY BY NAME datos_c.fecha
|
|
END IF
|
|
|
|
LET msgtxt = "Se cargaron ", k USING "<<<<&", " ofertas."
|
|
IF errores > 0 THEN
|
|
LET msgtxt = msgtxt CLIPPED, "\n", errores USING "<<<<&",
|
|
" filas omitidas (", sinprod USING "<<<<&",
|
|
" sin producto activo, ", sinfecha USING "<<<<&",
|
|
" sin fecha final)."
|
|
END IF
|
|
IF k = 0 THEN
|
|
CALL fgl_winmessage("IMPORTAR", msgtxt, "exclamation")
|
|
ELSE
|
|
LET msgtxt = msgtxt CLIPPED,
|
|
"\nRevise el detalle y presione GUARDAR para aplicar."
|
|
CALL fgl_winmessage("IMPORTAR", msgtxt, "information")
|
|
END IF
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Normaliza el % de descuento: valores <= 1 se toman como fraccion x100
|
|
#---------------------------------------------------------------------
|
|
FUNCTION normaliza_porc(ps)
|
|
DEFINE ps, s STRING
|
|
DEFINE v DEC(12,2)
|
|
|
|
LET s = ps.trim()
|
|
IF s.getLength() = 0 THEN RETURN NULL END IF
|
|
IF NOT es_numero(s) THEN RETURN NULL END IF
|
|
LET v = s
|
|
IF v > 0 AND v < 1 THEN
|
|
LET v = v * 100
|
|
END IF
|
|
RETURN v
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# Interpreta una fecha: serial de Excel, AAAA-MM-DD o DD/MM/AAAA
|
|
#---------------------------------------------------------------------
|
|
FUNCTION parse_fecha(ps)
|
|
DEFINE ps, s, p1, p2, p3 STRING
|
|
DEFINE serial, y, m, d, cnt INT
|
|
DEFINE tok base.StringTokenizer
|
|
|
|
LET s = ps.trim()
|
|
IF s.getLength() = 0 THEN RETURN NULL END IF
|
|
|
|
# numero serial de Excel (dias desde 1899-12-30)
|
|
IF es_numero(s) THEN
|
|
IF s.getIndexOf(".",1) > 0 THEN
|
|
LET s = s.subString(1, s.getIndexOf(".",1)-1)
|
|
END IF
|
|
IF s.getLength() = 0 THEN RETURN NULL END IF
|
|
LET serial = s
|
|
IF serial <= 0 THEN RETURN NULL END IF
|
|
RETURN MDY(12,30,1899) + serial
|
|
END IF
|
|
|
|
LET tok = base.StringTokenizer.create(s, "-/.")
|
|
LET cnt = 0
|
|
IF tok.hasMoreTokens() THEN LET p1 = tok.nextToken() LET cnt = cnt + 1 END IF
|
|
IF tok.hasMoreTokens() THEN LET p2 = tok.nextToken() LET cnt = cnt + 1 END IF
|
|
IF tok.hasMoreTokens() THEN LET p3 = tok.nextToken() LET cnt = cnt + 1 END IF
|
|
IF cnt < 3 THEN RETURN NULL END IF
|
|
IF NOT es_entero(p1) OR NOT es_entero(p2) OR NOT es_entero(p3) THEN
|
|
RETURN NULL
|
|
END IF
|
|
|
|
IF p1.getLength() = 4 THEN # ISO AAAA-MM-DD
|
|
LET y = p1 LET m = p2 LET d = p3
|
|
ELSE # R.D. DD/MM/AAAA
|
|
LET d = p1 LET m = p2 LET y = p3
|
|
IF p3.getLength() = 2 THEN
|
|
LET y = 2000 + y
|
|
END IF
|
|
END IF
|
|
|
|
# tolera hojas guardadas en formato MM/DD/AAAA
|
|
IF m > 12 AND d <= 12 THEN
|
|
LET cnt = m LET m = d LET d = cnt
|
|
END IF
|
|
|
|
IF m < 1 OR m > 12 OR d < 1 OR d > 31 THEN RETURN NULL END IF
|
|
RETURN MDY(m, d, y)
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# TRUE si la cadena contiene solo digitos (entero no negativo)
|
|
#---------------------------------------------------------------------
|
|
FUNCTION es_entero(ps)
|
|
DEFINE ps, s, c STRING
|
|
DEFINE i INT
|
|
|
|
LET s = ps.trim()
|
|
IF s.getLength() = 0 THEN RETURN FALSE END IF
|
|
FOR i = 1 TO s.getLength()
|
|
LET c = s.getCharAt(i)
|
|
IF c < "0" OR c > "9" THEN RETURN FALSE END IF
|
|
END FOR
|
|
RETURN TRUE
|
|
END FUNCTION
|
|
|
|
#---------------------------------------------------------------------
|
|
# TRUE si la cadena es un numero (digitos y a lo sumo un punto)
|
|
#---------------------------------------------------------------------
|
|
FUNCTION es_numero(ps)
|
|
DEFINE ps, s, c STRING
|
|
DEFINE i, puntos INT
|
|
|
|
LET s = ps.trim()
|
|
IF s.getLength() = 0 THEN RETURN FALSE END IF
|
|
LET puntos = 0
|
|
FOR i = 1 TO s.getLength()
|
|
LET c = s.getCharAt(i)
|
|
IF c = "." THEN
|
|
LET puntos = puntos + 1
|
|
IF puntos > 1 THEN RETURN FALSE END IF
|
|
ELSE
|
|
IF c < "0" OR c > "9" THEN RETURN FALSE END IF
|
|
END IF
|
|
END FOR
|
|
RETURN TRUE
|
|
END FUNCTION
|
|
|