retours-d-execution
Événement (AsyncAPI) — version 0.2.0. Producteur : carnet-ordres. Consommateurs déclarés : operations.
CE QUE LE CARNET RENVOIE À OPÉRATIONS. « Opérations publie ce qui doit être exécuté ; le carnet d’ordres dit ce qu’il en advient. » CES FAITS NE SONT PAS COMPTABILISABLES : ils disent l’AVANCEMENT, pas le fait définitif. La comptabilisation passe par le canal carnet-ordres.compte-rendu-execution, sous une règle de finalité versionnée — les mêler ouvrirait la porte à une comptabilisation prématurée. TOUTE ANNONCE PRISE EN CHARGE OBTIENT UNE ISSUE EXPLICITE : exécutée, rejetée, reportée, annulée — jamais d’abandon silencieux.
La spécification
Section intitulée « La spécification »asyncapi: 3.0.0info: title: carnet-ordres — les retours d'exécution version: 0.2.0 x-ruptures: - version: 0.2.0 rupture: "propriété retirée : tenant" motif: >- Isolation forte par tenant — aucun identifiant de tenant n'est transporté ; l'appartenance est celle de l'installation (précédent instruments 0.4.0). description: >- CE QUE LE CARNET RENVOIE À OPÉRATIONS. « Opérations publie ce qui doit être exécuté ; le carnet d'ordres dit ce qu'il en advient. » CES FAITS NE SONT PAS COMPTABILISABLES : ils disent l'AVANCEMENT, pas le fait définitif. La comptabilisation passe par le canal carnet-ordres.compte-rendu-execution, sous une règle de finalité versionnée — les mêler ouvrirait la porte à une comptabilisation prématurée. TOUTE ANNONCE PRISE EN CHARGE OBTIENT UNE ISSUE EXPLICITE : exécutée, rejetée, reportée, annulée — jamais d'abandon silencieux. x-producteurs: - carnet-ordres x-consommateurs: - operationsdefaultContentType: application/json
channels:
retoursDExecution: address: carnet-ordres.retour-execution description: >- Les retours vers Opérations. UN CANAL, UN CONSOMMATEUR : les obligations de règlement vers la banque et les comptes rendus vers la tenue de compte ont les leurs. messages: ordrePrisEnCharge: { $ref: '#/components/messages/ordrePrisEnCharge' } ordreRejete: { $ref: '#/components/messages/ordreRejete' } ordreExecute: { $ref: '#/components/messages/ordreExecute' } reliquatReporte: { $ref: '#/components/messages/reliquatReporte' } annulationTraitee: { $ref: '#/components/messages/annulationTraitee' } ordreSuspendu: { $ref: '#/components/messages/ordreSuspendu' }
operations:
publierLesRetoursDExecution: action: send channel: { $ref: '#/channels/retoursDExecution' } summary: >- Faire avancer les jambes d'Opérations sans jamais rien comptabiliser. Le carnet NE MODIFIE JAMAIS l'opération pour la rendre exécutable : il retourne de quoi décider. messages: - $ref: '#/channels/retoursDExecution/messages/ordrePrisEnCharge' - $ref: '#/channels/retoursDExecution/messages/ordreRejete' - $ref: '#/channels/retoursDExecution/messages/ordreExecute' - $ref: '#/channels/retoursDExecution/messages/reliquatReporte' - $ref: '#/channels/retoursDExecution/messages/annulationTraitee' - $ref: '#/channels/retoursDExecution/messages/ordreSuspendu'
components:
messages:
ordrePrisEnCharge: name: ordre-pris-en-charge title: Ordre pris en charge summary: >- Le carnet s'est saisi de l'annonce et lui a calculé une échéance. CELA NE GARANTIT NI LA RÉCEPTION EXTERNE NI L'EXÉCUTION. contentType: application/json payload: { $ref: '#/components/schemas/ordrePrisEnCharge' }
ordreRejete: name: ordre-rejete title: Ordre rejeté summary: >- L'annonce est inexécutable dans le contexte évalué. Le carnet PEUT REJETER une annonce qu'Opérations avait jugée recevable, si le contexte d'exécution a changé ; il ne rouvre jamais la recevabilité métier. contentType: application/json payload: { $ref: '#/components/schemas/ordreRejete' }
ordreExecute: name: ordre-execute title: Ordre exécuté summary: >- Une tranche est exécutée, VENTILÉE PAR INSTRUCTION D'ORIGINE. Une exécution PARTIELLE ne vaut JAMAIS exécution totale. contentType: application/json payload: { $ref: '#/components/schemas/ordreExecute' }
reliquatReporte: name: reliquat-reporte title: Reliquat reporté summary: >- La part non servie est affectée à une échéance ultérieure. UN RELIQUAT NÉ D'UN ORDRE EN MONTANT RESTE EN MONTANT : il s'exécutera à un autre prix. contentType: application/json payload: { $ref: '#/components/schemas/reliquatReporte' }
annulationTraitee: name: annulation-traitee title: Annulation traitée summary: >- La suite donnée à une demande d'annulation. AVANT REMISE elle est appliquée ; APRÈS REMISE elle devient une demande adressée à la place ; APRÈS EXÉCUTION elle est refusée — seule la compensation reste possible, et elle appartient à Opérations. contentType: application/json payload: { $ref: '#/components/schemas/annulationTraitee' }
ordreSuspendu: name: ordre-suspendu title: Ordre suspendu ou repris summary: >- Le traitement est bloqué ou reprend. LE MOTIF D'UNE SUSPENSION DE CONFORMITÉ NE REMONTE JAMAIS : la suspension se voit, elle ne s'explique pas. contentType: application/json payload: { $ref: '#/components/schemas/ordreSuspendu' }
schemas:
enveloppe: type: object required: [evenementId, typeEvenement, versionContrat, dateEvenement, producteur, ordreRef, operationRef, instructionRef, cleIdempotence] properties: evenementId: type: string description: >- Identifiant unique — LA CLÉ D'IDEMPOTENCE DES CONSOMMATEURS. La livraison est au moins une fois : le rejeu ne doit produire qu'un effet. typeEvenement: { type: string } versionContrat: { type: string } dateEvenement: { type: string, format: date-time } producteur: { type: string, const: carnet-ordres } sequenceEntite: type: integer description: Numéro de séquence PAR ORDRE INDIVIDUEL et par producteur. ordreRef: { type: string, description: 'Identifiant publié de l''ordre chez le carnet.' } annonceRef: { type: string } operationRef: { type: string } instructionRef: type: string description: >- L'INSTRUCTION D'ORIGINE. C'est elle qui rend le dépouillement exact : Opérations n'a jamais à redescendre d'un agrégat. cleIdempotence: { type: string }
ordrePrisEnCharge: allOf: - $ref: '#/components/schemas/enveloppe' - type: object required: [dateCentralisation, heureLimite, fuseauHeureLimite] properties: centralisationRef: { type: string } dateCentralisation: { type: string, format: date } dateVlAttendue: { type: string, format: date } dateReglementPrevue: { type: string, format: date } heureLimite: { type: string, format: date-time } fuseauHeureLimite: type: string description: >- Fuseau IANA. Un ordre reçu à 12 h 01 heure de Paris n'est pas le même fait qu'un ordre reçu à 12 h 01 heure de Luxembourg. reservationAccordee: { type: boolean }
ordreRejete: allOf: - $ref: '#/components/schemas/enveloppe' - type: object required: [motif, explication] properties: motif: type: string enum: [DOUBLON_CONFLICTUEL, SUPPORT_NON_ORDONNABLE, OFFRE_NON_ACTIVE, POSITION_INSUFFISANTE, POSITION_GELEE, RESERVATION_IMPOSSIBLE, HEURE_LIMITE_DEPASSEE, ECHEANCE_INTROUVABLE, CHAINE_INDISPONIBLE, SERVICE_NON_ACTIVE, VL_INDISPONIBLE, REJETE_EXTERNE] description: 'Code STABLE, séparé du libellé utilisateur.' explication: type: string description: 'En français métier — ce qui rend le rejet explicable des années plus tard.' versionsUtilisees: type: object description: >- Les versions consommées par la décision — support, offre, position. C'est ce qui la rend REJOUABLE À L'IDENTIQUE. Jamais le dispositif : il ne parvient pas au carnet. additionalProperties: { type: string }
ordreExecute: allOf: - $ref: '#/components/schemas/enveloppe' - type: object required: [nature, quantiteExecuteeUpm, montantExecuteCt, dateExecution, montantDemandeInitialCt] properties: nature: type: string enum: [PARTIELLE, TOTALE] description: >- UNE EXÉCUTION PARTIELLE NE VAUT JAMAIS EXÉCUTION TOTALE, et la clôture ne se déduit jamais du premier retour. quantiteExecuteeUpm: { type: integer, format: int64, description: 'En MILLIONIÈMES de part.' } montantExecuteCt: { type: integer, format: int64, description: 'En CENTIMES, brut.' } montantDemandeInitialCt: type: integer format: int64 description: >- LA DEMANDE INITIALE RAPPELÉE dans le message. C'est ce qui rend la conservation — exécutions + rejets + reliquats = demande — vérifiable DEPUIS LE FLUX SEUL, sans consultation. quantiteDemandeeInitialeUpm: { type: integer, format: int64 } vlUe6: type: integer format: int64 description: 'Valeur liquidative appliquée, en MICRO-EUROS. Fil rouge : 24381200.' dateVl: { type: string, format: date } prixMoyenUe6: { type: integer, format: int64, description: 'Chaîne titres.' } dateExecution: { type: string, format: date } motifReduction: type: string enum: [PLAFONNEMENT_RACHATS, EXECUTION_PARTIELLE_MARCHE, SUSPENSION_SUPPORT] description: 'Présent seulement quand la tranche est réduite.' frais: type: array description: 'Les frais par nature, EN LIGNES — jamais en colonnes.' items: type: object required: [nature, montantCt] properties: nature: type: string enum: [DROITS_ENTREE, DROITS_SORTIE, FRAIS_BOURSE, COMMISSION, TAXE_TRANSACTION] montantCt: { type: integer, format: int64 }
reliquatReporte: allOf: - $ref: '#/components/schemas/enveloppe' - type: object required: [revocable] properties: montantRestantCt: type: integer format: int64 description: >- UN RELIQUAT NÉ D'UN ORDRE EN MONTANT RESTE EN MONTANT : il s'exécutera à un AUTRE PRIX, et figer une quantité serait faux. quantiteRestanteUpm: { type: integer, format: int64 } prochaineDateVlAttendue: { type: string, format: date } prochaineCentralisationRef: { type: string } revocable: type: boolean description: >- Un reliquat NON RÉVOCABLE ne s'annule pas par une action locale : la demande d'annulation est rejetée avec la source de la règle. representationNo: { type: integer } maxRepresentations: { type: integer } motif: type: string enum: [PLAFONNEMENT_RACHATS, EXECUTION_PARTIELLE_MARCHE]
annulationTraitee: allOf: - $ref: '#/components/schemas/enveloppe' - type: object required: [demandeVisee, suite] properties: demandeVisee: { type: string } suite: type: string enum: [APPLIQUEE, TRANSMISE_A_LA_PLACE, CONFIRMEE_PAR_LA_PLACE, REFUSEE_PAR_LA_PLACE, IMPOSSIBLE_APRES_EXECUTION, IMPOSSIBLE_RELIQUAT_NON_REVOCABLE] description: >- UNE ANNULATION REÇOIT UNE SUITE, JAMAIS UN SILENCE. Après exécution, seule la compensation reste possible — et elle appartient à Opérations. motif: { type: string } reservationLiberee: { type: boolean }
ordreSuspendu: allOf: - $ref: '#/components/schemas/enveloppe' - type: object required: [sens, origine] properties: sens: type: string enum: [SUSPENDU, REPRIS] origine: type: string enum: [CONFORMITE, DONNEE, TECHNIQUE, EXTERNE] motif: type: string description: >- ABSENT ET INTERDIT quand l'origine est CONFORMITE : le cloisonnement du domaine Conformité vaut jusque dans ce contrat. Présent sinon.