jueves, 26 de febrero de 2026

Acceder a servicios REST FULL API desde Visual Foxpro 9.0

 Hola a tod@s, espero poder regresar más seguido por este blog; hoy les traigo otro reto.

El requerimiento es como sigue, existen servicios de internet que se pueden consumir desde su app de Visual FoxPro, estos son del tipo REST FULL API, que aceptan métodos de llamada del tipo: GET, POST, PUT

El Get es para recuperar información (es decir devolverá tipicamente un JSON con los datos solicitados).

El POST se usa para actualizar información que nosotros le enviamos.

Para usar el GET existen varias librerias, yo utilizo la del amigo Victor Espina, y la idea es recuperar información de una tabla basado en un rango de fechas, el rango esta en 2 variables digamos ldFec1 la fecha inicial y ldFec2 la fecha final.

Se construye la cadena a enviar de la siguiente manera

SET DATE TO ANSI

lcFec1 = DTOC(ldFec1)

lcFec1 = STRTRAN(lcFec1,'.','-')

lcFec2 = DTOC(ldFec2)

lcFec2 = STRTRAN(lcFec2,'.','-')

SET DATE TO YMD 

lcURL = "https://script.google.com/macros/s/AKfycbxeKrqo2sk-xjPaUX7LXtjMJ/exec?fromDate="

lcRango=lcFec1+"&toDate="+lcFec2

SET PROCEDURE TO qdfoxjson additive
JSONStart()
cJSON = GETURL(lcURL+lcRango)
oDatos = json.Parse(cJSON)

y listo, si el estado devuelve 200 hubo exito, en caso contrario algo salió mal
IF oDatos.meta.status#200
=MESSAGEBOX('La busqueda tuvo problemas',64)
oDatos = .Null.
RETURN 0
ENDIF

Luego podemos iterar el objeto

lnProcesados = 0
FOR J = 1 TO lnRegistros
   mMess="Procesando registros: "+TRANSFORM(J,'99,999')+"/"+Alltrim(TRANSFORM(lnRegistros,'99,999'))
   Wait mMess Window At Srows()/2,(Scols()/2 - (Len(mMess)/2)) NOWAIT

m.Detalles = oDatos.Result.Item(J).Conceptos.Count
m.Cliente = ALLTRIM(oDatos.Result.Item(J).Codigo)
...
...
...
endfor 

Y eso es todo en cuanto a recuperar información, ahora para enviar información y actualizar datos en la web, se usa el POST (hay que mencionar que ambos necesitan un EndPoint). Lo primero es construir la cadena JSON a enviar con información de los clientes, el endPoint requiere la siguiente estructura:

El body del request debe contener esta información por el momento:

Ejemplo:
{
    "meta": {
        "endpoint": "v1/reemplazar-actualizar-todos-los-clientes"
    },
    "payload": {
        "clientes": [
            {
                "codigo": "SVexamp",
                "nombreOrRazonSocial": "Example S.A de C.V",
                "email": "example@example.example",
                "zona": "CENTRO",
                "vendedorId": 5,
                "activo": 1,
                "limiteCartera": 1000,
                "saldoDebe": 0
            },
        ]
    }
}

Nuestro código se verá así

CLEAR
TEXT TO C1 NOSHOW 
{"meta": {"endpoint": "v1/reemplazar-actualizar-todos-los-clientes"}, "payload": {"clientes": [
ENDTEXT
CLOSE DATABASES ALL
USE Clientes ORDER Codigo
lcCuerpo = ''
llPrimero = .T.
GO TOP
SCAN FOR RECNO()<10
IF llPrimero
lcCuerpo = lcCuerpo + [{"codigo": "] + ALLTRIM(Clientes.Clie_Cod) + [",] + ;
["nombreOrRazonSocial": "] + ALLTRIM(Clientes.Clie_nom) + [",] + ;
["email": "] + ALLTRIM(Clientes.Clie_Email) + [",] + ;
["zona": "] + ALLTRIM(Clientes.Zona) + [",] + ;
["vendedorId":] + ALLTRIM(STR(Clientes.Clie_Vend)) + [,] + ;
["activo": 1,] + ;
["limiteCartera":] + ALLTRIM(STR(Clientes.clie_limca)) +[,] + ;
["saldoDebe":]+ALLTRIM(TRANSFORM(Clientes.Clie_Saldo,'999999.99')) + "}"
llPrimero = .F.
ELSE
lcCuerpo = lcCuerpo + [,{"codigo": "] + ALLTRIM(Clientes.Clie_Cod) + [",] + ;
["nombreOrRazonSocial": "] + ALLTRIM(Clientes.Clie_nom) + [",] + ;
["email": "] + ALLTRIM(Clientes.Clie_Email) + [",] + ;
["zona": "] + ALLTRIM(Clientes.Zona) + [",] + ;
["vendedorId":] + ALLTRIM(STR(Clientes.Clie_Vend)) + [,] + ;
["activo": 1,] + ;
["limiteCartera":] + ALLTRIM(STR(Clientes.clie_limca)) +[,] + ;
["saldoDebe":]+ALLTRIM(TRANSFORM(Clientes.Clie_Saldo,'999999.99')) + "}"
ENDIF
ENDSCAN
TEXT TO C2 NOSHOW 
]}}
ENDTEXT
cJson = C1 + lcCuerpo + C2
****

* Luego creamos el objeto para utilizar

LOCAL xmlHttp, result, lcUrl, lcJsonData

* 1. Instantiate the ServerXMLHTTP object
* Use version 6.0 for best compatibility, fall back to 3.0 if needed
TRY
    xmlHttp = CREATEOBJECT("Msxml2.ServerXMLHTTP.6.0")
CATCH
    xmlHttp = CREATEOBJECT("MSXML2.XMLHTTP.3.0")
ENDTRY

* 2. Define the target URL
lcUrl = "http://your-api-endpoint.com"
lcURL = "https://script.google.com/macros/s/AKfycbxjWfYQuGA3vh4oWsQTlLifmwFzWyxXe/exec"

* 3. Prepare the data to send (e.g., a JSON string)
* You would construct this string based on your API's requirements
lcJsonData = cJson  && '{ "key1": "value1", "key2": 123 }'

* 4. Open the connection and specify the method (POST)
* The third parameter (null or .F.) means the request is synchronous
xmlHttp.open("POST", lcUrl, .F.)

* 5. Set request headers (e.g., Content-Type for JSON data)
xmlHttp.setRequestHeader("Content-Type", "application/json")

* You might also need to set other headers like API keys or authorization tokens:
* xmlHttp.setRequestHeader("Authorization", "Bearer your_token_here")

* 6. Send the request with the data in the body
xmlHttp.send(lcJsonData)

* 7. (Optional but important in a PRG) Wait for the response
* This is essential within a program to ensure the response is received before processing
DO WHILE xmlHttp.readyState != 4 && 4 means "complete"
    * Wait or process other events while waiting
ENDDO

* 8. Retrieve the response
result = xmlHttp.responseText
? "Response Status: " + TRANSFORM(xmlHttp.status)
? "Response Text:"
? result

y eso es todo por ahora, espero les sirva el código que por cierto lo generó la IA de Google, saludos cordiales a tod@s y en especial a mi sobrina Trini que estuvo de cumpleaños hace unos días.

Pensar es gratis.

martes, 20 de enero de 2026

Como elimnar archivos y carpetas que tengan más de 48 horas de ser creados

 

Hola a tod@s, hoy les traigo algo simple pero que resuelve algunos problemas de higiene de sistemas de archivos.

El requerimiento es como sigue : existe una carpeta llamada “Estados de cuenta”, el nombre es suficientemente descriptivo, el caso es que los usuarios generan allí archivos en pdf que se almacenan en subcarpetas que llevan como parte del nombre la fecha del día en que son generados, pero que luego de algún tiempo pierden validez pues los datos ya no son representativos del presente, ese período suele ser de 2 días (48 horas) luego de lo cual se convierten en “basura” y deben ser eliminados, aparte hay aplicaciones que utilizan archivos temporales que se guardan en la carpeta c:\temporal  otro nombre muy sugerente y por alguna razón que desconozco los creadores de dicha aplicación no eliminan su basura, que queda en forma de archivos que comienzan con la cadena de caracteres _7  luego sigue una serie de 7 caracteres y la terminación puede ser 7z, dbf, pdf, xls, xlsx.

Su misión si deciden aceptarla es crear un proceso automático para eliminar dichos archivos.

Lo más simple es crear un proceso por lotes que ejecute comandos para realizar dicha tarea y luego programar una tarea automática en el servidor para que se ejecute todos los días a una hora fija.

Lo de crear la tarea se hace con el task scheduler



Y no entraré en ese punto, así que vamos al procesamiento por lotes

 Creamos un archivo llamado “Borra_Archivosycarpetas.bat”  si ya sé que los nombres son sugerentes, pero esa es la idea, recordar que demonios hace un batch que creamos hace varios años.

 cls

@echo off

rem        *Establecer la Ruta de todos los usuarios

rem        ** Primero se borran los archivos de todas las subcarpetas

ForFiles /P "C:\Estados de cuenta" /S /D -3 /C "cmd /c del /q @path"

El parámetro /P dice que lo que viene a continuación es una ruta con la cual se va a trabajar, luego el parámetro /S le indica al sistema operativo que incluya las subcarpetas que existan, el parámetro /D le indica al sistema operativo que debe tomar únicamente aquellos archivos que tengan una antigüedad con el siguiente parámetro (-3) hace 3 días, osea, los que tengan más de 48 horas, el parámetro /C le indica que lo que viene a continuación es un comando  (del) que en efecto borra todo lo que esté en la ruta @Path que se fijó al principio con /P y finalmente el parámetro /q que indica que no pregunte si estás seguro que lo haga en modo silencioso.

 

rem        ** Luego se borran todas las subcarpetas

forfiles /s /d -3 /p "C:\Estados de cuenta" /c "cmd /c IF @isdir == TRUE rd /s /q @path"

Aquí la única diferencia es que se hace la pregunta si el archivo tratado es una carpeta @isdir, en cuyo caso se ejecuta el commando rd  (remove directory), que en efecto borra las subcarpetas que existan.

Si quisiéramos borrar carpetas que se encuentran en el escritorio de algún usuario en particular, la sintaxis sería como la siguiente, ojo que aquí se usan una variable llamada RUTA que se le asigna un valor con el comando SET; y que para utilizarla la ponemos entre signos de porcentaje %RUTA%

SET RUTA=C:\Users\TELEFONICO\Desktop\Estados de Cuenta

ForFiles /P "%RUTA%" /S /D -3 /C "cmd /c del /q @path"

rem        ** Luego se borran todas las subcarpetas con 3 o mas dias de antiguedad

forfiles /s /d -3 /p "%RUTA%" /c "cmd /c IF @isdir == TRUE rd /s /q @path"

 

SET RUTA=C:\Users\CENTRO\Desktop\Estados de Cuenta

ForFiles /P "%RUTA%" /S /D -3 /C "cmd /c del /q @path"

rem        ** Luego se borran todas las subcarpetas con 3 o mas dias de antiguedad

forfiles /s /d -3 /p "%RUTA%" /c "cmd /c IF @isdir == TRUE rd /s /q @path"

 

 

rem        ** se aprovecha la funcionalidad para borrar los respaldos que quedan

rem        ** almacenados en el servidor, dado que ya se subieron a DropBox, para liberar espacio

rem        ** y también se borran los archivos temporales que deja foxpro, todo con 2 días de antiguedad

rem        *Establecer la Ruta de los respaldos diarios

rem        ** Primero se borran los archivos .7z que tengan más de 2 días de antiguedad, no borrar los de hoy ni ayer

ForFiles /P "C:\Temporal\Datos" /m *.7z /D -2 /C "cmd /c del /q @path"

La novedad aquí es el parámetro /m  que le dice al sistema operativo que solo afecte aquellos archivos que cumplen con una mascara (por eso la m)  que tengan la extensión 7z

rem        ** Luego se borran los archivos temporales   _7*.dbf y pdf

del C:\Temporal\_7*.dbf C:\Temporal\_7*.pdf C:\Temporal\_7*.xls C:\Temporal\_7*.xlsx C:\Temporal\_7*.idx

 

Espero les sirva como orientación si se enfrentan a esta tarea, que es relativamente simple si se sabe como realizarla, me despido no sin antes felicitar a mi hermana Teresita y su esposo Alex que cumplen años el mismo día, eso fue hace unos días este Enero.