QuickBooks Online y la actualización que borra
Eloquent envía los atributos que cambiaron. QuickBooks Online quiere el objeto en el que debe convertirse. Entre esas dos frases hay un proveedor sin dirección.
Una integración con QuickBooks Online entra en producción. Tres semanas después un registro de proveedor no tiene dirección de facturación, ni condiciones de pago, ni código fiscal, y el registro de auditoría dice que la última persona que lo tocó solo cambió un correo electrónico.
Nada está roto. La integración hizo exactamente lo que se le dijo, y lo que se le dijo fue el comportamiento por defecto de dos sistemas que no se ponen de acuerdo sobre qué significa una actualización.
Eloquent envía cambios, la API quiere el objeto
Un save() envía los atributos que están sucios. Para una base de datos eso es
correcto y para esta API es la carga equivocada, porque trata el cuerpo de una
actualización como el registro en el que debe convertirse. Los campos que omites
no quedan intactos - quedan vacíos.
Así que la capa de mapeo no puede ser una traducción fina de los atributos cambiados de un modelo. Tiene que producir un objeto completo, lo que en la práctica significa leer el registro actual de QuickBooks, aplicarle el cambio y enviar el resultado.
La API admite una actualización parcial y conviene usarla. No conviene depender solo de ella, porque es un parámetro que alguien puede olvidar en la única llamada que importaba. Leer antes de escribir cuesta una petición y elimina toda una clase de pérdida silenciosa de datos, y en código que edita la contabilidad de alguien ese intercambio no necesita discusión.
La conclusión estructural es que esta integración necesita su propio mapeador en vez de un método en el modelo. El objeto que va al sistema contable no es el modelo, es un documento montado a partir de él, y tratarlos como lo mismo es el origen de este fallo.
La versión hace que un reintento ciego sea incorrecto
Cada registro lleva una versión, y una actualización tiene que enviar la versión sobre la que se basó. Si el registro se movió entretanto, se rechaza.
Eso es buen comportamiento y rompe el reintento por defecto. Un trabajo en cola que guarda una carga terminada y la reenvía tras un fallo está enviando una versión ya obsoleta, así que falla otra vez, y otra, hasta agotar los intentos - y el cambio acaba en una tabla de trabajos fallidos en vez de en la contabilidad.
El trabajo tiene que guardar la intención y no la carga. Lo que guarda es «pon el correo de este cliente en este valor», no un cuerpo de petición serializado. En cada intento lee el registro actual, aplica la intención y envía el resultado con la versión que acaba de leer.
Eso hace que el trabajo sea de verdad seguro de ejecutar dos veces, y aquí importa más que de costumbre, porque la cola de Laravel reintenta lo haya previsto alguien o no.
La otra mitad de la cuestión de la versión es qué hacer con un conflicto real, cuando alguien de contabilidad cambió el mismo campo. Sobrescribir es una decisión, no un valor por defecto. Para la mayoría de los campos la respuesta honesta es que gana el sistema contable y tu aplicación registra que su cambio fue rechazado, porque sobrescribir en silencio la corrección de una persona de contabilidad es el comportamiento que acaba con la credibilidad de una integración en una sola reunión.
La conexión es una empresa y no el usuario
La autorización se concede para un archivo de empresa concreto, y un mismo acceso puede conceder varias. Así que lo que guardas es una conexión - identificador de empresa, tokens, cuándo se renovaron y si está sana ahora mismo - en su propia tabla.
No columnas en un usuario. No una fila en una tabla de ajustes. El identificador de empresa es la clave de búsqueda, porque cada petición posterior se hace en nombre de una empresa y la aplicación tiene que saber de cuál.
La renovación del token tiene el mismo peligro que cualquier otra API contable: dos workers renovando en el mismo instante y anulándose entre sí. Ese fallo y su remedio están en el texto sobre Xero, y la mecánica es lo bastante parecida como para que repetirla aquí te hiciera perder el tiempo. Las duraciones difieren - consulta las actuales en la documentación de Intuit en vez de fijar lo que dijo un artículo - la forma del problema no.
Vale la pena conocer una particularidad de QuickBooks: el token de refresco caduca también por desuso, no solo por antigüedad. Una integración para un cliente que dejó de usar tu aplicación unos meses vuelve desconectada sin que nada haya ido mal. Un estado desconectado tiene que ser un estado real en tu esquema, con algo en la interfaz que lo explique, y no una excepción que sorprenda a alguien.
Casar clientes es una tabla, no una búsqueda
QuickBooks exige que el nombre visible de un cliente sea único dentro de una empresa. Esa única restricción causa la mayor parte del dolor de estos proyectos.
Tu base de datos tiene «Acme S.L.». La suya tiene «Acme SL», y además «ACME SL - NO USAR» de 2019, y además «Acme » con un espacio al final. Casar por nombre crea un duplicado la primera vez que alguien escribe la forma jurídica de otra manera, y los duplicados en un sistema contable son trabajo de limpieza para una persona de contabilidad, no un bug que puedas arreglar.
Así que la pareja vive en una tabla de correspondencia - tu identificador, el suyo, la empresa a la que pertenece - y se rellena con un emparejamiento que alguien revisa una vez, no con una comparación de cadenas que se ejecuta para siempre.
Ese ejercicio no es trabajo de ingeniería y es la mayor parte del tiempo transcurrido. También saca a la luz la pregunta que nadie hizo: qué sistema es dueño del nombre de un cliente, y qué pasa cuando cambia en el otro lado.
Decidir quién numera las facturas
La última decisión es comercial y hay que tomarla antes de escribir nada.
Si QuickBooks asigna los números, tu aplicación no puede mostrar un número hasta que el envío haya funcionado, lo que cambia lo que ve un cliente y lo que pasa cuando el sistema contable no está disponible.
Si los asigna tu aplicación, alguien tiene que desactivar la numeración automática en QuickBooks y los huecos de la secuencia pasan a ser tu responsabilidad.
Las dos cosas funcionan. Lo que no funciona es que numeren las dos, porque produce dos facturas por una venta y una conciliación que nadie puede cerrar. Es una conversación de cinco minutos en la semana uno y dos semanas de desenredo en el mes cuatro.
Cuándo no construir esto
Si las facturas tienen que aparecer en QuickBooks, nada tiene que volver, y el volumen son decenas al mes, un conector gestionado es la respuesta correcta y funciona esta tarde.
Una integración construida se gana su sitio cuando la correspondencia es específica de cómo trabaja tu negocio, cuando el volumen hace caro corregir a mano, o cuando los datos contables tienen que poder consultarse dentro de tu propio sistema. No se lo gana como forma de evitar una cuota, porque el coste está en las reglas de conciliación y esas son las mismas en ambos casos.
