- Hiboutik payloads are zod schemas, validated by HiboutikClient (getOne added for single resources: null on 404 or empty answer). - Hiboutik dates are read as Europe/Paris whatever the server time zone. - Paginated lists driving deletions are walked twice when they span several pages (brands, categories, products); categories are sorted by id. - A customer missing from the list is deleted only once Hiboutik confirms it; completed orders that lost their customer are linked back. - Each product and its versions are written in one transaction. - An email removed in Hiboutik is erased in database. - A 429 is retried with backoff, then stops the synchronization. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
66 lines
2.6 KiB
TypeScript
66 lines
2.6 KiB
TypeScript
import { z } from 'zod';
|
|
import { hiboutikId, hiboutikInt, hiboutikOptionalList, hiboutikRef, hiboutikText } from './hiboutik.schema';
|
|
|
|
/** Customer summary as returned by Hiboutik `GET /customers`: only its id is used (no addresses nor sales). */
|
|
export const customerHiboutikSchema = z.object({
|
|
customers_id: hiboutikId,
|
|
});
|
|
|
|
/** Address of a customer, embedded in the full customer. Name and phone can differ from the customer's. */
|
|
export const customerAddressHiboutikSchema = z.object({
|
|
address_id: hiboutikId,
|
|
first_name: hiboutikText,
|
|
last_name: hiboutikText,
|
|
phone: hiboutikText,
|
|
company: hiboutikText,
|
|
address: hiboutikText,
|
|
zip_code: hiboutikText,
|
|
city: hiboutikText,
|
|
state: hiboutikText,
|
|
country: hiboutikText,
|
|
customers_id: hiboutikRef,
|
|
/** 1 for the customer's default address. */
|
|
default: hiboutikInt,
|
|
other: hiboutikText,
|
|
});
|
|
|
|
/**
|
|
* Address as returned by Hiboutik `GET /customers_addresses/:address_id`: the embedded address plus the company
|
|
* details. One API call per address, so only fetched for addresses with a company.
|
|
*/
|
|
export const customerAddressDetailsHiboutikSchema = customerAddressHiboutikSchema.extend({
|
|
tax_number: hiboutikText,
|
|
company_number: hiboutikText,
|
|
legal_status: hiboutikText,
|
|
/** French local time, "YYYY-MM-DD HH:mm:ss". */
|
|
updated_at: hiboutikText,
|
|
});
|
|
|
|
/** Sale header embedded in the full customer: no sale lines, they come from `GET /sales/:sale_id`. */
|
|
export const customerSaleHiboutikSchema = z.object({
|
|
sale_id: hiboutikId,
|
|
});
|
|
|
|
/** Full customer as returned by Hiboutik `GET /customer/:customers_id`, with its addresses and sales. */
|
|
export const fullCustomerHiboutikSchema = z.object({
|
|
customers_id: hiboutikId,
|
|
last_name: hiboutikText,
|
|
first_name: hiboutikText,
|
|
email: hiboutikText,
|
|
phone: hiboutikText,
|
|
/** "YYYY-MM-DD", "0000-00-00" when unknown. */
|
|
date_of_birth: hiboutikText,
|
|
/** French local time, "YYYY-MM-DD HH:mm:ss". */
|
|
updated_at: hiboutikText,
|
|
/** `undefined` when not an array: the stored addresses are then kept. */
|
|
addresses: hiboutikOptionalList(customerAddressHiboutikSchema),
|
|
/** `undefined` when not an array: the orders sync is then skipped. */
|
|
sales: hiboutikOptionalList(customerSaleHiboutikSchema),
|
|
});
|
|
|
|
export type CustomerHiboutikDto = z.output<typeof customerHiboutikSchema>;
|
|
export type CustomerAddressHiboutikDto = z.output<typeof customerAddressHiboutikSchema>;
|
|
export type CustomerAddressDetailsHiboutikDto = z.output<typeof customerAddressDetailsHiboutikSchema>;
|
|
export type CustomerSaleHiboutikDto = z.output<typeof customerSaleHiboutikSchema>;
|
|
export type FullCustomerHiboutikDto = z.output<typeof fullCustomerHiboutikSchema>;
|