diff --git a/frontend/modules/commercial/components/ClientAddressBlock.vue b/frontend/modules/commercial/components/ClientAddressBlock.vue new file mode 100644 index 0000000..5cc1ce2 --- /dev/null +++ b/frontend/modules/commercial/components/ClientAddressBlock.vue @@ -0,0 +1,280 @@ + + + diff --git a/frontend/modules/commercial/components/ClientContactBlock.vue b/frontend/modules/commercial/components/ClientContactBlock.vue new file mode 100644 index 0000000..cc0c493 --- /dev/null +++ b/frontend/modules/commercial/components/ClientContactBlock.vue @@ -0,0 +1,102 @@ + + + diff --git a/frontend/modules/commercial/components/TabPlaceholderBlank.vue b/frontend/modules/commercial/components/TabPlaceholderBlank.vue new file mode 100644 index 0000000..5375cb6 --- /dev/null +++ b/frontend/modules/commercial/components/TabPlaceholderBlank.vue @@ -0,0 +1,14 @@ + + + diff --git a/frontend/modules/commercial/composables/useClientReferentials.ts b/frontend/modules/commercial/composables/useClientReferentials.ts new file mode 100644 index 0000000..941b111 --- /dev/null +++ b/frontend/modules/commercial/composables/useClientReferentials.ts @@ -0,0 +1,141 @@ +import { ref } from 'vue' + +/** + * Charge les referentiels (listes courtes) alimentant les selects de l'ecran + * « Ajouter un client » : categories, sites, modes de TVA, delais et types de + * reglement, banques, et les listes distributeurs / courtiers. + * + * Toutes les collections sont recuperees en entier via l'echappatoire prevue + * `?pagination=false` (referentiels de quelques dizaines d'entrees max), avec + * l'en-tete `Accept: application/ld+json` impose par API Platform 4 pour obtenir + * l'enveloppe Hydra (`member`). Les valeurs d'option sont les IRI Hydra (`@id`) + * pour pouvoir etre renvoyees telles quelles dans les payloads POST/PATCH + * (relations ManyToOne / ManyToMany). + * + * Etat 100 % local a l'instance (refs) — aucune persistance URL. + */ + +/** Option generique au format attendu par MalioSelect / MalioSelectCheckbox ({ label, value }). */ +export interface RefOption { + value: string + label: string +} + +/** Option de type de reglement enrichie de son code stable (RG-1.12 / RG-1.13). */ +export interface PaymentTypeOption extends RefOption { + code: string +} + +/** Option de categorie enrichie de son code stable (filtrage RG-1.29 cote adresse). */ +export interface CategoryOption extends RefOption { + code: string +} + +/** Option de client (distributeur / courtier) — value = IRI du client lie. */ +export type ClientOption = RefOption + +interface HydraMember { + '@id': string +} + +interface CategoryMember extends HydraMember { + code: string + name: string +} + +interface SiteMember extends HydraMember { + name: string +} + +interface ReferentialMember extends HydraMember { + code: string + label: string +} + +interface ClientMember extends HydraMember { + companyName: string +} + +const LD_JSON_HEADERS = { Accept: 'application/ld+json' } + +export function useClientReferentials() { + const api = useApi() + + const categories = ref([]) + const sites = ref([]) + const tvaModes = ref([]) + const paymentDelays = ref([]) + const paymentTypes = ref([]) + const banks = ref([]) + const distributors = ref([]) + const brokers = ref([]) + + /** Recupere une collection complete (pagination desactivee) en Hydra. */ + async function fetchAll( + url: string, + query: Record = {}, + ): Promise { + const res = await api.get<{ member?: T[] }>( + url, + { pagination: 'false', ...query }, + { headers: LD_JSON_HEADERS, toast: false }, + ) + return res.member ?? [] + } + + /** + * Charge en parallele les referentiels communs (hors distributeurs/courtiers, + * charges a la demande selon la relation choisie). Les selects compta ne sont + * pertinents que si l'utilisateur a acces a l'onglet, mais le cout est + * negligeable et simplifie l'orchestration. + */ + async function loadCommon(): Promise { + const [cats, sitesList, tva, delays, types, banksList] = await Promise.all([ + fetchAll('/categories'), + fetchAll('/sites'), + fetchAll('/tva_modes'), + fetchAll('/payment_delays'), + fetchAll('/payment_types'), + fetchAll('/banks'), + ]) + + categories.value = cats.map(c => ({ value: c['@id'], label: c.name, code: c.code })) + sites.value = sitesList.map(s => ({ value: s['@id'], label: s.name })) + tvaModes.value = tva.map(t => ({ value: t['@id'], label: t.label })) + paymentDelays.value = delays.map(d => ({ value: d['@id'], label: d.label })) + paymentTypes.value = types.map(t => ({ value: t['@id'], label: t.label, code: t.code })) + banks.value = banksList.map(b => ({ value: b['@id'], label: b.label })) + } + + /** Liste des clients pouvant etre choisis comme distributeur (code DISTRIBUTEUR). */ + async function loadDistributors(): Promise { + if (distributors.value.length > 0) { + return + } + const clients = await fetchAll('/clients', { categoryCode: 'DISTRIBUTEUR' }) + distributors.value = clients.map(c => ({ value: c['@id'], label: c.companyName })) + } + + /** Liste des clients pouvant etre choisis comme courtier (code COURTIER). */ + async function loadBrokers(): Promise { + if (brokers.value.length > 0) { + return + } + const clients = await fetchAll('/clients', { categoryCode: 'COURTIER' }) + brokers.value = clients.map(c => ({ value: c['@id'], label: c.companyName })) + } + + return { + categories, + sites, + tvaModes, + paymentDelays, + paymentTypes, + banks, + distributors, + brokers, + loadCommon, + loadDistributors, + loadBrokers, + } +}