Files
dinggadingga-backend/src/models/hiboutikDTO/customer.hiboutik.dto.ts
MastersGame 35a7c27cb7 feat(sync): validate Hiboutik payloads, read French dates and guard deletions
- 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>
2026-11-08 12:20:58 +01:00

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>;