feat(sync): synchronize Hiboutik data through POST /sync

POST /sync starts the full synchronization in the background (202, or 409 when one is already running). Entities are synced in dependency order: brands, categories, suppliers, products (with versions and stocks), customers. Each row is synced independently so that a failure is logged without stopping the rest. Replaces the default AppService.
This commit is contained in:
MastersGame
2026-11-05 23:00:49 +01:00
parent 27d0e0149d
commit c5f65dcb62
17 changed files with 878 additions and 34 deletions

View File

@@ -0,0 +1,49 @@
import { Injectable, Logger } from '@nestjs/common';
import { CustomerHiboutikRepository } from '../../hiboutik/customer.hiboutik.repository';
import { CustomerDatabaseRepository } from '../../database/customer.database.repository';
import { CustomerConverter } from '../../models/converter/customer.converter';
@Injectable()
export class CustomerHiboutikService {
private readonly logger = new Logger('CustomerHiboutikService');
constructor(
private readonly customerHiboutikRepository: CustomerHiboutikRepository,
private readonly customerDatabaseRepository: CustomerDatabaseRepository,
private readonly customerConverter: CustomerConverter,
) {}
/**
* Mirrors Hiboutik customers: deletes the customers missing from Hiboutik, then fetches and upserts each of them
* (one API call per customer, as the list endpoint lacks addresses and sales).
*/
async syncCustomers() {
const hiboutikCustomers = await this.customerHiboutikRepository.getCustomers();
// Safety net: an empty answer from Hiboutik most likely means an API issue
let nbDeleted = 0;
if (hiboutikCustomers.length === 0) {
this.logger.warn('Hiboutik returned no customers: deletion skipped.');
} else {
nbDeleted = await this.customerDatabaseRepository.deleteMissing(hiboutikCustomers.map((dto) => Number(dto.customers_id)));
}
let nbFailed = 0;
for (const hiboutikCustomer of hiboutikCustomers) {
try {
const hiboutikFullCustomer = await this.customerHiboutikRepository.getCustomerById(hiboutikCustomer.customers_id);
await this.customerDatabaseRepository.upsert(this.customerConverter.fromHiboutik(hiboutikFullCustomer));
// retreive addresses DTOs
// save addresses
// retreive orders DTOs
// save orders
} catch (error) {
nbFailed++;
this.logger.error(`Failed to sync Hiboutik customer ${hiboutikCustomer.customers_id}`, (error as Error)?.stack);
}
}
this.logger.log(
`Customers sync ended: ${hiboutikCustomers.length - nbFailed}/${hiboutikCustomers.length} synced, ${nbFailed} failed, ${nbDeleted} deleted.`,
);
}
}