raorsa/sage-middleware-client

Maintainers

Package info

github.com/raorsa/sageMiddlewareClient

pkg:composer/raorsa/sage-middleware-client

Transparency log

Statistics

Installs: 128

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v6 2026-08-19 11:30 UTC

README

Cliente PHP para consumir la API REST del middleware Laravel que hace de puente entre el ERP interno (SQL Server, tipo Sage) y aplicaciones externas (portal de cliente, apps de técnicos, etc.).

Este paquete no contiene lógica de negocio: cada clase es un wrapper fino sobre un grupo de endpoints del middleware — construye la URL, gestiona el login/token y el caché local, y decodifica la respuesta JSON. Toda la lógica real (filtros, cálculos, formateo de IDs) vive en el middleware.

  • Requisitos: PHP >= 8.2
  • Dependencias: symfony/http-client, raorsa/rw-file-cache, monolog/monolog
  • Licencia: Apache-2.0

Instalación

composer require raorsa/sage-middleware-client

Uso básico

Cada clase de negocio (Clients, Invoices, DeliveryNotesClients, DeliveryNotesProviders, OrdersClients, OrdersProviders, Jobs, Incentives, Articles, Apparatus, CustomClient) extiende Raorsa\SageMiddlewareClient\components\baseClient y se instancia con el método estático make():

use Raorsa\SageMiddlewareClient\Clients;

$clients = Clients::make(
    url: 'https://middleware.ejemplo.com/api/',
    user: 'usuario@dominio.com',
    password: 'secreto',
    verify: true,           // verificar certificado SSL
    name: 'MiApp',          // nombre del token generado en el login del middleware
    cacheLife: 10,          // minutos de vida del caché de respuestas (0 = sin caché)
    cacheDir: null,         // por defecto /tmp/sageCache.<hash del cwd>/
    cacheCompress: true,    // comprime el caché en disco (gzip)
    logDir: null,           // por defecto ./logs/
    logLengthData: 100,     // nº de caracteres de payload que se registran en el log
);

$listado = $clients->clients();          // GET /clients/list
$ficha   = $clients->clientInfo('1234'); // GET /clients/info/1234

make() crea una conexión (login/password/name) nueva; el login contra el middleware (POST /login) es perezoso: solo se dispara la primera vez que se necesita un token, y el token obtenido se cachea (ver "Caché y autenticación" más abajo).

Si ya tienes una connexion construida (por ejemplo, en tests, o para compartir conexión entre varias clases de negocio), usa mount():

use Raorsa\SageMiddlewareClient\components\connexion;
use Raorsa\SageMiddlewareClient\wrappers\{cache, log};
use Raorsa\SageMiddlewareClient\Invoices;

$connexion = connexion::mount('https://middleware.ejemplo.com/api/', 'usuario@dominio.com', 'secreto');
$invoices = Invoices::mount($connexion, new log(), new cache());

Casi todos los métodos aceptan un último parámetro bool $cache = true para omitir el caché de esa llamada concreta ($clients->clients(false)).

Caché y autenticación

  • El login se resuelve solo cuando hace falta un token: connexion::call() intenta primero con el token en memoria/caché; si el middleware responde 405, descarta el token y vuelve a autenticar.
  • El token se guarda en el mismo caché de disco que las respuestas (wrappers\cache, basado en raorsa/rw-file-cache), con una vida de 6 días (baseClient::TOKEN_LIFE_TIME) — un margen intencionado por debajo de los 7 días de caducidad que el middleware aplica a los tokens Sanctum, para evitar reutilizar uno a punto de expirar.
  • Las respuestas JSON de cada endpoint se cachean por su URL completa (cacheLife minutos, por defecto 10). Si una llamada al servidor falla pero hay una respuesta anterior en caché ("last"), baseClient::call() la devuelve igualmente como fallback.
  • Los métodos callJson() devuelven false si la respuesta no es JSON válido o si la llamada falla (nunca lanzan excepción).

Clases disponibles

Cada tabla indica el método del cliente, el endpoint del middleware al que llama y qué devuelve. La semántica completa de cada endpoint (filtros de negocio, formato de identificadores compuestos, etc.) la define el middleware; aquí solo se documenta el contrato desde el punto de vista del cliente.

Clients — clientes del ERP

Método Endpoint Descripción
clients(bool $cache = true) GET /clients/list Diccionario {código: nombre} de clientes válidos.
clientInfo(string $id, bool $cache = true) GET /clients/info/{id} Ficha completa de un cliente (incluye direcciones).
companyInfo(string $domain, bool $cache = true) GET /clients/team/{domain} Cliente asociado a un dominio de email de contacto.
validDomains(bool $cache = true) GET /clients/auth-domains Lista de dominios de email válidos para alta/login de usuarios del portal.

Invoices — facturas de cliente

Método Endpoint Descripción
list(string $team, bool $cache = true) GET /invoice/list/{team} Facturas de un cliente, ordenadas por fecha descendente.
info(string $id, bool $cache = true) GET /invoice/info/{id} Cabecera de una factura. $id es el identificador compuesto Serie-Ejercicio-Numero (p. ej. G-2024-00123).
lines(string $invoiceNumber, bool $cache = true) GET /invoice/lines/{invoiceNumber} Líneas de una factura. ⚠️ $invoiceNumber es solo NumeroFactura (no el identificador compuesto de info): si el número se repite entre series o ejercicios distintos, el middleware puede devolver líneas de más de un documento — bug conocido del lado servidor, pendiente de corregir allí.
download(string $id, bool $cache = true) GET /invoice/download/{id} PDF original de la factura (string binario).
img(string $id, bool $cache = true) GET /invoice/img/{id} Primera página de la factura en JPG.

DeliveryNotesClients / DeliveryNotesProviders — albaranes

Ambas clases comparten toda la lógica en la clase abstracta DeliveryNotes; solo cambia el prefijo de ruta (delivery para albaranes de cliente, delivery-provider para los de proveedor).

Método Endpoint Descripción
list(string $team, bool $cache = true) .../list/{team} Albaranes de un cliente/proveedor.
info(string $id, bool $cache = true) .../info/{id} Cabecera de un albarán. $id admite identificador compuesto parcial (serie, ejercicio y/o número, en cualquier combinación).
lines(string $id, bool $cache = true) .../lines/{id} Líneas de un albarán.
linesSN(string $sn, bool $cache = true) .../lines-sn/{sn} Líneas que coinciden con un número de serie de aparato.
emptyLines(string $year, bool $cache = true) .../empty-lines/{year} Líneas de un año sin número de serie asignado.
find(string $query, bool $cache = true) .../find/{query} Búsqueda flexible (coincidencia parcial) por el mismo identificador compuesto que info.
download(string $id, bool $cache = true) .../download/{id} PDF del albarán.
img(string $id, bool $cache = true) .../img/{id} JPG de la primera página del albarán.

OrdersClients / OrdersProviders — pedidos

Ambas clases comparten toda la lógica en la clase abstracta Orders; solo cambia el prefijo de ruta (orders para pedidos de cliente, orders-provider para los de proveedor). A diferencia de los albaranes, un pedido todavía no se ha servido: no hay número de serie de aparato ni PDF asociado, por lo que no existen linesSN, emptyLines, download ni img.

Método Endpoint Descripción
list(string $team, bool $cache = true) .../list/{team} Pedidos de un cliente/proveedor.
info(string $id, bool $cache = true) .../info/{id} Cabecera de un pedido. $id admite identificador compuesto parcial (serie, ejercicio y/o número, en cualquier combinación).
lines(string $id, bool $cache = true) .../lines/{id} Líneas de un pedido.
find(string $query, bool $cache = true) .../find/{query} Búsqueda flexible (coincidencia parcial) por el mismo identificador compuesto que info.

Jobs — órdenes de trabajo (OT)

Método Endpoint Descripción
list(bool $cache = true) GET /jobs/list OT activas, enriquecidas con número de serie del aparato y operaciones.
listReport(bool $cache = true) GET /jobs/list-report Todas las OT (pasen o no las validaciones internas) con el detalle de cada comprobación — pensado para auditoría, no para uso normal.
info(string $id, bool $cache = true) GET /jobs/job/{id} Una OT concreta. $id es el identificador compuesto Ejercicio-Numero.
getSN(string $id, bool $cache = true) GET /jobs/jobsn/{id} Número de serie del aparato asociado a una OT.
operations(bool $cache = true) GET /jobs/operations Catálogo {idOperación: nombre}.
operationsJobs(bool $cache = true) GET /jobs/operations-jobs Operaciones asignadas a cada OT activa.
importLog(string $date, bool $cache = true) GET /jobs/import/{date} Registros de importación de actividades de una fecha concreta.

Incentives — incentivos SAT

Método Endpoint Descripción
sat(?string $startDate = null, ?string $endDate = null, bool $cache = true) GET /incentives/sat/all[/{startDate}[/{endDate}]] Puntos de incentivo de todos los técnicos, opcionalmente acotado por fechas.
userSAT(string $user, bool $cache = true) GET /incentives/sat/user/{user} Resumen de puntos de un técnico concreto.
satDetails(string $user, ?string $startDate = null, ?string $endDate = null, bool $cache = true) GET /incentives/sat/details/{user}[/{startDate}[/{endDate}]] Igual que userSAT pero con el detalle de líneas que justifica el cálculo.

Articles — artículos

Método Endpoint Descripción
screwTips(bool $cache = true) GET /articles/screw-tip/list Lista de "punteras completas" disponibles.
findScrewTips(string $diameter, bool $cache = true) GET /articles/screw-tip/find/{diameter} Punteras que coinciden con un diámetro.
searchScrewTips(array $diameters, bool $cache = true) GET /articles/screw-tip/search/{diameters} Punteras que coinciden con cualquiera de varios diámetros (el array se envía unido por comas).

Apparatus — aparatos

Estos endpoints no forman parte de la documentación de API compartida por el equipo de middleware; la tabla siguiente refleja únicamente el contrato tal como lo usa este cliente (rutas y parámetros), no las reglas de negocio del servidor.

Método Endpoint Descripción
list(string $client, bool $cache = true) GET /apparatus/{client} Aparatos asociados a un cliente.
interventions(string $id, bool $cache = true) GET /apparatus/{id}/interventions Intervenciones registradas sobre un aparato.
integrated(string $id, bool $cache = true) GET /apparatus/{id}/integrated Componentes/elementos integrados en un aparato.
brandInfo(string $id, bool $cache = true) GET /apparatus/brand/{id} Ficha de una marca de aparato.
brands(bool $cache = true) GET /apparatus/brands Catálogo de marcas.
types(bool $cache = true) GET /apparatus/types Catálogo de tipos de aparato.
warranties(bool $cache = true) GET /apparatus/warranties Catálogo de tipos de garantía.
interventionsType(bool $cache = true) GET /apparatus/interventions-type Catálogo de tipos de intervención.

CustomClient — llamadas ad-hoc

Para endpoints del middleware que aún no tienen wrapper dedicado, o para prototipar:

use Raorsa\SageMiddlewareClient\CustomClient;

$client = CustomClient::make(/* ... */);
$raw  = $client->get('ruta/relativa', false);      // string crudo de la respuesta
$json = $client->getJson('ruta/relativa', false);   // decodificado como object|array|false

Testing

composer install
vendor/bin/phpunit

Los tests no llaman a un middleware real: usan Symfony\Component\HttpClient\MockHttpClient con respuestas fijas en tests/resources/{clase}/{test}.json (ver tests/Traits/baseTest.php). Al añadir un método nuevo a una clase de negocio, el patrón habitual es añadir un test que use Nombre::mount($this->createConnexion(__FUNCTION__), $this->log, $this->cache) y un fixture JSON correspondiente.

Licencia

Apache-2.0