Fiche admin d'une société emprunteuse (BO Projets)¶
GET /administration/project-owner-company/:projectOwnerCompanyId, PATCH …/:projectOwnerCompanyId/identity, PATCH …/:projectOwnerCompanyId/lei, PATCH …/:projectOwnerCompanyId/banking et PUT …/:projectOwnerCompanyId/representatives/:representativeId (AdminAuthGuard) servent la fiche « Société » du BO Projets (BRI-2276 socle, BRI-2277 onglet Identité & LEI, BRI-2278 onglet RIB, BRI-2279 onglet Documents, BRI-2280 onglet Bénéficiaires effectifs). Chaque onglet de la fiche étend la vue et ajoute ses écritures dans sa propre issue.
Source : project-owner-company-administration.controller.ts, contrat projectOwnerCompanyDetail.endpoint.ts (api-communication-bricksoffice).
Vue¶
{ id, status, createdAt, company: { companyName?, siren?, legalForm?, shareCapital?, headquartersAddress?, postalCode?, city?, country? }, lei: { number?, obtainedAt?, expiresAt? }, banking?: { iban?, bic?, bankName?, accountHolder?, label? }, linkedSpvIds, representatives: [{ id, firstName?, lastName?, professionalEmail?, title?, birthDate?, birthCountry?, nationality?, residenceCountry? }], projects: [{ id, name?, category?, status, fundingAmountRequested? }] }
projects= les projets de la société (project_financing_request.companyId), du plus récent au plus ancien, avec le nom, le type et le montant demandé (fundingAmountRequested, centimes) de leur présentation et leur statut : la carte « Projets attachés » du BO.createdAt= création de la row, rendue en lecture seule (« Date de création ») : pas de date d'immatriculation stockée.companyne porte pasrcsCity(non rendu) et n'ajoute aucun champ au JSON portail.lei.obtainedAt=banking.leiObtainedAt;lei.expiresAt= obtention + 1 an (computeLeiExpiresAt), comme les listes, jamais stockée. Undraftn'a pas de blocbanking:leisort vide.banking= le RIB tel qu'il est stocké, IBAN entier (l'ops le corrige depuis la fiche). Absent endraft, possiblement{}encompletedtant que le portail n'a rien posé.bankName/accountHolder/label(libellé du compte) sont des champs additifs écrits uniquement par le PATCH ci-dessous.linkedSpvIds= lesproperties.spvIddistincts des projets financés de la société (la lignepropertiesn'existe qu'une fois le projet financé). Le BO lit les mandats SDD Lemonway de chacune viaGET /administration/spv/:spvId/sdd-mandates; vide = pas de mandat à afficher.representatives= les personnes physiques de la société, qui sont aussi ses bénéficiaires effectifs (décision BRI-2280 : pas de liste séparée). La vue expose l'identité KYB que Lemonway exige d'un UBO —birthDate,birthCountry,nationality,residenceCountry— mais ni adresse, ni lieu de naissance, niprojectUserId(une ligne ouvre la fiche utilisateur par email). Ils vivent dansproject_owner_company_representative(seuls les actifs,archivedAtnul, et seuls lesindividual: le représentant légal d'un représentantbusinessn'est pas un bénéficiaire effectif de l'emprunteuse) ;professionalEmaily est stocké sousidentity.email.title(fonction : « Président »…),birthCountryetnationalitysont des champs additifs de l'identité, écrits uniquement par le PUT ci-dessous ;residenceCountryporte un libellé (« France ») quand il vient de l'App PDP, un code alpha-2 quand il vient du BO.- 404
project-owner-company-not-found, même code que la complétion portail.
PATCH identité¶
Body strict { country?, siren?, legalForm?, shareCapital?, headquartersAddress?, postalCode?, city? } ; absent = conservé, null = effacé ; companyName (non modifiable), clé inconnue, SIREN hors 9 chiffres ou pays hors alpha-2 minuscule = 400.
- Tous statuts : corriger un SIREN sur un dossier complété est le cas ops. Hors
draft,completedCompanyexige chaque champ → unnullrépond 409company-invalid-for-status, sans effet de bord (la row resterait sinon illisible :jsonest reparsé à chaque lecture). - Lock
FOR UPDATE, mergeproduceinline,statusintouché. Auditadmin_actionproject-owner-company.update-identity, body complet (données société, pas de PII).
PATCH LEI¶
Body strict { leiNumber?, leiObtainedAt? } ; leiNumber = 20 caractères alphanumériques (forme ISO 17442, pas de contrôle GLEIF), leiObtainedAt = YYYY-MM-DD stocké à minuit UTC comme le save portail ; null = effacé.
completed/finalizeduniquement : undraftn'a pas de blocbankinget le save portail commePUT …/completereconstruisent la row sans lui → 409lei-not-editable. Surcompletedsans bloc, le bloc naît avec le LEI seul (banking.partial()).- Mêmes champs que le formulaire de finalisation portail ; la péremption n'est pas un champ : la fiche et les listes la dérivent (
+ 1 an). UnnullretireleiObtainedAt. - Audit
admin_actionproject-owner-company.update-lei, body complet.
Documents¶
Les pièces de la société (liste par fichier avec ses projets, dépôt sur les projets choisis, remplacement, retypage, suppression d'un fichier sur un seul projet, URL signée) vivent sur …/:projectOwnerCompanyId/documents* — voir Documents admin.
PUT représentant¶
Body strict { firstName, lastName, professionalEmail, title?, birthDate?, birthCountry?, nationality?, residenceCountry? } (YYYY-MM-DD, pays en alpha-2 minuscule) ; champ absent = conservé, null = retiré ; clé inconnue, email malformé ou pays hors alpha-2 = 400.
- Personnes physiques uniquement, identité et KYB seulement :
projectUserId, adresse postale et lieu de naissance sont intouchés. Aucune promotion de rôle, aucune invitation. - Tous statuts : l'ops complète l'identité KYB après que le porteur a fini (BRI-2280). Hors
draft, l'identité complétée exige une date de naissance →birthDate: nullouresidenceCountry: nullrépond 409company-invalid-for-status, sans effet de bord : une identité complétée le reste. Même règle que l'identité. - Lock
FOR UPDATEsur la société (il sérialise toute écriture sur ses représentants), mergeproduceinline sur l'identité,statusintouché. Représentant inconnu, archivé ou d'une autre société → 404representative-not-found; id non uuid → 400. - Audit
admin_actionproject-owner-company.representative-update: body réduit àfirstName+lastName(email, naissance, nationalité = PII), cible dansparams. - Pas de création depuis le BO : un représentant naît dans le portail (avec promotion / invitation à la complétion) ; le BO complète. Consommateur BO : l'onglet « Bénéficiaires effectifs » (une ligne par représentant, dialog d'édition) ; le panneau « Représentants » ouvre la fiche utilisateur.
PATCH RIB¶
Body strict { iban?, bic?, bankName?, accountHolder?, label? } ; champ absent = conservé ; bankName / accountHolder / label (libellé du compte, BO seulement) à null = retirés ; iban / bic n'acceptent jamais null ; clé inconnue = 400.
iban/bicsont validés (ibantools) et normalisés (espaces retirés, majuscules) par le contrat (iban_zod/bic_zod,api-communication-bricksoffice/src/lib) : la valeur stockée est canonique. Le portail, lui, accepte n'importe quelle chaîne — un IBAN saisi côté porteur peut donc être invalide jusqu'à sa première correction BO.finalizedseulement : jusque-là le porteur a la main sur le RIB (saveetPOST …/completeréécrivent le blocbanking) → 409company-banking-not-editable, sans effet de bord.- Lock
FOR UPDATEsur la société, mergeproduceinline champ par champ,statusintouché. Sur unecompletedsans bloc, le bloc naît ; sur unefinalized,ibanetbicrestent présents par construction — le schémafinalizedles exige, et un bloc incomplet ferait échouer la relecture de la ligne chez tous ses lecteurs. - Audit
admin_actionproject-owner-company.update-banking: body vide (IBAN = donnée bancaire), cible dansparams. - Aucune génération de mandat SDD : un changement d'IBAN rend le mandat Lemonway existant caduc, le nouveau mandat se signe depuis l'onglet Contrats du projet (BRI-2287). Le BO signale l'écart entre l'IBAN d'un mandat et le RIB.
Choix¶
- Pas de liste
beneficialOwners[]distincte : les représentants sont les bénéficiaires effectifs (Benoît, 15/09). Le modèle SPV legacy les sépare (beneficialOwnersalpha-3,birthimbriqué) : le mapping à la migration part derepresentatives, et les BE legacy non-signataires n'ont pas de cible — à trancher avec Kevin dans le mapping Bubble. - Trois PATCH sous-ressource (
/identity,/lei,/banking) plutôt qu'unPATCH /:id { company?, banking? }: une carte = un formulaire = une route, gardes de statut distinctes, unAdminActionNamepar écriture. POST …/finalization/completereconstruitbankingexplicitement au passagefinalized: il recopiebankName/accountHolder/labelplutôt que de les perdre en silence.- Les lectures
linkedSpvIdset dossiers liés vivent dansProjectOwnerCompanyRepository(elles partent de la société) plutôt que dansProjectFinancingRequestRepository, déjà à la limite de taille.