feat(front) : onglet comptabilite prestataire (ERP-144) (#106)
Auto Tag Develop / tag (push) Successful in 8s

Empilée sur ERP-143 (#105).

## Périmètre ERP-144
Onglet **Comptabilité** de l'écran `/providers/new` — gated par permission + blocs RIB conditionnels.

- Champs (`Malio*`) : SIREN / Numéro de compte / Mode de TVA (`/api/tva_modes`) / N° de TVA / Délai (`/api/payment_delays`) / Type de règlement (`/api/payment_types`) / Banque (`/api/banks`).
- **RG-3.07** : Banque visible **et** obligatoire **seulement si** Type = `VIREMENT` (affichage conditionnel + payload `bank` forcé à null sinon).
- **RG-3.08** : blocs RIB (Libellé/BIC/IBAN) affichés et requis si Type = `LCR` ; « + RIB » gated (dernier RIB complet) / Supprimer (modal). À la validation, **POST des RIB AVANT** le PATCH des scalaires (le back valide RG-3.08 sur le PATCH).
- **Gating** : onglet présent uniquement si `technique.providers.accounting.view` ; **éditable** uniquement si `.manage` (sinon lecture seule). Masqué pour Bureau/Commerciale.
- « Valider » → PATCH `/api/providers/{id}` (groupe `provider:write:accounting`) + sous-ressource RIBs (`/providers/{id}/ribs` + `/provider_ribs/{id}`). Erreurs 422 inline (scalaires) et par ligne (RIB).
- `useProviderReferentials.loadAccounting()` (chargé seulement si l'onglet est accessible). Helpers purs `utils/forms/providerAccounting.ts`.
- i18n `technique.providers.form.accounting` + `confirmDelete.rib`.

> NB : les placeholders **Rapports / Échanges** relèvent des écrans Consultation/Modification (ERP-145) — le flux de **création** ne porte que 3 onglets (Contact/Adresse/Comptabilité), conformément à la spec.

## Conformité
- `useApi()` only ; `Malio*` only ; pas de masque email ; aucun texte FR en dur ; pas d'import inter-module (helpers ré-implémentés côté Technique, règle ABSOLUE n°1).

## Vérifications
- Vitest : 454/454 (18 nouveaux : helpers compta RG-3.07/3.08, workflow VIREMENT/LCR, ordre RIB→scalaires, 422 inline + par ligne, lecture seule sans manage).
- ESLint : OK.
- `nuxi typecheck` : 0 erreur sur les fichiers source du ticket.
- Golden path navigateur : page compile, onglet Comptabilité visible (gating accounting.view OK pour admin). Contenu de l'onglet gaté derrière le déverrouillage des 3 onglets (multiselect `Malio` non pilotable en a11y) — couvert par les tests unitaires + typecheck.

Reviewed-on: #106
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #106.
This commit is contained in:
2026-06-15 09:15:20 +00:00
committed by Autin
parent 17aa61d014
commit c25c33116d
8 changed files with 793 additions and 5 deletions
@@ -0,0 +1,83 @@
import { describe, it, expect } from 'vitest'
import {
buildProviderAccountingPayload,
buildProviderRibPayload,
isBankRequiredForPaymentType,
isRibBlank,
isRibComplete,
isRibRequiredForPaymentType,
} from '../providerAccounting'
import { emptyProviderAccounting, emptyProviderRib } from '~/modules/technique/types/providerForm'
/**
* Helpers purs de l'onglet Comptabilite prestataire (ERP-144) : RG inter-champs
* RG-3.07 (banque si VIREMENT) / RG-3.08 (RIB si LCR) + construction des payloads.
*/
describe('providerAccounting helpers', () => {
describe('RG-3.07 / RG-3.08 — type de reglement', () => {
it('banque requise uniquement pour VIREMENT', () => {
expect(isBankRequiredForPaymentType('VIREMENT')).toBe(true)
expect(isBankRequiredForPaymentType('LCR')).toBe(false)
expect(isBankRequiredForPaymentType('CHEQUE')).toBe(false)
expect(isBankRequiredForPaymentType(null)).toBe(false)
})
it('RIB requis uniquement pour LCR', () => {
expect(isRibRequiredForPaymentType('LCR')).toBe(true)
expect(isRibRequiredForPaymentType('VIREMENT')).toBe(false)
expect(isRibRequiredForPaymentType(null)).toBe(false)
})
})
describe('isRibBlank / isRibComplete', () => {
it('un RIB vierge est vide et incomplet', () => {
expect(isRibBlank(emptyProviderRib())).toBe(true)
expect(isRibComplete(emptyProviderRib())).toBe(false)
})
it('un RIB partiel n\'est ni vide ni complet', () => {
const rib = { ...emptyProviderRib(), iban: 'FR76...' }
expect(isRibBlank(rib)).toBe(false)
expect(isRibComplete(rib)).toBe(false)
})
it('un RIB avec libelle + BIC + IBAN est complet', () => {
const rib = { ...emptyProviderRib(), label: 'Compte', bic: 'BNPAFRPP', iban: 'FR76...' }
expect(isRibComplete(rib)).toBe(true)
})
})
describe('buildProviderAccountingPayload (RG-3.07)', () => {
it('envoie la banque si requise (VIREMENT)', () => {
const payload = buildProviderAccountingPayload({
...emptyProviderAccounting(),
paymentTypeIri: '/api/payment_types/3',
bankIri: '/api/banks/2',
}, true)
expect(payload.bank).toBe('/api/banks/2')
expect(payload.paymentType).toBe('/api/payment_types/3')
})
it('force la banque a null si non requise (hors VIREMENT)', () => {
const payload = buildProviderAccountingPayload({
...emptyProviderAccounting(),
bankIri: '/api/banks/2',
}, false)
expect(payload.bank).toBeNull()
})
})
describe('buildProviderRibPayload', () => {
it('omet les champs requis vides (NotBlank back joue sur le champ)', () => {
const payload = buildProviderRibPayload(emptyProviderRib())
expect(payload).not.toHaveProperty('label')
expect(payload).not.toHaveProperty('bic')
expect(payload).not.toHaveProperty('iban')
})
it('conserve les champs remplis', () => {
const payload = buildProviderRibPayload({ ...emptyProviderRib(), label: 'Compte', bic: 'BNPAFRPP', iban: 'FR76...' })
expect(payload).toEqual({ label: 'Compte', bic: 'BNPAFRPP', iban: 'FR76...' })
})
})
})