Este documento recoge todas las llamadas HTTP que el conector sage200c.php realiza contra la API de SAGE 200c: autenticación, lecturas (GET), escrituras (POST/PUT) y descargas de ficheros. Para cada llamada se especifica la URL, los parámetros enviados, la estructura de la respuesta y los campos que el conector lee efectivamente.
SAGE_TOKEN está vacío, ha caducado, o tras una respuesta HTTP 401.| Cabecera | Valor |
|---|---|
Authorization | Basic {base64(SAGE_USERNAME:SAGE_PASSWORD)} |
Content-Type | application/x-www-form-urlencoded |
| Campo | Tipo | Uso interno |
|---|---|---|
data.token | string | Bearer token guardado en SAGE_TOKEN |
data.expires_in | integer (minutos) | Convertido a Unix timestamp: time() + expires_in × 60 → SAGE_EXPIRES |
time() >= SAGE_EXPIRES. Si el token ha caducado se vuelve a solicitar automáticamente. Además, cualquier respuesta HTTP 401 invalida el token y reintenta la llamada original una sola vez con un token nuevo.
Todas las llamadas GET comparten el mismo sistema de construcción de querystring y la misma estructura de respuesta.
| Parámetro | Tipo | Descripción | Ejemplo |
|---|---|---|---|
fields |
array JSON | Columnas a devolver. Si está vacío, la API devuelve todos los campos. | ["CodigoCliente","RazonSocial"] |
filters |
array JSON | Expresiones de filtro en formato cadena. Las comillas tipográficas (´) se convierten a comillas simples (') antes de enviar. |
["CodigoSubfamilia = '**********'","BajaEmpresaLc = 0"] |
order_by |
array JSON | Expresiones de ordenación. | ["CodigoCliente ASC"] |
next |
integer | Offset de paginación (fila inicial de la página). | 100 |
limit |
integer | Número máximo de filas por página. | 50 |
meta.Total y calcula si hay página siguiente comparando el total de filas recibidas con el total esperado. Si meta.nextpage está presente, se realiza una nueva llamada añadiendo el valor de next incrementado. El comportamiento (paginación en el servidor o en el cliente) se controla con pagination_server en la configuración del método.
| Cabecera | Valor |
|---|---|
Authorization | Bearer {SAGE_TOKEN} |
Content-Type | application/json |
| Opción | Valor |
|---|---|
CURLOPT_RETURNTRANSFER | true |
CURLOPT_ENCODING | '' (auto) |
CURLOPT_MAXREDIRS | 10 |
CURLOPT_TIMEOUT | 0 (sin límite) |
CURLOPT_FOLLOWLOCATION | true |
CURLOPT_HTTP_VERSION | CURL_HTTP_VERSION_1_1 |
CURLOPT_SSL_VERIFYPEER | false |
CURLOPT_SSL_VERIFYHOST | false |
El conector hace dos tipos de llamadas a la API de SAGE 200c:
La siguiente tabla resume qué método FACTSYNC dispara cada llamada. Cada endpoint individual repite esta información en su cabecera con un badge y la línea «Disparada por».
| Endpoint | Tipo | Disparada por |
|---|---|---|
POST oauth/token |
INDIRECTA | Antes de cualquier llamada autenticada cuando el token está caducado o ante una respuesta 401 |
GET api/clientes |
DIRECTA | customers GET |
GET api/cadenas |
INDIRECTA | Durante customers GET, vía _get_codigocliente_from_cadena() cuando el cliente tiene CodigoCadena_ |
GET api/domicilios |
DIRECTA | addresses GET |
GET api/sector |
DIRECTA | sectors GET |
GET api/comisionistas |
DIRECTA | employees GET |
GET api/familias |
DIRECTA | product_families GET y product_groups GET (mismo endpoint, distinto filtro) |
GET api/articulos |
DIRECTA | products GET |
GET api/imagenes_articulos |
DIRECTA | product_images GET |
GET api/imagen/{id} |
INDIRECTA | Durante product_images GET, una llamada por imagen vía _get_base64_image() |
GET api/almacenes |
DIRECTA | warehouses GET |
GET api/stock |
DIRECTA | stocks GET |
GET api/divisas |
DIRECTA | currencies GET |
GET api/iva |
INDIRECTA | Cache global vía _init_tipus_iva(); usado por products GET, conditions GET y external_documents GET |
GET api/tarifas |
DIRECTA | discount_lines GET |
GET api/tarifa_precio |
DIRECTA | conditions GET (1ª pasada) |
GET api/articulos_cliente |
INDIRECTA | Durante conditions GET (2ª pasada) |
GET api/condiciones_especiales |
INDIRECTA | Durante conditions GET (3ª pasada) |
GET api/pedidos |
DIRECTA | external_documents GET con {type:"orders"} |
GET api/pedido/{any}-{serie}-{num} |
INDIRECTA | Durante external_documents GET {type:orders}, una llamada por pedido |
GET api/albaranes |
DIRECTA | external_documents GET con {type:"delivery_notes"} |
GET api/albaran/{any}-{serie}-{num} |
INDIRECTA | Durante external_documents GET {type:delivery_notes}, una llamada por albarán |
GET api/facturas |
DIRECTA | external_documents GET con {type:"invoices"} |
GET api/factura/{any}-{serie}-{num} |
INDIRECTA | Durante external_documents GET {type:invoices}, una llamada por factura |
GET api/doc/{path} |
INDIRECTA | Durante external_documents GET (cualquier tipo), una llamada por adjunto |
GET api/formas_pago |
DIRECTA | payment_methods GET |
GET api/campos |
DIRECTA | custom_fields GET (directa) y resolución de definiciones de características (indirecta) |
GET api/idiomas_articulos |
DIRECTA | translations GET |
POST api/clientes |
DIRECTA | customers POST (exportación) |
PUT api/clientes/{codi} |
DIRECTA | customers PUT (exportación) |
POST api/pedidos |
DIRECTA | orders POST (exportación) |
cli_ficha)
paginada
customers GET.data.rows[])| Campo SAGE | Tipo | Campo Softbase | Notas |
|---|---|---|---|
CodigoCliente | string | id_extern / __id_extern__ | Clave externa principal |
CodigoCadena_ | string | id_parent | Si difiere de CodigoCliente → cliente hijo de cadena |
RazonSocial | string | empresa | |
Nombre | string | nom | |
Email1 | string | email | |
Telefono | string | telefon | |
Telefono2 | string | telefon2 | |
Telefono3 | string | telefon_movil | |
Fax | string | fax | |
CifDni | string | nif / num_doc | |
FechaAlta | ISO 8601 datetime | data_alta | Convertido a Unix timestamp |
CodigoContable | string | subcuenta | |
CodigoDivisa | string | id_divisa | Resuelto vía _get_id_divisa() |
FormadePago | string | id_forma_pago | Resuelto vía _get_id_forma_pago() |
TarifaPrecio | string | id_linea_descompte | Resuelto vía _get_id_linia_descompte() |
CodigoRuta_ | string | id_ruta | |
CodigoComisionista | string | id_treballador | Comisionista 1 (principal) |
CodigoComisionista2_ | string | __treballadors[] | Comisionista 2 |
CodigoComisionista3_ | string | __treballadors[] | Comisionista 3 |
CodigoComisionista4_ | string | __treballadors[] | Comisionista 4 |
CodigoSector_ | string | id_sector | Fuente seleccionable vía SAGE_SECTORS |
CodigoTipoClienteLc | string | id_sector | Fuente alternativa si SAGE_SECTORS='tipus_client' |
CodigoColectivoClienteLc | string | id_sector | Fuente alternativa si SAGE_SECTORS='colectius' |
ObservacionesCliente | string | observacions | |
IBAN | string | CC_iban | |
SiglaNacion | string | CC_pais | |
RiesgoMaximo | float | credit | |
FechaNacimiento | ISO 8601 datetime | data_naixement | Convertido a Unix timestamp |
ClaveIVA | string | clau_iva | |
%Descuento | float | descompte1 | |
BajaEmpresaLc | integer | arxivat | 0 → 'N' · ≠0 → 'Y' |
FechaBajaLc | ISO 8601 datetime | data_baixa | Convertido a Unix timestamp |
DIRe | string | codi_dir3_gestor | |
Domicilio | string | carrer | Dirección completa del cliente (en Sage el Domicilio ya contiene la calle entera; enriquecida vía _add_address_to_data()) |
CodigoPostal | string | cp | |
Municipio | string | poblacio | |
Provincia | string | provincia | |
Nacion | string | pais | Nombre del país |
CodigoNacion | integer | — | Usado internamente para la resolución de país |
CodigoCadena_ existe y es diferente de CodigoCliente, el cliente es hijo de una cadena. El conector busca el cliente padre vía _get_codigocliente_from_cadena() y asigna id_parent, enllacar_factures='N' y enllacar_condicions='Y'.
customers GET, vía _get_codigocliente_from_cadena(), cuando el cliente importado tiene CodigoCadena_. Se cachea en variable estática: una sola llamada por sesión.Esta llamada se realiza una sola vez por sesión de sync y el resultado se guarda en una variable estática para evitar repetir la consulta. Se utiliza para construir el mapa CodigoCadena_ → CodigoCliente que se usa durante la importación de clientes.
data.rows[])| Campo SAGE | Tipo | Uso |
|---|---|---|
CodigoCadena_ | string | Clave del mapa (código de cadena) |
CodigoCliente | string | Valor del mapa (código del cliente cabecera) |
CodigoEmpresa | integer | Leído pero no mapeado a Softbase |
Cadena_ | string | Nombre de la cadena (no mapeado) |
RazonSocial | string | Leído pero no mapeado |
Nombre | string | Leído pero no mapeado |
SiglaNacion | string | Leído pero no mapeado |
CifDni | string | Leído pero no mapeado |
cli_direccions)
paginada
addresses GET.data.rows[])| Campo SAGE | Tipo | Campo Softbase | Notas |
|---|---|---|---|
IdDomicilio | string/integer | __id_extern__ | Clave externa |
CodigoCliente | string | id_tabla | Resuelto a cli_ficha.id vía _get_id_client() |
NumeroDomicilio | integer | — | Si = 0, el registro se ignora (es la dirección principal) |
RazonSocial | string | nom | Combinado con TipoDomicilio |
TipoDomicilio | string | defecte | 'F' → 'Y' · otros → 'N' |
Domicilio | string | carrer | Combinado con Domicilio2 |
Domicilio2 | string | carrer | Se añade al campo anterior |
Numero1 | string | numero | |
Piso | string | pis | |
Escalera | string | escala | |
Puerta | string | porta | |
CodigoPostal | string | cp | |
Municipio | string | poblacio | |
Provincia | string | provincia | |
Nacion | string | pais | |
CodigoNacion | integer | — | Usado para búsqueda de país |
SiglaNacion | string | pais_codi | |
Telefono | string | telefon | |
Telefono2 | string | telefon_movil |
cli_sectors)
paginada
sectors GET.data.rows[])| Campo SAGE | Tipo | Campo Softbase |
|---|---|---|
CodigoSector_ | string | __id_extern__ |
DescripcionSector_ | string | descripcio |
SAGE_SECTORS: api/sector (valor sectors), api/tipos_cliente (valor tipus_client) o api/colectivos_clientes (valor colectius). Los campos devueltos difieren: api/tipos_cliente usa CodigoTipoClienteLc / TipoClienteLc; api/colectivos_clientes usa CodigoColectivoClienteLc / ColectivoClienteLc.ficha_treballador)
paginada
employees GET.data.rows[])| Campo SAGE | Tipo | Campo Softbase |
|---|---|---|
CodigoComisionista | string | __id_extern__ |
Comisionista | string | nom |
CifDni | string | nif |
Telefono | string | telefon |
Telefono2 | string | telefon_movil |
EMail1 | string | email |
Domicilio | string | carrer |
CodigoPostal | string | cp |
Municipio | string | poblacio |
Provincia | string | provincia |
Nacion | string | pais |
SiglaNacion | string | pais_codi |
El conector llama dos veces al mismo endpoint con filtros opuestos para separar familias de grupos.
art_families)
paginada
product_families GET.| Campo SAGE | Tipo | Campo Softbase |
|---|---|---|
CodigoFamilia | string | __id_extern__ |
Descripcion | string | descripcio |
CodigoSubfamilia | string | Usado para el filtro, valor siempre ********** |
art_articles)
paginada
product_groups GET.| Campo SAGE | Tipo | Campo Softbase |
|---|---|---|
CodigoSubfamilia | string | __id_extern__ |
Descripcion | string | descripcio |
CodigoFamilia | string | id_familia (resuelto vía _get_id_familia()) |
art_subarticles)
paginada
products GET. La resolución del campo GrupoIva dispara adicionalmente GET api/iva (sección 15) la primera vez.data.rows[])| Campo SAGE | Tipo | Campo Softbase | Notas |
|---|---|---|---|
CodigoArticulo | string | __id_extern__ / codi | Clave externa y código interno |
DescripcionArticulo | string | model | |
CodigoFamilia | string | id_familia | Resuelto vía _get_id_familia() |
CodigoSubfamilia | string | id_article | Resuelto vía _get_id_article() |
PrecioCompra | float | preu_cost | |
PrecioVenta | float | __preus[TARIFA_BASE] | Precio de venta base |
PrecioVentasinIVA1 | float | __preus[LINIA_DESC_1] | Precio para línea de descuento 1 |
PrecioVentasinIVA2 | float | __preus[LINIA_DESC_2] | Precio para línea de descuento 2 |
PrecioVentasinIVA3 | float | __preus[LINIA_DESC_3] | Precio para línea de descuento 3 |
PrecioOfertasinIVA | float | __preus[TARIFA_BASE_OFERTA] | Solo si la oferta está activa |
FechaInicioOferta | ISO 8601 datetime | __preus[].data_ini | Fecha de inicio de la oferta |
FechaFinalOferta | ISO 8601 datetime | __preus[].data_fi | Fecha fin de la oferta (ajustada a 23:59:59) |
GrupoIva | string | id_tipus_iva | Resuelto vía _get_tipus_iva() |
ObsoletoLc | integer | arxivat / web | ≠0 → arxivat='Y', web='N' |
FechaAlta | ISO 8601 datetime | data_alta | Convertido a Unix timestamp |
CodigoContable | string | subcuenta |
d_items_fotos)
paginada
product_images GET. Cada imagen nueva o modificada dispara adicionalmente GET api/imagen/{ImagenExt} (sección 11).data.rows[])| Campo SAGE | Tipo | Uso |
|---|---|---|
CodigoArticulo | string | Resuelto a id_subarticle vía _get_id_subarticle() |
ImagenExt | string (UUID) | __id_extern__ — si ha cambiado respecto al guardado, se vuelve a descargar |
sysDescripcionBinario | string | descripcio |
ImagenExt difiere del guardado en Softbase, se realiza una llamada adicional a GET /api/imagen/{ImagenExt} (sección 11).
product_images GET, una vez por imagen nueva o modificada, vía _get_base64_image().Llamada individual, sin paginación. El parámetro ImagenExt es el UUID devuelto por /api/imagenes_articulos.
data.rows[])| Campo SAGE | Tipo | Uso |
|---|---|---|
data.ImagenBase64 | string (Base64) | Decodificado y guardado en files/public/products/prod_{ImagenExt} vía SaveFileBase64() |
mag_magatzems)
paginada
warehouses GET.data.rows[])| Campo SAGE | Tipo | Campo Softbase | Notas |
|---|---|---|---|
CodigoAlmacen | string | __id_extern__ | |
Almacen | string | descripcio | |
Domicilio | string | adreca | Concatenado con Municipio, CP y Provincia |
Municipio | string | adreca | |
CodigoPostal | string | adreca | |
Provincia | string | adreca | |
| — | — | actiu | Siempre 'Y' en importación |
| — | — | web | Siempre 'Y' en importación |
mag_estocs)
paginada
stocks GET.data.rows[])| Campo SAGE | Tipo | Campo Softbase |
|---|---|---|
IdAcumuladoStock | string/integer | __id_extern__ |
CodigoArticulo | string | id_subarticle (resuelto vía _get_id_subarticle()) |
CodigoAlmacen | string | id_magatzem (resuelto vía _get_id_magatzem()) |
UnidadSaldo | float | estoc |
PrecioMedio | float | valor_mig_unitari |
tbl_divisas)
paginada
currencies GET.data.rows[])| Campo SAGE | Tipo | Campo Softbase |
|---|---|---|
CodigoDivisa | string | __id_extern__ / codi |
Divisa | string | nom |
SimboloDivisa | string | simbol |
Decimales | integer | decimals |
FactorCambioEuro | float | factor_conversio |
art_tipus_iva)
paginada
_init_tipus_iva(), una sola vez por sesión. Se invoca desde products GET, conditions GET y external_documents GET al resolver el campo GrupoIva. No es un método FACTSYNC: el conector no expone vat_types al usuario.data.rows[])| Campo SAGE | Tipo | Campo Softbase | Notas |
|---|---|---|---|
GrupoIva | string | __id_extern__ | Clave compuesta: S{GrupoIva}-{CodigoIvaConRecargo} |
CodigoIvaConRecargo | string | __id_extern__ | Parte de la clave compuesta |
DescIvaConrecargo | string | descripcio | |
IVAConrecargo | float | tipus | Porcentaje de IVA |
RecargoConRecargo | float | tipus_rec | Porcentaje de recargo de equivalencia |
art_linees_descompte)
paginada
discount_lines GET.data.rows[])| Campo SAGE | Tipo | Campo Softbase |
|---|---|---|
Tarifa | integer | __id_extern__ |
DescripcionTarifa | string | descripcio (prefijada con Tarifa + " - ") |
IndicadorTarifa | integer | __IndicadorTarifa (0=precio directo, 1=% incremento, 2=valor fijo) |
CodigoDivisa | string | Leído pero no mapeado directamente |
CodigoEmpresa | integer | Leído pero no mapeado |
art_linees_descompte condiciones)
paginada
conditions GET (1ª pasada). Las pasadas 2 y 3 son indirectas: GET api/articulos_cliente (sección 18) y GET api/condiciones_especiales (sección 19).data.rows[])| Campo SAGE | Tipo | Campo Softbase / Uso |
|---|---|---|
IdTarifaPrecio | string/integer | Base para __id_extern__ (tramos: {Id}-1 … {Id}-N; fila base: {Id}) |
Tarifa | integer | id_tabla (resuelto vía _get_id_linia_descompte()) |
CodigoArticulo | string | id_tipus_element (resuelto vía _get_id_subarticle()) |
IndicadorTarifa | integer | Tipo de cálculo (0/1/2) |
FechaInicio | ISO 8601 datetime | data_ini (convertido a timestamp) |
FechaFinal | ISO 8601 datetime | data_fi (ajustado a 23:59:59) |
StatusActivo | integer | Si ≠ -1 → eliminat = timestamp |
HastaUnidades1 … HastaUnidades10 | integer | volum de cada tramo |
Precio1 … Precio10 | float | Precio del tramo; calculado según IndicadorTarifa |
HastaUnidades no vacío (identificada como {IdTarifaPrecio}-{j}) y una fila base final sin límite de volumen (identificada como {IdTarifaPrecio}).
| IndicadorTarifa | Fórmula |
|---|---|
| 0 — Precio directo | valor = Precio |
| 1 — % incremento | valor = preu_base × (1 + Precio / 100) |
| 2 — Valor fijo | valor = preu_base + Precio |
preu_base es el precio de la tarifa BASE del artículo, obtenido vía _get_preu_base().
conditions GET durante su 2ª pasada, con el filtro Automatico=0 (sólo las condiciones introducidas manualmente; las generadas automáticamente a partir de albaranes o pedidos se ignoran).data.rows[])| Campo SAGE | Tipo | Campo Softbase / Uso |
|---|---|---|
CodigoCliente | string | id_tabla (resuelto a cli_ficha.id) |
CodigoArticulo | string | id_tipus_element (resuelto a art_subarticles.id) |
FechaInicio | ISO 8601 datetime | data_ini |
PrecioOferta | float | Si > 0 → tipus_condicio='preu' |
%Descuento | float | Descuento 1 para cascada |
%Descuento2 | float | Descuento 2 para cascada |
%Descuento3 | float | Descuento 3 para cascada |
| Condición | tipus_condicio | Fórmula valor |
|---|---|---|
PrecioOferta > 0 | 'preu' | PrecioOferta × (1 - d1/100) × (1 - d2/100) × (1 - d3/100) |
%Descuento > 0 | 'desc' | % efectivo resultante de la cascada de descuentos |
| Sin precio ni descuento | — | __import = false → registro ignorado |
conditions GET (3ª pasada del método).data.rows[])| Campo SAGE | Tipo | Campo Softbase / Uso |
|---|---|---|
CodigoCliente | string | id_tabla si ámbito = cliente concreto |
CodigoCadena_ | string | id_tabla si ámbito = cadena (resuelto al cliente cabecera) |
CodigoTipoClienteLc | string | Si SAGE_SECTORS='tipus_client': se expande a todos los clientes del tipo |
CodigoFamilia | string | id_tipus_element si ámbito = familia |
CodigoSubfamilia | string | id_tipus_element si ámbito = grupo |
FechaInicio | ISO 8601 datetime | data_ini |
Precio | float | Precio base para el cálculo (cuando hay precio directo) |
%Descuento | float | Descuento 1 |
%Descuento2 | float | Descuento 2 |
%Descuento3 | float | Descuento 3 |
IdDelegacion | string | Leído pero no mapeado |
external_documents GET con {extra_params:{type:"orders"}}. Cada fila dispara una llamada indirecta a GET api/pedido/{any}-{serie}-{num} (sección 21) y, si hay PathFichero, a GET api/doc/{path} (sección 26).data.rows[])| Campo SAGE | Tipo | Campo Softbase | Notas |
|---|---|---|---|
IdPedidoCli | string | __id_extern__ | Clave externa del pedido |
CodigoCliente | string | id_client | Resuelto vía _get_id_client() |
EjercicioPedido | integer | any | Año de la serie |
SeriePedido | string | serie | |
NumeroPedido | integer | numero | |
FechaPedido | ISO 8601 datetime | data | Convertido a timestamp |
ImporteBruto | float | total_brut | |
ImporteLiquido | float | total | |
FactorCambio | float | factor_conversio | |
CodigoDivisa | string | id_divisa | Resuelto vía _get_id_divisa() |
estado | integer | estat | 0→BLOQUEADO · 1→SERVIDO · 2→PENDIENTE |
PathFicheros | array | — | Ruta(s) de los ficheros adjuntos; dispara llamadas a GET /api/doc/{path} |
GET /api/pedido/{any}-{serie}-{num} para obtener las líneas de detalle (sección 21).
external_documents GET {type:orders}, una vez por cada pedido devuelto en la lista (sección 20).Ejemplo: api/pedido/2026-A-1
| Campo SAGE | Tipo | Uso |
|---|---|---|
Estado | integer | Confirma el estado para el campo estat |
| Campo SAGE | Tipo | Campo Softbase | Notas |
|---|---|---|---|
CodigoArticulo | string | id_subarticle | Resuelto vía _get_id_subarticle() |
DescripcionArticulo | string | descripcio | |
UnidadesPedidas | float | quantitat | Cantidad solicitada |
Precio | float | preu | |
ImporteBruto | float | — | Usado para calcular descompte |
BaseImponible | float | — | Usado para calcular descompte: (ImporteBruto - BaseImponible) / ImporteBruto × 100 |
ImporteNeto | float | preu_net | |
GrupoIva | string | iva | Resuelto a tipo IVA interno |
external_documents GET con {extra_params:{type:"delivery_notes"}}. Cada fila dispara GET api/albaran/{any}-{serie}-{num} (sección 23) y, si procede, GET api/doc/{path} (sección 26).data.rows[])| Campo SAGE | Tipo | Campo Softbase | Notas |
|---|---|---|---|
IdAlbaranCli | string | __id_extern__ | Clave externa |
CodigoCliente | string | id_client | Resuelto vía _get_id_client() |
EjercicioAlbaran | integer | any | |
SerieAlbaran | string | serie | |
NumeroAlbaran | integer | numero | |
FechaAlbaran | ISO 8601 datetime | data | Convertido a timestamp |
ImporteBruto | float | total_brut | |
ImporteLiquido | float | total | |
StatusFacturado | integer | estat | 1 → 'FACTURAT' · 0 → 'OBERT' |
StatusAbono | integer | subestat | 1 → 'ABONO' |
PathFicheros | Array | — | Dispara llamadas a GET /api/doc/{path} |
GET /api/albaran/{any}-{serie}-{num} para obtener las líneas (sección 23).external_documents GET {type:delivery_notes}, una vez por cada albarán devuelto en la lista (sección 22).Ejemplo: api/albaran/2026-A-1. Misma estructura que el detalle de pedido.
| Campo SAGE | Tipo | Campo Softbase |
|---|---|---|
CodigoArticulo | string | id_subarticle |
Unidades | float | quantitat |
Precio | float | preu |
CodigoIva | integer | iva |
ImporteBruto | float | total_brut de línea |
ImporteNetoLineas | float | preu_net |
external_documents GET con {extra_params:{type:"invoices"}}. Cada fila dispara GET api/factura/{any}-{serie}-{num} (sección 25) y, si procede, GET api/doc/{path} (sección 26).data.rows[])| Campo SAGE | Tipo | Campo Softbase | Notas |
|---|---|---|---|
IdFacturaCli | string | __id_extern__ | Clave externa |
CodigoCliente | string | id_client | Resuelto vía _get_id_client() |
EjercicioFactura | integer | any | |
SerieFactura | string | serie | |
NumeroFactura | integer | numero | |
FechaFactura | ISO 8601 datetime | data | Convertido a timestamp |
ImporteBruto | float | total_brut | |
ImporteLiquido | float | total | |
CodigoDivisa | string | id_divisa | Resuelto vía _get_id_divisa() |
StatusContabilizado | integer | estat | 1 → 'COMPTABILITZAT' · 0 → 'PENDENT' |
StatusAbono | integer | subestat | 1 → 'ABONO' |
PathFichero | string | — | Dispara llamadas a GET /api/doc/{path} |
GET /api/factura/{any}-{serie}-{num} para obtener las líneas (sección 25).external_documents GET {type:invoices}, una vez por cada factura devuelta en la lista (sección 24).Ejemplo: api/factura/2026-A-1. Misma estructura que el detalle de pedido y albarán.
| Campo SAGE | Tipo | Uso |
|---|---|---|
StatusContabilizado | integer | Confirma el estado de contabilización |
StatusAbono | integer | Confirma si es abono |
| Campo SAGE | Tipo | Campo Softbase |
|---|---|---|
CodigoArticulo | string | id_subarticle |
Unidades | float | quantitat |
Precio | float | preu |
CodigoIva | integer | iva |
ImporteBruto | float | total_brut de línea |
ImporteNetoLineas | float | preu_net |
external_documents GET (cualquier tipo), una llamada por cada adjunto referenciado en el campo PathFichero de la cabecera del documento.Llamado para cada valor de PathFichero devuelto en los listados de pedidos, albaranes y facturas. El conector puede recibir múltiples rutas separadas por coma en un solo campo y realiza una llamada por cada una.
| Campo SAGE | Tipo | Uso interno |
|---|---|---|
data.name | string | Nombre del fichero guardado en files/external_docs/ |
data.FileBase64 | string (Base64) | Decodificado y guardado localmente |
data.last_write_time | ISO 8601 datetime | Guardado como date_updated_file para detectar cambios futuros |
payment_methods GET| Campo | Tipo | Uso interno |
|---|---|---|
IdCondicion | string | Clave externa |
CodigoCondiciones | string | Código de condición; se prefija como COND_{valor} para el external ID |
FormadePago | string | Descripción de la forma de pago |
NumeroPlazos | integer | Número de vencimientos |
DiasPrimerPlazo | integer | Solo visualización |
DiasEntrePlazos | integer | Solo visualización |
Remesable | integer | -1: remesable (rebuts=Y, comptat=N) · otro: al contado |
custom_fields GET (directa) y también de forma indirecta durante products GET para resolver definiciones de características.| Campo | Tipo | Uso interno |
|---|---|---|
sysId | string | Clave externa (nombre del campo) |
sysRotulo | string | Etiqueta / descripción del campo |
_fetch_field_definition() también llama a este endpoint con un filtro sysId = '{campo}' para obtener la definición de un campo concreto al importar características de productos.
translations GET| Campo | Tipo | Uso interno |
|---|---|---|
IdIdioma | string | Clave externa; fallback: CodigoArticulo_CodigoIdioma_ |
CodigoArticulo | string | Resuelto a art_subarticles.id via _get_item_subarticle() |
CodigoIdioma_ | string | Código idioma SAGE (CAT, CAS, ING, POR, FRA, ALE, ITA) → código interno (ca, es, en, pt, fr, de, it) |
DescripcionArticulo | string | Guardado como titol |
Descripcion2Articulo | string | Guardado como subtitol |
id_item > 0) y el código de idioma tiene equivalencia en el mapa interno. Los registros que no cumplen se marcan con __import=false.
customers POST (exportación de un cliente sin fsync_external_id). También por el método orders POST con extra_params.export_customers = "Y" cuando el cliente del pedido no existe en SAGE (ver sección 32).id_treballador, id_divisa, id_linea_descompte…) no se exportan porque no se corresponden con ningún código de Sage. CifDni toma nif y, si está vacío, num_doc; el teléfono móvil (telefon_movil) viaja como Telefono3. La dirección sigue la misma correspondencia que la importación (_add_address_to_data): cp→CodigoPostal, poblacio→Municipio, pais→Nacion y SiglaNacion resuelto como código ISO del país (cfg_paisos.codi a partir de id_pais). Provincia toma el literal provincia y, si está vacío, la descripcion resuelta desde id_provincia (cfg_paisos_provincies).
Nacion se trunca a 25 caracteres y ObservacionesCliente a 50. El campo CifDni admite un máximo de 13 caracteres: si el NIF/CIF del cliente (nif o num_doc) lo supera, no se exporta — se marca en la previsualización (de clientes y de pedidos) como no exportable con el motivo, y la exportación queda bloqueada (también si se fuerza). El límite es configurable por conector vía la propiedad _export_nif_maxlen (0 = sin límite).
[ { ... } ]).
cli_ficha.id) como string (entre comillas), de forma que SAGE conserve la referencia al cliente de origen.
extra_params.default_values del método customers POST se añaden solo al crear el cliente y no sobreescriben los campos ya mapeados desde la ficha.
| Campo SAGE | Tipo | Uso |
|---|---|---|
meta.IdProcesoIME | integer | Id del proceso de importación. La creación de clientes en SAGE es asíncrona: el POST no devuelve CodigoCliente; la operación se considera correcta si llega este IdProcesoIME. |
CodigoCliente, fsync_external_id_{id_connexio} queda vacío de momento. El código real se resuelve más tarde buscando el cliente en SAGE por NIF (CifDni) o Email (EMail1) — ver sección 32.
cli_ficha ya tiene fsync_external_id_{id_connexio} rellenado, se usa PUT (sección 28); en caso contrario, POST.
customers PUT (exportación de un cliente que ya tiene fsync_external_id).El parámetro de ruta {CodigoCliente} se obtiene de fsync_external_id_{id_connexio} en cli_ficha y se aplica URL-encoding.
En una actualización correcta, SAGE devuelve HTTP 200. El conector no lee ningún campo específico de la respuesta; comprueba únicamente que no haya error.
orders POST (exportación de un pedido pendiente).extra_params.export_customers = "Y"): si el cliente del pedido no tiene fsync_external_id, antes de enviar el pedido el conector lo busca en SAGE vía GET /api/clientes filtrando por NIF (CifDni; usa nif o, si está vacío, num_doc — el mismo valor con el que se creó el cliente) y, si no hay resultado, por Email (EMail1). Si existe, lo vincula; si no, lo crea vía POST /api/clientes (sección 30) usando la configuración del método indicado en extra_params.customers_method. Como la creación no es instantánea, el pedido se aplaza 3 minutos marcando fsync_export_status_{id_connexio} con el timestamp del reintento en negativo; el cron lo reprocesa cuando vence.
fsync_export_status > 0); para resolverlo, el botón Reiniciar (acción FSYN_export_reset) pone a 0 el estado de exportación del pedido y del cliente vinculado, forzando que el cliente se vuelva a enviar en el siguiente intento.
| Campo SAGE | Tipo | Uso |
|---|---|---|
data.NumeroPedido | integer/string | Número de pedido asignado por SAGE, guardado en Softbase |
data.Id | string | Fallback si NumeroPedido no está presente |
GET /api/doc/{PathFichero} y los incluye serializados en el campo args JSON guardado en Softbase. No se envían a SAGE en el POST del pedido.
| Código HTTP / situación | Comportamiento del conector |
|---|---|
401 Unauthorized |
Invalida el token (SAGE_TOKEN = ''), solicita un token nuevo y reintenta la llamada original una sola vez. |
4xx (otros) |
Devuelve mensaje de error: "HTTP status:{codi} - URL not found" con el detalle de la respuesta. |
5xx |
Intenta parsear el JSON de la respuesta buscando error.message o error; si no, devuelve la respuesta en bruto. |
| Error cURL | Devuelve "Communication Error: {curl_error()}". |
| JSON inválido | Devuelve la respuesta en bruto sin procesar. |
| ID externo no resoluble | Las funciones _get_id_*() devuelven 0. El registro se ignora silenciosamente sin detener la sincronización. |