Life ConnectLife Connect
Wiki index
Architecture
Services
UI
Concepts
Runbooks
Infra
Swagger Docs
GitHub
Wiki index
Architecture
Services
UI
Concepts
Runbooks
Infra
Swagger Docs
GitHub
  • Cross-cutting concepts

    • EventBridge fanout
    • Atlas Triggers
    • ECS Fargate deploy lifecycle
    • Keycloak hosting
    • Cloudflare Pages auto-deploy
    • Bitbucket Pipelines (legacy)
    • Terraform-managed envs (legacy)
  • Accounting model

    • Accounting model
    • Events catalog
    • Accounting journals
    • Plan of accounts (PCG)
    • Auxiliary accounts
    • FEC fields (Fichier des Écritures Comptables)
    • pieceReference numbering
    • Tenant matrix (payable / receivable)
    • Owner matrix (payable / receivable)
    • Take-over UI display rules
    • Engine functional audit (2026-07) ↗
Last updated 2026-10-11

Cross-service BDD scenario catalog (EN/FR)

AI-generated content

This document was generated by an AI assistant. Verify accuracy before relying on the details.

The 140 client-readable scenarios run by the @adb/e2e-api harness against the services' real Hono apps, in English (canonical) and French. The English text is exactly what the .feature files execute; the French column is the translation maintained per service in scenarios/<svc>.json.

Generated by node tools/scenario-catalog.mjs from the @i18n: tags in tests/e2e-api/features/*.feature and the per-service EN/FR seeds in adb-doc/docs/concepts/testing/scenarios/<svc>.json. Do not edit by hand — re-run the generator. The @adb/e2e-api test scenario-i18n.test.mjs fails if a node is untagged, a translation is missing, the English seed drifts from a title, or this page is stale.

Aggregates API — read-side person, contract and part projections for an organization

FR — API Agrégats — projections en lecture des personnes, contrats et lots pour une organisation

Key: @i18n:aggregates · Source: tests/e2e-api/features/aggregates.feature

A person aggregate gathers the bank contracts received by the person

FR — Un agrégat de personne rassemble les contrats bancaires reçus par la personne

Key: @i18n:aggregates.person_with_bank_contracts

  • Given a natural person "p1" named "Alice Martin"
  • And a "BANK" contract "bank1" from supplier "x" to receiver "p1"
  • And a "BANK" contract "bank2" from supplier "x" to receiver "other"
  • When they fetch the person aggregate for "p1"
  • Then the aggregate request succeeds
  • And the aggregate has id "p1"
  • And the aggregate bank contracts are "bank1"

The supplier route computes the same person aggregate

FR — La route fournisseur calcule le même agrégat de personne

Key: @i18n:aggregates.supplier_person_route

  • Given a natural person "s1" named "Supplier SARL"
  • When they fetch the supplier aggregate for "s1"
  • Then the aggregate request succeeds
  • And the aggregate has id "s1"

A missing person's aggregate is not found, as on the JVM

FR — L'agrégat d'une personne introuvable renvoie une erreur introuvable, comme sur la JVM

Key: @i18n:aggregates.missing_person_is_null

  • When they fetch the person aggregate for "ghost"
  • Then the JVM's not-found error names "ghost"

A rental contract aggregate resolves its parties, parts and tenant bank accounts

FR — Un agrégat de contrat de location résout ses parties, ses lots et les comptes bancaires du locataire

Key: @i18n:aggregates.contract_resolves_parties_and_parts

  • Given a natural person "owner1" named "Owner"
  • And a natural person "tenant1" named "Tenant"
  • And a building "b1" named "Block A"
  • And building "b1" has the primary postal address "12 rue des Lilas", "75001", "Paris"
  • And a unit "u1" under building "b1"
  • And a "RENTAL" contract "rc1" from supplier "owner1" to receiver "tenant1" on part "u1"
  • And a "BANK" contract "bank-t" from supplier "x" to receiver "tenant1"
  • When they fetch the contract aggregate for "rc1"
  • Then the aggregate request succeeds
  • And the aggregate supplier is named "Owner"
  • And the aggregate receiver is named "Tenant"
  • And the aggregate first related part carries the address "12 rue des Lilas , 75001 Paris"
  • And the aggregate tenant bank contracts are "bank-t"
  • And the aggregate contract type is "RentalContract"
  • And the aggregate concrete type is "RENTAL"

A rental contract aggregate ships JVM-shaped dates, a current date and filtered rent amounts

FR — Un agrégat de contrat de location renvoie des dates au format JVM, une date du jour et des loyers filtrés

Key: @i18n:aggregates.contract_dates_and_rent_amounts

  • Given a natural person "ownerz" named "Owner Z"
  • And a natural person "tenantz" named "Tenant Z"
  • And a "RENTAL" contract "rcz" from supplier "ownerz" to receiver "tenantz" beginning "2021-09-30T22:00:00.000Z" and ending "2024-11-20T23:00:00.000Z"
  • When they fetch the contract aggregate for "rcz"
  • Then the aggregate request succeeds
  • And the aggregate date "begin" is "01/10/2021"
  • And the aggregate date "end" is "21/11/2024"
  • And the aggregate date "initialCallForRentDate" is "2021-10-01"
  • And the aggregate carries a current date
  • And the aggregate rent amount event types are "CFR_RENT"
  • And the first aggregate rent amount is effective "2021-10-01" and priced in "QUANTITY.EURO"

A rental contract aggregate computes its payment total and its fees with VAT from the stored amounts

FR — Un agrégat de contrat de location calcule son total à payer et ses honoraires TTC à partir des montants enregistrés

Key: @i18n:aggregates.lease_money_members

  • Given a natural person "ownerm" named "Owner M"
  • And a natural person "tenantm" named "Tenant M"
  • And a "RENTAL" contract "rcfee" from supplier "ownerm" to receiver "tenantm" with a rent of "800" and charges of "50.5" euros
  • And contract "rcfee" charges a landlord mediation fee of "33.33" euros
  • When they fetch the contract aggregate for "rcfee"
  • Then the aggregate request succeeds
  • And the aggregate amount "paymentTotal" is "850.5" euros
  • And the aggregate amount "rentalMediationLandlordFeesWithVatCalc" is "40" euros
  • And the aggregate amount "rentalLandlordFeesTotalWithoutVatCalc" is "33.33" euros
  • And the aggregate amount "rentalLandlordFeesTotalWithVatCalc" is "40" euros

An unknown contract id is reported as not found

FR — Un identifiant de contrat inconnu est signalé comme introuvable

Key: @i18n:aggregates.missing_contract_is_404

  • When they fetch the contract aggregate for "missing"
  • Then the aggregate request is not found

A unit aggregate resolves its parent building, related contracts and people

FR — Un agrégat de lot résout son bâtiment parent, ses contrats liés et les personnes associées

Key: @i18n:aggregates.part_resolves_parent_and_contracts

  • Given a building "b1" named "Block A"
  • And a unit "u1" under building "b1"
  • And a natural person "ten1" named "Tenant One"
  • And a "RENTAL" contract "c1" from supplier "owner1" to receiver "ten1" on part "u1"
  • When they fetch the part aggregate for "u1"
  • Then the aggregate request succeeds
  • And the aggregate has id "u1"
  • And the aggregate parent has id "b1"
  • And the aggregate related contracts are "c1"
  • And the aggregate related persons include "Tenant One"

A person with no contracts still yields a fully shaped, empty aggregate

FR — Une personne sans contrat produit tout de même un agrégat complet mais vide

Key: @i18n:aggregates.person_without_contracts_is_empty

  • Given a natural person "lone1" named "Lonely Lucie"
  • When they fetch the person aggregate for "lone1"
  • Then the aggregate request succeeds
  • And the aggregate has id "lone1"
  • And the aggregate has no bank contracts
  • And the aggregate has no active rental allowance contract

A rental contract aggregate resolves every related unit and its parent building

FR — Un agrégat de contrat de location résout chaque lot lié et son bâtiment parent

Key: @i18n:aggregates.contract_resolves_multiple_parts

  • Given a natural person "owner2" named "Owner Two"
  • And a natural person "tenant2" named "Tenant Two"
  • And a building "bm" named "Block M"
  • And building "bm" has the primary postal address "3 avenue Foch", "69006", "Lyon"
  • And a unit "um1" under building "bm"
  • And a unit "um2" under building "bm"
  • And a "RENTAL" contract "rcm" from supplier "owner2" to receiver "tenant2" on parts "um1,um2"
  • When they fetch the contract aggregate for "rcm"
  • Then the aggregate request succeeds
  • And the aggregate related parts are "um1,um2"
  • And every aggregate related part carries the address "3 avenue Foch , 69006 Lyon"

A rental-delegate contract files the receiver bank accounts under the owner side and resolves no parts

FR — Un contrat de délégation de gestion classe les comptes bancaires du receveur du côté propriétaire et ne résout aucun lot

Key: @i18n:aggregates.rental_delegate_resolves_owner_bank

  • Given a natural person "deleg1" named "Delegate"
  • And a natural person "ownerd" named "Owner D"
  • And a building "bd" named "Block D"
  • And a unit "ud" under building "bd"
  • And a "RENTAL_DELEGATE" contract "rd1" from supplier "deleg1" to receiver "ownerd" on part "ud"
  • And a "BANK" contract "bank-d" from supplier "x" to receiver "ownerd"
  • When they fetch the contract aggregate for "rd1"
  • Then the aggregate request succeeds
  • And the aggregate owner bank contracts are "bank-d"
  • And the aggregate has no tenant bank contracts
  • And the aggregate has no related parts
  • And the aggregate contract type is "RentalDelegateContract"
  • And the aggregate concrete type is "RENTAL_DELEGATE"

A top-level building aggregate exposes no parent and an empty contract graph

FR — Un agrégat de bâtiment de premier niveau n'expose aucun parent et un graphe de contrats vide

Key: @i18n:aggregates.top_level_building_has_no_parent

  • Given a building "btop" named "Standalone"
  • When they fetch the part aggregate for "btop"
  • Then the aggregate request succeeds
  • And the aggregate has id "btop"
  • And the aggregate has no parent
  • And the aggregate related contracts are empty
  • And the aggregate related persons are empty

An unknown part id is reported as not found

FR — Un identifiant de lot inconnu est signalé comme introuvable

Key: @i18n:aggregates.missing_part_is_404

  • When they fetch the part aggregate for "no-such-part"
  • Then the aggregate request is not found

Public API — what an external project may do through the one public door

FR — API publique — ce qu'un projet externe peut faire par l'unique porte publique

Key: @i18n:api · Source: tests/e2e-api/features/api.feature

A tenant reads their own tickets through the public API, while the organization's ticket search stays refused to them

FR — Un locataire consulte ses propres tickets via l'API publique, tandis que la recherche des tickets de l'organisation lui reste refusée

Key: @i18n:api.tenant_reads_tickets

  • Given the caller is a signed-in tenant
  • And a person record exists for the tenant's email
  • And an incident ticket "Leaking tap in the bathroom" was declared for the tenant
  • When they list their own tickets
  • Then the request succeeds
  • And the ticket results include "Leaking tap in the bathroom"
  • And the ticket results do not include "Water leak in the kitchen"
  • And the tickets service saw the tenant route under the gateway's service identity
  • When they search the tickets for "leak"
  • Then the tickets service refuses it for want of the scope "tickets.list"

A tenant reads their own person record through the public API

FR — Un locataire consulte sa propre fiche personne via l'API publique

Key: @i18n:api.tenant_reads_own_record

  • Given the caller is a signed-in tenant
  • And a person record exists for the tenant's email
  • When they read their own record
  • Then the request succeeds
  • And the record carries the tenant's name

A tenant declares an incident on their own tickets through the public API, while a declaration on the organization's tickets stays refused to them

FR — Un locataire déclare un incident sur ses propres tickets via l'API publique, tandis qu'une déclaration sur les tickets de l'organisation lui reste refusée

Key: @i18n:api.tenant_creates_incident

  • Given the caller is a signed-in tenant
  • And a person record exists for the tenant's email
  • When they declare an incident "Something filed on the organization"
  • Then the tickets service refuses it for want of the scope "tickets.incidents.create"
  • And no ticket "Something filed on the organization" was stored
  • When they declare an incident "Broken boiler in flat 3" on their own tickets
  • Then the ticket is created
  • And the new ticket is open
  • And the new ticket names the tenant
  • And the tickets service saw the tenant route under the gateway's service identity

A technical client's ticket search for the organization it was granted passes the public door and is refused by the tickets service

FR — La recherche de tickets d'un client technique pour l'organisation qui lui a été accordée franchit la porte publique et est refusée par le service tickets

Key: @i18n:api.client_reads_tickets

  • Given the caller is the technical client
  • When they search the tickets for "leak"
  • Then the tickets service refuses it for want of the scope "tickets.list"

A technical client cannot create a ticket

FR — Un client technique ne peut pas créer de ticket

Key: @i18n:api.client_cannot_create_ticket

  • Given the caller is the technical client
  • When they declare an incident "Something they should not file"
  • Then the request is forbidden because the endpoint is reserved for end users

A technical client must name the organization it acts for

FR — Un client technique doit nommer l'organisation pour laquelle il agit

Key: @i18n:api.client_must_name_its_organization

  • Given the caller is the technical client
  • When they search the tickets for "leak" without naming an organization
  • Then the request is forbidden because no organization was named

A technical client is refused an organization it was not granted

FR — Un client technique se voit refuser une organisation qui ne lui a pas été accordée

Key: @i18n:api.client_refused_other_organization

  • Given the caller is the technical client
  • When they search the tickets of organization "org-999"
  • Then the request is forbidden because that organization was not granted

A tenant finds the ticket they declared, by its identifier, among their own tickets through the public API, while the read by identifier stays refused to them

FR — Un locataire retrouve le ticket qu'il a déclaré, par son identifiant, parmi ses propres tickets via l'API publique, tandis que la lecture par identifiant lui reste refusée

Key: @i18n:api.tenant_reads_one_ticket

  • Given the caller is a signed-in tenant
  • And a person record exists for the tenant's email
  • When they declare an incident "Broken boiler in flat 3" on their own tickets
  • And they list their own tickets
  • Then the ticket they declared is among their own tickets, with the subject "Broken boiler in flat 3"
  • When they read the ticket they declared by its identifier
  • Then the tickets service refuses it for want of the scope "tickets.read"

A tenant cannot read another tenant's tickets through the public API

FR — Un locataire ne peut pas consulter les tickets d'un autre locataire via l'API publique

Key: @i18n:api.tenant_cannot_read_another_tenants_tickets

  • Given the caller is a signed-in tenant
  • And a person record exists for the tenant's email
  • And an incident ticket "Noisy neighbour upstairs" was declared for another tenant
  • When they list the tickets of that other tenant
  • Then the request is forbidden because they may only reach their own tickets
  • And the tickets service was not called

An identifier that is not a ticket identifier reaches nothing

FR — Un identifiant qui n'est pas un identifiant de ticket n'atteint rien

Key: @i18n:api.ticket_identifier_must_be_an_object_id

  • Given the caller is a signed-in tenant
  • When they call "/tickets/not-an-object-id"
  • Then the route is not found
  • When they call "/tickets/incidents/not-an-object-id"
  • Then the route is not found
  • When they call "/tickets/not-an-object-id/events"
  • Then the route is not found

The tickets service's own literal routes are not part of the public surface

FR — Les routes littérales propres au service tickets ne font pas partie de la surface publique

Key: @i18n:api.tickets_service_literals_are_not_public

  • Given the caller is a signed-in tenant
  • When they call "/tickets/info"
  • Then the route is not found
  • When they call "/tickets/delegatees"
  • Then the route is not found
  • When they call "/tickets/incidents/common"
  • Then the route is not found

A route that is not on the allowlist does not exist

FR — Une route absente de la liste d'autorisation n'existe pas

Key: @i18n:api.unlisted_route_is_not_found

  • Given the caller is a signed-in tenant
  • When they call "/files/some-document"
  • Then the route is not found
  • When they call "/persons/tenants"
  • Then the route is not found

A caller with no token is refused at the door

FR — Un appelant sans jeton est refusé à la porte

Key: @i18n:api.anonymous_is_refused

  • Given the caller has no token
  • When they search the tickets for "leak"
  • Then the request is unauthorized

An agency user reads any tenant's rented parts through the first-party tier

FR — Un utilisateur d'agence consulte les lots loués de n'importe quel locataire via le palier first-party

Key: @i18n:api.first_party_reads_any_tenants_parts

  • Given the caller is an agency user in adb-ui
  • When they call "/contracts/parts/tenants/someone-else"
  • Then the request succeeds
  • And the contracts service saw the path "/contracts/parts/tenants/someone-else"
  • And no ownership lookup was made

An agency user reaches /parts, which the public tier does not have

FR — Un utilisateur d'agence atteint /parts, que le palier public ne possède pas

Key: @i18n:api.first_party_reaches_parts

  • Given the caller is an agency user in adb-ui
  • When they register a property named "Résidence du Port"
  • Then the part is created
  • When they search the parts for "Port"
  • Then the part results include "Résidence du Port"

A tenant on the public tier still gets 404 on /parts

FR — Un locataire sur le palier public obtient toujours un 404 sur /parts

Key: @i18n:api.public_tier_has_no_parts

  • Given the caller is a signed-in tenant
  • When they call "/parts"
  • Then the route is not found

Files API — storing document metadata and serving downloads for an organization

FR — API Fichiers — stocker les métadonnées de documents et servir les téléchargements pour une organisation

Key: @i18n:files · Source: tests/e2e-api/features/files.feature

An agency registers file metadata and finds it again

FR — Une agence enregistre les métadonnées d'un fichier et les retrouve

Key: @i18n:files.register_and_refetch_metadata

  • When they register metadata for a "facture-2024-03.pdf" invoice on asset "asset-1"
  • Then the file metadata is created
  • And the saved metadata belongs to organization "org-88"
  • When they fetch that file metadata by id
  • Then the returned metadata is for file "facture-2024-03.pdf"

Uploaded files are searchable within the agency's organization

FR — Les fichiers téléversés sont recherchables au sein de l'organisation de l'agence

Key: @i18n:files.search_metadata_in_org

  • Given an uploaded file "lease-A.pdf" on asset "asset-2"
  • And an uploaded file "lease-B.pdf" on asset "asset-2"
  • When they search the file metadata for asset "asset-2"
  • Then the metadata results include "lease-A.pdf"
  • And the metadata results include "lease-B.pdf"
  • And the metadata result total is 2

An agency presigns a direct upload to storage

FR — Une agence présigne un téléversement direct vers le stockage

Key: @i18n:files.presign_upload

  • When they ask for an upload link for a "application/pdf" file
  • Then the request succeeds
  • And the response carries a signed upload URL

Downloading a stored file returns the file itself

FR — Le téléchargement d'un fichier stocké renvoie le fichier lui-même

Key: @i18n:files.download_bytes

  • Given an uploaded file "diagnostic-dpe.pdf" on asset "asset-3"
  • When they download that file
  • Then the download returns the stored file bytes

An agency replaces the tags on a file

FR — Une agence remplace les étiquettes d'un fichier

Key: @i18n:files.replace_tags

  • Given an uploaded file "mandat-sepa.pdf" on asset "asset-4"
  • When they replace the tags of that file with "SIGNED"
  • Then the request succeeds
  • And the file is tagged "SIGNED"

An agency cancels the files attached to an asset

FR — Une agence annule les fichiers rattachés à un bien

Key: @i18n:files.cancel_files_for_target

  • Given an uploaded file "old-dpe.pdf" on asset "asset-5"
  • When they cancel the files of asset "asset-5"
  • Then the request succeeds
  • And the cancelled file is tagged "CANCELED"

An agency attaches a file to an incident ticket and lists it back

FR — Une agence attache un fichier à un ticket d'incident et le liste

Key: @i18n:files.attach_and_list_ticket_files

  • When they attach a "devis-travaux.pdf" file to incident ticket "ticket-9"
  • Then the file metadata is created
  • Given an uploaded file on incident ticket "ticket-9"
  • When they list the files of incident ticket "ticket-9"
  • Then the metadata results include "ticket-attachment.pdf"

Energy diagnostics are summarised per asset

FR — Les diagnostics énergétiques sont synthétisés par bien

Key: @i18n:files.diagnostics_per_target

  • Given a DPE energy diagnostic of "120.5" on asset "asset-6"
  • And a DPE greenhouse-gas diagnostic of "30" on asset "asset-6"
  • When they read the diagnostics for asset "asset-6"
  • Then the energy diagnostic for asset "asset-6" is 120.5
  • And the greenhouse-gas diagnostic for asset "asset-6" is 30

Fetching metadata for an unknown file id returns not found

FR — La récupération des métadonnées d'un identifiant de fichier inconnu renvoie introuvable

Key: @i18n:files.fetch_unknown_metadata_404

  • When they fetch the file metadata for unknown id "no-such-file"
  • Then the metadata is reported as not found

Downloading a missing or not-yet-uploaded file fails without a redirect

FR — Le téléchargement d'un fichier absent ou non encore téléversé échoue sans redirection

Key: @i18n:files.download_unknown_or_unuploaded

  • When they download the unknown file "no-such-file"
  • Then the download is reported as not found
  • Given a metadata-only file "lease-draft.pdf" on asset "asset-7" with no stored blob
  • When they download that metadata-only file
  • Then the download is rejected because the blob is not uploaded yet

An agency deletes a person's document, and deleting it again is a no-op

FR — Une agence supprime le document d'une personne, et le supprimer à nouveau est sans effet

Key: @i18n:files.delete_person_document

  • Given an uploaded file "rc-augustine.xlsx" on person "person-1"
  • When they delete that person document
  • Then the document is deleted
  • When they delete the unknown file "no-such-file"
  • Then nothing is deleted

Reading file metadata without an active organization is rejected

FR — La lecture des métadonnées d'un fichier sans organisation active est rejetée

Key: @i18n:files.requires_active_organization

  • Given the agency has no active organization
  • When they fetch the file metadata for unknown id "any-file"
  • Then the request is rejected for lack of an organization scope

Listing and downloading files for a ticket that has none returns nothing

FR — Lister et télécharger les fichiers d'un ticket qui n'en a aucun ne renvoie rien

Key: @i18n:files.ticket_files_not_found_paths

  • When they list the files of damage ticket "ticket-empty"
  • Then the metadata page is reported as empty
  • When they download ticket "ticket-empty" file "no-such-file"
  • Then the download is reported as not found

An agency builds a browser POST upload policy for a SEPA mandate

FR — Une agence construit une politique de téléversement POST navigateur pour un mandat SEPA

Key: @i18n:files.presign_browser_upload_policy

  • When they ask for a browser upload policy for a "application/pdf" file on SEPA mandate "mandate-1"
  • Then the request succeeds
  • And the response carries a signed POST upload policy

Notifications API — the HTTP surface of the queue-consumer notifications Worker

FR — API Notifications — la surface HTTP du Worker notifications consommateur de files

Key: @i18n:notifications · Source: tests/e2e-api/features/notifications.feature

The notifications worker answers its liveness probe

FR — Le worker notifications répond à sa sonde de vivacité

Key: @i18n:notifications.liveness_probe

  • When a monitor calls the health endpoint
  • Then the request succeeds
  • And the worker reports itself as "adb-notifications"

The notifications worker serves its OpenAPI contract

FR — Le worker notifications expose son contrat OpenAPI

Key: @i18n:notifications.openapi_served

  • When a tool fetches the OpenAPI document
  • Then the request succeeds
  • And the document is an OpenAPI 3.x contract
  • And the contract is titled for the "Notifications" service
  • And the contract documents the "/healthz" endpoint

Every documented endpoint resolves to a real route

FR — Chaque point d'accès documenté correspond à une route réelle

Key: @i18n:notifications.documented_endpoints_resolve

  • When a tool fetches the OpenAPI document
  • Then every documented endpoint resolves to a real route

The notifications worker serves a browsable Swagger UI

FR — Le worker notifications expose une interface Swagger navigable

Key: @i18n:notifications.swagger_ui_served

  • When a developer opens the API docs page
  • Then the request succeeds
  • And an HTML page mentioning "swagger-ui" is returned

There is no notification CRUD over HTTP — the worker consumes the message bus

FR — Il n'y a pas de CRUD de notification en HTTP — le worker consomme le bus de messages

Key: @i18n:notifications.no_http_crud_surface

  • When a client requests "/notifications" over HTTP
  • Then the request is not found
  • And the response points to the message bus

Reading a single notification by id is not found — there is no read route

FR — La lecture d'une notification par identifiant est introuvable — il n'y a pas de route de lecture

Key: @i18n:notifications.unknown_notification_read_not_found

  • When a client requests "/notifications/ntf-does-not-exist" over HTTP
  • Then the request is not found
  • And the response points to the message bus

A recorded notification definition has no HTTP read surface — the config is not served

FR — Une définition de notification enregistrée n'a pas de surface de lecture HTTP — la configuration n'est pas exposée

Key: @i18n:notifications.definition_config_has_no_http_read

  • Given a notification definition is recorded for the certificate-expired kind
  • Then the recorded definition is in the read model
  • When a client requests "/notification-definitions/CERTIFICATE_EXPIRED" over HTTP
  • Then the request is not found
  • And the response points to the message bus

A non-GET call on the liveness probe falls through to the not-found catch-all

FR — Un appel non-GET sur la sonde de vivacité bascule vers le gestionnaire d'absence générique

Key: @i18n:notifications.liveness_probe_rejects_non_get

  • When a client sends a "POST" to "/healthz" over HTTP
  • Then the request is not found
  • And the response points to the message bus

Posting a notification is not found — the worker has no write route, it consumes the bus

FR — L'envoi d'une notification est introuvable — le worker n'a pas de route d'écriture, il consomme le bus

Key: @i18n:notifications.creating_a_notification_is_not_found

  • When a client sends a "POST" to "/notifications" over HTTP
  • Then the request is not found
  • And the response points to the message bus

Parts API — building the property → unit hierarchy for an organization

FR — API Lots — construire la hiérarchie propriété → lot pour une organisation

Key: @i18n:parts · Source: tests/e2e-api/features/parts.feature

An agency builds a property, a building and a unit

FR — Une agence crée une propriété, un bâtiment et un lot

Key: @i18n:parts.build_hierarchy

  • When they create a property named "Résidence Les Lilas"
  • Then the part is created
  • And the saved part belongs to organization "org-77"
  • When they add a building named "Bâtiment A" to that property
  • Then the part is created
  • When they add a unit named "Lot 12 — T3" to that building
  • Then the part is created
  • When they fetch that part by id
  • Then the part named "Lot 12 — T3" is returned

A part's postal address is stored the way the JVM stores it

FR — L'adresse postale d'un lot est stockée telle que la JVM la stocke

Key: @i18n:parts.addresses_are_stored_in_the_entity_shape

  • When they create a property named "Villa Aurélia" addressed "12 avenue des Pins" in "Nice"
  • Then the part is created
  • And the stored part holds one entity-shaped postal address in "Nice"
  • And the stored part nature is the bare code "PART_NATURE.PROPERTY"
  • When they fetch that part by id
  • Then the returned part exposes the postal address in "Nice"
  • When they add a unit named "Lot 7" sharing that property's address
  • Then the part is created
  • And the stored unit holds only the parent's primary address id

The units of a building are listed back

FR — Les lots d'un bâtiment sont listés

Key: @i18n:parts.list_units_of_building

  • When they create a property named "Le Clos Fleuri"
  • And they add a building named "Bâtiment B" to that property
  • And they add a unit named "Lot 1" to that building
  • And they add a unit named "Lot 2" to that building
  • When they list the units of that building
  • Then the part list includes "Lot 1"
  • And the part list includes "Lot 2"
  • And the part list count is 2

Parts are searchable within the agency's organization

FR — Les lots sont recherchables au sein de l'organisation de l'agence

Key: @i18n:parts.search_in_org

  • When they create a property named "Villa Marius"
  • And they create a property named "Villa Doria"
  • And they search parts for "Doria"
  • Then the part results include "Villa Doria"
  • And the part results are SearchResult projections
  • And the part result total is 1

An agency edits a part's common information

FR — Une agence modifie les informations communes d'un lot

Key: @i18n:parts.patch_common_section

  • When they create a property named "Maison Bleue"
  • And they patch the "common" section of that part to name "Maison Repeinte"
  • Then the request succeeds
  • And the returned section has name "Maison Repeinte"

An agency opens a sale, finds it again and refines its basic information

FR — Une agence ouvre une vente, la retrouve et affine ses informations de base

Key: @i18n:parts.register_search_and_refine_sale

  • When they open a sale titled "Vente Villa Rose"
  • Then the sale is created
  • And the saved sale belongs to organization "org-77"
  • When they fetch that sale by id
  • Then the sale titled "Vente Villa Rose" is returned
  • When they search sales for "Villa Rose"
  • Then the sale results include "Vente Villa Rose"
  • And the sale result total is 1
  • When they patch the "basic-information" section of that sale to title "Vente Villa Rose — révisée"
  • Then the request succeeds
  • And the returned sale section has title "Vente Villa Rose — révisée"

An agency records, lists and removes a charge on a unit

FR — Une agence enregistre, liste et supprime une charge sur un lot

Key: @i18n:parts.charges_lifecycle_for_a_part

  • When they create a property named "Le Patio"
  • And they record a charge titled "Électricité parties communes" on that part
  • Then the charge is created
  • When they list the charges of that part
  • Then the charge list includes "Électricité parties communes"
  • And the charge list count is 1
  • When they delete that charge
  • Then the request succeeds
  • When they list the charges of that part
  • And the charge list count is 0

An agency stores an asset record on a part and reads it back by type

FR — Une agence enregistre un équipement sur un lot et le relit par type

Key: @i18n:parts.asset_upsert_and_read_by_type

  • When they create a property named "Le Belvédère"
  • And they upsert a "VALUATION.PURCHASE_VALUATION" asset on that part
  • Then the request succeeds
  • When they fetch the "VALUATION.PURCHASE_VALUATION" asset of that part
  • Then the asset of category "VALUATION.PURCHASE_VALUATION" is returned
  • When they list the assets of that part
  • Then the asset list includes the "VALUATION.PURCHASE_VALUATION" category
  • And the asset list renders the valuation grid

An agency adds a budget line and sees the version surface for the year

FR — Une agence ajoute une ligne de budget et voit les versions de l'année

Key: @i18n:parts.budget_line_create_and_versions

  • When they create a property named "Le Hameau"
  • And they add a budget line to budget "budget-2026" of that part for year 2026 version 1
  • Then the budget line is created
  • When they fetch that budget line
  • Then the current budget line is returned
  • When they list the budget versions of that part
  • Then the budget versions include year 2026

A local part search returns a page without a total

FR — Une recherche locale de lots renvoie une page sans total

Key: @i18n:parts.search_local_returns_a_page

  • When they create a property named "Villa Aurore"
  • And they create a property named "Villa Aurélie"
  • And they run a local parts search for "Aurore"
  • Then the local part results include "Villa Aurore"
  • And the local part results exclude "Villa Aurélie"
  • And the local part page metadata is present

GET parts info answers the module info, not a part read

FR — GET parts info renvoie les informations du module, et non la lecture d'un lot

Key: @i18n:parts.module_info_is_shadowed_by_part_id_lookup

  • When they fetch the module info
  • Then the request succeeds
  • And the module info names "adb-parts"

Creating a part without an active organization currently surfaces a server error

FR — Créer un lot sans organisation active provoque actuellement une erreur serveur

Key: @i18n:parts.create_without_org_currently_surfaces_500

  • Given an agency with no active organization
  • When they create a property named "Lot Orphelin"
  • Then the request fails with a server error

Fetching a part that does not exist is not found, as on the JVM

FR — Récupérer un lot inexistant renvoie une erreur introuvable, comme sur la JVM

Key: @i18n:parts.fetch_unknown_part_returns_an_empty_envelope

  • When they fetch the part with id "part-does-not-exist"
  • Then the JVM's not-found error names "part-does-not-exist"

Fetching a sale that does not exist is not found, as on the JVM

FR — Récupérer une vente inexistante renvoie une erreur introuvable, comme sur la JVM

Key: @i18n:parts.fetch_unknown_sale_returns_an_empty_envelope

  • When they fetch the sale with id "sale-does-not-exist"
  • Then the JVM's not-found error names "sale-does-not-exist"

Deleting a charge that was never recorded is idempotent

FR — Supprimer une charge jamais enregistrée est idempotent

Key: @i18n:parts.deleting_an_unrecorded_charge_is_idempotent

  • When they create a property named "Le Square"
  • And they delete the charge with id "000000000000000000000000" from that part
  • Then the request succeeds
  • And an empty charge envelope is returned
  • When they list the charges of that part
  • Then the charge list count is 0

An agency fills in every sale sub-section and reads each one back

FR — Une agence renseigne chaque sous-section de vente et relit chacune d'elles

Key: @i18n:parts.sale_sub_sections_round_trip

  • When they open a sale titled "Vente Le Domaine"
  • And they patch the "financing-information" section of that sale to "commissionAmount" "12500"
  • Then the request succeeds
  • When they fetch the "financing-information" section of that sale
  • Then the returned sale section has "commissionAmount" "12500"
  • When they patch the "agency-details" section of that sale to "name" "Agence Centrale"
  • And they fetch the "agency-details" section of that sale
  • Then the returned sale section has "name" "Agence Centrale"
  • When they patch the "buyer-details" section of that sale to "name" "Jean Dupont"
  • And they fetch the "buyer-details" section of that sale
  • Then the returned sale section has "name" "Jean Dupont"
  • When they patch the "notarial-office-details" section of that sale to "name" "Étude Martin"
  • And they fetch the "notarial-office-details" section of that sale
  • Then the returned sale section has "name" "Étude Martin"
  • When they patch the "dates" section of that sale to "sellingDecisionDate" "2026-01-15"
  • And they fetch the "dates" section of that sale
  • Then the returned sale section has "sellingDecisionDate" "2026-01-15"

An agency sells a unit and the sale information is persisted on the part

FR — Une agence vend un lot et les informations de vente sont enregistrées sur le lot

Key: @i18n:parts.sell_a_unit_records_sale_information

  • When they create a property named "Le Manoir"
  • And they sell that part with selling date "2026-03-31"
  • Then the request succeeds
  • And the returned part carries the selling date "2026-03-31"
  • When they fetch that part by id
  • Then the part carries the selling date "2026-03-31"

An agency builds a co-property building and lists its child units back

FR — Une agence crée un immeuble en copropriété et liste ses lots enfants

Key: @i18n:parts.co_building_children_listing

  • When they create a property named "Copropriété Le Parc"
  • And they add a building named "Bâtiment Central" to that property
  • And they add a unit named "Lot A" to that building
  • And they add a unit named "Lot B" to that building
  • When they list the children of that building
  • Then the part list includes "Lot A"
  • And the part list includes "Lot B"
  • And the part list count is 2

Persons API — managing owners and tenants for an organization

FR — API Personnes — gérer les propriétaires et locataires d'une organisation

Key: @i18n:persons · Source: tests/e2e-api/features/persons.feature

A representative registers an owner and finds it again

FR — Un représentant enregistre un propriétaire et le retrouve

Key: @i18n:persons.register_and_refetch_owner

  • When they create an owner named "Compagnie Dupont"
  • Then the request succeeds
  • And the saved person belongs to organization "org-42"
  • When they fetch that person by id
  • Then the person named "Compagnie Dupont" is returned

Owners are searchable within the representative's organization

FR — Les propriétaires sont recherchables au sein de l'organisation du représentant

Key: @i18n:persons.search_owners_in_org

  • When they create an owner named "Alice Martin"
  • And they create an owner named "Bob Durant"
  • And they search persons for "Alice"
  • Then the results include "Alice Martin"
  • And the result count is 1

A tenant is created and scoped to the organization

FR — Un locataire est créé et rattaché à l'organisation

Key: @i18n:persons.create_tenant_scoped

  • When they create a tenant named "Tom Tenant"
  • Then the request succeeds
  • And the saved person belongs to organization "org-42"

Creating a person without an active organization is refused

FR — La création d'une personne sans organisation active est refusée

Key: @i18n:persons.refuse_create_without_org

  • Given a representative without an active organization
  • When they create an owner named "Orphan Owner"
  • Then the request is rejected as a bad request

A supplier is created and scoped to the organization

FR — Un fournisseur est créé et rattaché à l'organisation

Key: @i18n:persons.create_supplier_scoped

  • When they create a supplier named "ACME Plomberie"
  • Then the request succeeds
  • And the saved person belongs to organization "org-42"
  • When they fetch that person by id
  • Then the person named "ACME Plomberie" is returned

An owner registered with an alias is found again by that alias

FR — Un propriétaire enregistré avec un alias est retrouvé grâce à cet alias

Key: @i18n:persons.lookup_owner_by_alias

  • When they create an owner named "Garry Lopez" with alias "garry-l"
  • Then the request succeeds
  • When they fetch the person with alias "garry-l"
  • Then the person named "Garry Lopez" is returned

Fetching an unknown person is not found, as on the JVM

FR — La consultation d'une personne inconnue renvoie une erreur introuvable, comme sur la JVM

Key: @i18n:persons.unknown_person_returns_no_content

  • When they fetch the person with id "does-not-exist"
  • Then the JVM's not-found error names "does-not-exist"

A representative creates an organization and reads it back as the active one

FR — Un représentant crée une organisation et la relit en tant qu'organisation active

Key: @i18n:persons.create_and_read_active_organization

  • When they create an organization named "Agence Centrale"
  • Then the organization is created
  • And they act as a representative of the new organization
  • When they fetch the active organization
  • Then the organization named "Agence Centrale" is returned

Company settings are created, listed and deleted for the organization

FR — Les paramètres d'entreprise sont créés, listés et supprimés pour l'organisation

Key: @i18n:persons.manage_company_settings

  • When they create a company setting of type "PROFESSIONNAL_ID" with value "RCS-12345"
  • Then the request succeeds
  • When they list the company settings of the organization
  • Then the company settings include value "RCS-12345"
  • And the company setting count is 1
  • When they delete that company setting
  • Then the request succeeds
  • When they list the company settings of the organization
  • And the company setting count is 0

Issuing the same invitation twice does not create a duplicate

FR — Émettre deux fois la même invitation ne crée pas de doublon

Key: @i18n:persons.deduplicate_invitation

  • When they invite "tenant@home.fr" to person "p-7" as a "DELEGATION"
  • Then the request succeeds
  • When they invite "tenant@home.fr" to person "p-7" as a "DELEGATION"
  • Then the request succeeds
  • And the same invitation is returned as the first one

An owner is not visible to a representative of another organization

FR — Un propriétaire n'est pas visible pour un représentant d'une autre organisation

Key: @i18n:persons.owner_not_visible_to_other_organization

  • When they create an owner named "Confidential Owner"
  • Then the request succeeds
  • Given a representative of organization "org-99"
  • When they fetch that person by id
  • Then the JVM's not-found error names that person

Listing invitations without an email claim is unauthorized

FR — Lister les invitations sans claim d'e-mail n'est pas autorisé

Key: @i18n:persons.invitations_require_email_claim

  • Given a representative whose token carries no email
  • When they list their invitations
  • Then the request is rejected as unauthorized

Creating an owner with a malformed request body is refused

FR — La création d'un propriétaire avec un corps de requête malformé est refusée

Key: @i18n:persons.reject_owner_with_malformed_body

  • When they post a malformed body to the owners endpoint
  • Then the request is rejected as a bad request

Inviting the same person under two different types yields two invitations

FR — Inviter la même personne sous deux types différents produit deux invitations

Key: @i18n:persons.distinct_invitation_per_target_type

  • When they invite "agent@home.fr" to person "p-7" as a "DELEGATION"
  • Then the request succeeds
  • When they invite "agent@home.fr" to person "p-7" as a "REGISTRATION"
  • Then the request succeeds
  • And a distinct invitation is returned from the first one

A contact category is added to an existing person and read back

FR — Une catégorie de contact est ajoutée à une personne existante puis relue

Key: @i18n:persons.promote_person_to_owner_via_category

  • When they create a tenant named "Polyvalent Person"
  • Then the request succeeds
  • When they add the contact category "CONTACT_CATEGORY.OWNER" to that person
  • Then the request succeeds
  • And that person carries the contact category "CONTACT_CATEGORY.OWNER"

A reused alias is accepted and the lookup returns the first registrant

FR — Un alias réutilisé est accepté et la recherche renvoie le premier inscrit

Key: @i18n:persons.duplicate_alias_keeps_first_registrant

  • When they create an owner named "Earliest Claimant" with alias "shared-alias"
  • Then the request succeeds
  • When they create an owner named "Latecomer" with alias "shared-alias"
  • Then the request succeeds
  • When they fetch the person with alias "shared-alias"
  • Then the person named "Earliest Claimant" is returned

Reports API — report-template metadata and rendered report files for an organization

FR — API Rapports — métadonnées de modèles de rapport et fichiers de rapport rendus pour une organisation

Key: @i18n:reports · Source: tests/e2e-api/features/reports.feature

A back-office registers report-template metadata and finds it again

FR — Un back-office enregistre les métadonnées d'un modèle de rapport et les retrouve

Key: @i18n:reports.upsert_and_refetch_metadata

  • When they save metadata for template "RC_COMMERCIAL" with title "Commercial lease report"
  • Then the metadata is saved
  • And the saved metadata belongs to organization "org-7"
  • When they fetch the metadata for template "RC_COMMERCIAL"
  • Then the request succeeds
  • And the returned metadata is for template "RC_COMMERCIAL"

Saving metadata stamps the matching template file name

FR — L'enregistrement des métadonnées renseigne le nom du fichier de modèle correspondant

Key: @i18n:reports.stamp_template_file_on_upsert

  • Given a stored template file "CIVIL_RC" named "civil-rc-pdf.ftlh"
  • When they save metadata for template "CIVIL_RC" with title "Civil lease report"
  • Then the metadata is saved
  • And the saved metadata references template file "civil-rc-pdf.ftlh"

All report-template metadata is listed back

FR — Toutes les métadonnées de modèles de rapport sont listées

Key: @i18n:reports.list_metadata

  • When they save metadata for template "RC_COMMERCIAL" with title "Commercial lease report"
  • And they save metadata for template "CIVIL_RC" with title "Civil lease report"
  • When they list the report metadata
  • Then the metadata list includes template "RC_COMMERCIAL"
  • And the metadata list includes template "CIVIL_RC"
  • And the metadata list count is 2

Fetching metadata for an unknown template returns not found

FR — La récupération des métadonnées d'un modèle inconnu renvoie introuvable

Key: @i18n:reports.metadata_not_found

  • When they fetch the metadata for template "CALL_FOR_RENT_SUMMARY"
  • Then the request is rejected as not found

An unrecognized template easyId is rejected

FR — Un easyId de modèle non reconnu est rejeté

Key: @i18n:reports.reject_invalid_easy_id

  • When they fetch the metadata for template "NOT_A_TEMPLATE"
  • Then the request is rejected as a bad request

A resumption-of-lease report file is rendered as a PDF

FR — Un fichier de rapport de reprise de bail est rendu au format PDF

Key: @i18n:reports.render_resumption_of_lease_pdf

  • When they request the resumption-of-lease report file for contract "c-123"
  • Then the request succeeds
  • And the report file is returned as a PDF

A caller without the reports scope is refused

FR — Un appelant sans le périmètre rapports est refusé

Key: @i18n:reports.refuse_without_scope

  • Given a caller without the reports scope
  • When they list the report metadata
  • Then the request is forbidden

Listing metadata before anything is saved returns an empty list

FR — Lister les métadonnées avant tout enregistrement renvoie une liste vide

Key: @i18n:reports.list_metadata_empty

  • When they list the report metadata
  • Then the request succeeds
  • And the metadata list count is 0

Re-saving metadata for the same template updates it in place

FR — Réenregistrer les métadonnées du même modèle les met à jour sur place

Key: @i18n:reports.upsert_metadata_updates_in_place

  • When they save metadata for template "RC_COMMERCIAL" with title "Commercial lease report"
  • Then the metadata is saved
  • When they save metadata for template "RC_COMMERCIAL" with title "Commercial lease report v2"
  • Then the metadata is saved
  • When they fetch the metadata for template "RC_COMMERCIAL"
  • Then the request succeeds
  • And the returned metadata has title "Commercial lease report v2"
  • When they list the report metadata
  • Then the metadata list count is 1

Downloading a raw template file is not yet supported

FR — Le téléchargement d'un fichier de modèle brut n'est pas encore pris en charge

Key: @i18n:reports.template_file_download_blocked

  • When they download the template file for template "CALL_FOR_RENT_SUMMARY"
  • Then the request is rejected as not implemented

Reading a merged template without saved metadata returns not found

FR — Lire un modèle fusionné sans métadonnées enregistrées renvoie introuvable

Key: @i18n:reports.merged_template_not_found

  • When they read the merged template for template "RC_COMMERCIAL"
  • Then the request is rejected as not found

A caller without the reports scope cannot render a report file

FR — Un appelant sans le périmètre rapports ne peut pas générer un fichier de rapport

Key: @i18n:reports.refuse_render_without_scope

  • Given a caller without the reports scope
  • When they request the resumption-of-lease report file for contract "c-123"
  • Then the request is forbidden

Tickets API — tracking incidents and their lifecycle for an organization

FR — API Tickets — suivre les incidents et leur cycle de vie pour une organisation

Key: @i18n:tickets · Source: tests/e2e-api/features/tickets.feature

An agent reads an existing incident ticket

FR — Un agent consulte un ticket d'incident existant

Key: @i18n:tickets.read_seeded_ticket

  • Given an incident ticket "Water leak in the kitchen" exists
  • When they fetch that ticket
  • Then the ticket subject is "Water leak in the kitchen"
  • And the ticket belongs to organization "org-55"

Tickets are searchable by subject within the organization

FR — Les tickets sont recherchables par objet au sein de l'organisation

Key: @i18n:tickets.search_by_subject

  • Given an incident ticket "Broken boiler in flat 3" exists
  • And an incident ticket "Leaking roof over the garage" exists
  • When they search tickets for "boiler"
  • Then the ticket results include "Broken boiler in flat 3"
  • And the ticket result total is 1

An open ticket is taken into progress

FR — Un ticket ouvert est pris en charge

Key: @i18n:tickets.process_transition

  • Given an incident ticket "Lift out of service" exists
  • When they fetch that ticket
  • Then the fetched ticket is in state "TICKET_STATE.OPEN"
  • When they apply the "PROCESS" transition to that ticket
  • Then the request succeeds
  • And the ticket state is "TICKET_STATE.IN_PROGRESS"

An illegal transition is refused

FR — Une transition illégale est refusée

Key: @i18n:tickets.illegal_transition_refused

  • Given an archived ticket "Old settled dispute" exists
  • When they apply the "CLOSE" transition to that ticket
  • Then the transition is rejected as illegal

An event is appended to a ticket and listed back

FR — Un événement est ajouté à un ticket puis listé

Key: @i18n:tickets.append_and_list_events

  • Given an incident ticket "Window won't close" exists
  • When they append an event "Called the tenant — leak confirmed" to that ticket
  • Then the event is created
  • When they list the events of that ticket
  • Then the event list includes "Called the tenant — leak confirmed"
  • And the event list count is 1

A note is attached to a ticket and listed back

FR — Une note est attachée à un ticket puis listée

Key: @i18n:tickets.attach_and_list_notes

  • Given an incident ticket "Heating barely warm" exists
  • When they add a note "Tenant prefers morning interventions" on that ticket
  • Then the note is created
  • When they list the notes of that ticket
  • Then the note list includes "Tenant prefers morning interventions"

A ticket is delegated and the delegation is listed back

FR — Un ticket est délégué et la délégation est listée

Key: @i18n:tickets.delegate_ticket

  • Given an agent handling tickets for organization "665f1f77bcf86cd7994390a1"
  • And an incident ticket "Garage door jammed" exists
  • And organization "665f1f77bcf86cd7994390c3" is an eligible ticket delegatee
  • When they delegate that ticket to organization "665f1f77bcf86cd7994390c3"
  • Then the delegation is created
  • And the delegation grants the incident ticket scopes
  • When they list the delegations of that ticket
  • Then the delegation list count is 1
  • And a delegation targets organization "665f1f77bcf86cd7994390c3"

A ticket delegated to another organization appears in that organization's list and global search while the delegation is valid

FR — Un ticket délégué à une autre organisation apparaît dans sa liste et sa recherche globale tant que la délégation est valide

Key: @i18n:tickets.delegated_ticket_in_delegate_lists

  • Given an agent handling tickets for organization "665f1f77bcf86cd7994390a1"
  • And an incident ticket "Garage door jammed" exists
  • And organization "665f1f77bcf86cd7994390c3" is an eligible ticket delegatee
  • When they delegate that ticket to organization "665f1f77bcf86cd7994390c3"
  • Then the delegation is created
  • Given an incident ticket "Gutter overflowing onto the terrace" exists
  • And that ticket was delegated to organization "665f1f77bcf86cd7994390c3" until yesterday
  • And an agent handling tickets for organization "665f1f77bcf86cd7994390c3"
  • When they list the tickets
  • Then the ticket results include "Garage door jammed"
  • And the ticket results do not include "Gutter overflowing onto the terrace"
  • When they search all tickets globally
  • Then the global search results include "Garage door jammed"
  • And the global search results do not include "Gutter overflowing onto the terrace"

Reading an unknown ticket is not found

FR — La lecture d'un ticket inconnu renvoie introuvable

Key: @i18n:tickets.unknown_ticket_not_found

  • When they fetch the ticket with id "ticket-does-not-exist"
  • Then the ticket is not found

A ticket belonging to another organization is not found

FR — Un ticket appartenant à une autre organisation est introuvable

Key: @i18n:tickets.ticket_of_another_org_hidden

  • Given an incident ticket "Confidential dispute" exists
  • Given an agent handling tickets for organization "org-99"
  • When they fetch that ticket
  • Then the ticket is not found

Re-closing a ticket after it was archived is refused

FR — Reclôturer un ticket après son archivage est refusé

Key: @i18n:tickets.reclose_after_archive_refused

  • Given a closed ticket "Settled water damage" exists
  • When they apply the "ARCHIVE" transition to that ticket
  • Then the request succeeds
  • And the ticket state is "TICKET_STATE.ARCHIVED"
  • When they apply the "CLOSE" transition to that ticket
  • Then the transition is rejected as illegal

Deleting an archived ticket is refused

FR — La suppression d'un ticket archivé est refusée

Key: @i18n:tickets.delete_archived_ticket_refused

  • Given an archived ticket "Closed and filed dispute" exists
  • When they soft-delete that ticket
  • Then the transition is rejected as illegal

An unknown transition name is rejected as a bad request

FR — Un nom de transition inconnu est rejeté comme requête invalide

Key: @i18n:tickets.unknown_transition_name_rejected

  • Given an incident ticket "Cracked window pane" exists
  • When they apply the "REOPEN" transition to that ticket
  • Then the request is rejected as a bad request

A ticket's events start empty and list back every appended event

FR — Les événements d'un ticket sont d'abord vides puis listent chaque événement ajouté

Key: @i18n:tickets.events_listed_empty_then_grow

  • Given an incident ticket "Recurring damp patch" exists
  • When they list the events of that ticket
  • Then the event list count is 0
  • When they append an event "First site visit booked" to that ticket
  • Then the event is created
  • When they append an event "Dehumidifier installed" to that ticket
  • Then the event is created
  • When they list the events of that ticket
  • Then the event list includes "First site visit booked"
  • And the event list includes "Dehumidifier installed"
  • And the event list count is 2

Utilities API — serials, allocation keys, indexes, configurations and i18n for an organization

FR — API Utilitaires — numéros de série, clés de répartition, indices, configurations et i18n pour une organisation

Key: @i18n:utilities · Source: tests/e2e-api/features/utilities.feature

Serial numbers for a type are allocated sequentially within the organization

FR — Les numéros de série d'un type sont attribués séquentiellement au sein de l'organisation

Key: @i18n:utilities.allocate_sequential_serials

  • When they allocate a serial number for type "INVOICE"
  • Then the allocated serial number is 1
  • When they allocate a serial number for type "INVOICE"
  • Then the allocated serial number is 2
  • When they allocate a serial number for type "CREDIT_NOTE"
  • Then the allocated serial number is 1

An agency creates an allocation key and finds it again

FR — Une agence crée une clé de répartition et la retrouve

Key: @i18n:utilities.create_and_refetch_allocation_key

  • When they create an allocation key "ak-roof" for target "building-1" totalling 100 with shares 60 and 40
  • Then the request succeeds
  • And the saved allocation key is in state "ACTIVE"
  • When they fetch the allocation key "ak-roof"
  • Then the returned allocation key is in state "ACTIVE"

An allocation key whose shares do not sum to the total starts as a draft

FR — Une clé de répartition dont les parts ne totalisent pas le montant démarre à l'état brouillon

Key: @i18n:utilities.draft_allocation_key_when_shares_incomplete

  • When they create an allocation key "ak-wip" for target "building-2" totalling 100 with shares 60 and 30
  • Then the request succeeds
  • And the saved allocation key is in state "DRAFT"

An active allocation key can be archived through the state machine

FR — Une clé de répartition active peut être archivée via la machine à états

Key: @i18n:utilities.transition_allocation_key_to_archived

  • When they create an allocation key "ak-old" for target "building-3" totalling 100 with shares 70 and 30
  • And they apply the transition "SET_ARCHIVED" to allocation key "ak-old"
  • Then the request succeeds
  • When they fetch the allocation key "ak-old"
  • Then the returned allocation key is in state "ARCHIVED"

INSEE rent-revision indexes are listed by their type

FR — Les indices INSEE de révision des loyers sont listés par type

Key: @i18n:utilities.list_insee_indexes_by_type

  • Given an INSEE index "ILC" for quarter 2 of year 2026 with value "143.46"
  • And an INSEE index "ILC" for quarter 1 of year 2026 with value "142.06"
  • When they list indexes of type "ILC"
  • Then the index list count is 2
  • And the index list includes value "143.46"

The serial configuration for a type is read back

FR — La configuration de numérotation d'un type est relue

Key: @i18n:utilities.read_serial_configuration

  • Given a serial configuration for type "INVOICE" with format "FAC-%05d"
  • When they read the serial configuration for type "INVOICE"
  • Then the returned configuration has format "FAC-%05d"

Catalog reference values are listed for a code

FR — Les valeurs de référence du catalogue sont listées pour un code

Key: @i18n:utilities.search_catalog_values_by_code

  • Given a catalog value "TITLE" labelled "Monsieur"
  • And a catalog value "TITLE" labelled "Madame"
  • When they list catalog values for code "TITLE"
  • Then the catalog list count is 2

A translation key is resolved from the bundled i18n resources

FR — Une clé de traduction est résolue depuis les ressources i18n embarquées

Key: @i18n:utilities.translate_i18n_key

  • When they translate the key "GREETING" of bundle "JEDI" in "en"
  • Then the translation contains "May the Force"

Configured dashboards are listed for the agency

FR — Les tableaux de bord configurés sont listés pour l'agence

Key: @i18n:utilities.list_dashboards

  • Given a dashboard "Rents" at base url "https://bi.example/rents"
  • And a dashboard "Arrears" at base url "https://bi.example/arrears"
  • When they list the dashboards
  • Then the dashboard list count is 2
  • And the dashboard list includes name "Arrears"

Ticket natures are listed for a ticket type as a raw collection

FR — Les natures de ticket sont listées pour un type de ticket sous forme de collection brute

Key: @i18n:utilities.list_ticket_natures

  • Given a ticket nature "Plumbing" for ticket type "TICKET_TYPE.INCIDENT"
  • And a ticket nature "Roofing" for ticket type "TICKET_TYPE.INCIDENT"
  • When they list the ticket natures for ticket type "TICKET_TYPE.INCIDENT"
  • Then the ticket nature list count is 2

A well-formed IBAN passes validation and is echoed back

FR — Un IBAN bien formé passe la validation et est renvoyé tel quel

Key: @i18n:utilities.validate_well_formed_iban

  • When they validate the IBAN "FR1420041010050500013M02606"
  • Then the request succeeds
  • And the validated IBAN is "FR1420041010050500013M02606"

An IBAN that fails the checksum is rejected

FR — Un IBAN dont la clé de contrôle est incorrecte est rejeté

Key: @i18n:utilities.reject_malformed_iban

  • When they validate the IBAN "FR0000000000000000000000000"
  • Then the request is rejected as a bad request

Fetching an unknown allocation key reports not found

FR — La récupération d'une clé de répartition inconnue renvoie introuvable

Key: @i18n:utilities.allocation_key_not_found

  • When they fetch the allocation key "does-not-exist"
  • Then the request is not found

Archiving a draft allocation key is rejected by the state machine

FR — L'archivage d'une clé de répartition brouillon est rejeté par la machine à états

Key: @i18n:utilities.reject_illegal_allocation_key_transition

  • When they create an allocation key "ak-draft" for target "building-9" totalling 100 with shares 50 and 30
  • Then the saved allocation key is in state "DRAFT"
  • When they apply the transition "SET_ARCHIVED" to allocation key "ak-draft"
  • Then the request is rejected as a conflict

Views API — read-model projections for an organization's properties and reports

FR — API Vues — projections du modèle de lecture pour les propriétés et rapports d'une organisation

Key: @i18n:views · Source: tests/e2e-api/features/views.feature

The distinct owners of an organization are listed as a filter source

FR — Les propriétaires distincts d'une organisation sont listés comme source de filtre

Key: @i18n:views.list_distinct_owners

  • Given a property "Résidence Les Lilas" owned by owner "own-1" named "Compagnie Dupont"
  • And a property "Le Clos Fleuri" owned by owner "own-2" named "Alice Martin"
  • When they list the owner filter source
  • Then the view collection includes an owner named "Compagnie Dupont"
  • And the view collection includes an owner named "Alice Martin"
  • And the view collection has 2 entries

The property filter source returns roots and excludes units

FR — La source de filtre des propriétés renvoie les racines et exclut les lots

Key: @i18n:views.list_property_filter

  • Given a property "Villa Marius" with id "prop-10"
  • And a unit "Lot 12 — T3" with id "unit-1" under property "prop-10"
  • When they list the property filter source
  • Then the view collection includes a part named "Villa Marius"
  • And the view collection has 1 entries

Properties are searched by name within the organization

FR — Les propriétés sont recherchées par nom au sein de l'organisation

Key: @i18n:views.search_properties_by_name

  • Given a property "Villa Doria" with id "prop-20"
  • And a property "Maison Bleue" with id "prop-21"
  • When they search properties for "Doria"
  • Then the view page includes a part named "Villa Doria"
  • And the view page total is 1

The units of a property are returned as a page

FR — Les lots d'une propriété sont renvoyés sous forme de page

Key: @i18n:views.paged_units_of_property

  • Given a property "Le Grand Large" with id "prop-30"
  • And a unit "Lot 1" with id "unit-10" under property "prop-30"
  • And a unit "Lot 2" with id "unit-11" under property "prop-30"
  • When they read the units page of property "prop-30"
  • Then the view page includes a part named "Lot 1"
  • And the view page includes a part named "Lot 2"
  • And the view page total is 2

The parts held by an owner are returned

FR — Les lots détenus par un propriétaire sont renvoyés

Key: @i18n:views.parts_for_owner

  • Given a property "Résidence du Parc" owned by owner "own-9" named "Bob Durant"
  • When they read the parts for owner "own-9"
  • Then the view collection includes a part named "Résidence du Parc"
  • And the view collection has 1 entries

The buildings of a signed rental-delegate contract are returned

FR — Les bâtiments d'un mandat de gestion locative signé sont renvoyés

Key: @i18n:views.buildings_for_rdc

  • Given a building "Bâtiment A" with id "bld-1" linked to rental-delegate contract "rdc-1"
  • When they read the buildings for rental-delegate contract "rdc-1"
  • Then the view collection includes a part named "Bâtiment A"
  • And the view collection has 1 entries

The call-for-rent summary is rolled up for a period and state

FR — Le récapitulatif d'appel de loyer est agrégé pour une période et un état

Key: @i18n:views.call_for_rent_summary

  • Given a call-for-rent event for period "2026-06" in state "PROCESSED"
  • And a call-for-rent event for period "2026-05" in state "PROCESSED"
  • When they request the call-for-rent summary for period "2026-06" and state "PROCESSED"
  • Then the request succeeds
  • And the view value list has 1 entries

The CRG report is rendered as a PDF when a PDF is requested

FR — Le rapport CRG est rendu en PDF lorsqu'un PDF est demandé

Key: @i18n:views.crg_report_as_pdf

  • Given a CRG event for owner "own-5" in state "PROCESSED"
  • When they request the CRG report for owner "own-5" in state "PROCESSED" as a PDF
  • Then the request succeeds
  • And the response content type is "application/pdf"

The distinct tenants of an organization are listed as a filter source

FR — Les locataires distincts d'une organisation sont listés comme source de filtre

Key: @i18n:views.list_distinct_tenants

  • Given a property "Studio Gambetta" tenanted by tenant "ten-1" named "Claire Petit"
  • And a property "Loft Belleville" tenanted by tenant "ten-2" named "Hugo Bernard"
  • When they list the tenant filter source
  • Then the view collection includes a tenant named "Claire Petit"
  • And the view collection includes a tenant named "Hugo Bernard"
  • And the view collection has 2 entries

The owner filter source is empty when the organization has no seeded parts

FR — La source de filtre des propriétaires est vide lorsque l'organisation n'a aucun bien enregistré

Key: @i18n:views.empty_owner_filter

  • When they list the owner filter source
  • Then the request succeeds
  • And the view collection has 0 entries

A property search that matches nothing returns an empty page

FR — Une recherche de propriétés sans correspondance renvoie une page vide

Key: @i18n:views.search_no_match_is_empty_page

  • Given a property "Villa Solaire" with id "prop-40"
  • When they search properties for "Inexistante"
  • Then the request succeeds
  • And the view page total is 0
  • And the view page has 0 entries

The units of a property are paginated by page and page size

FR — Les lots d'une propriété sont paginés par numéro de page et taille de page

Key: @i18n:views.units_page_second_page

  • Given a property "Le Phare" with id "prop-50"
  • And a unit "Lot A" with id "unit-50" under property "prop-50"
  • And a unit "Lot B" with id "unit-51" under property "prop-50"
  • When they read the units page of property "prop-50" at page 1 with size 1
  • Then the request succeeds
  • And the view page has 1 entries
  • And the view page total is 2
  • And the view page includes only the part named "Lot B"
  • And the view page size is 1

The market performances of a known part are returned with its children

FR — Les performances de marché d'un bien connu sont renvoyées avec ses enfants

Key: @i18n:views.market_performances_for_part

  • Given a property "Résidence Horizon" with id "prop-60"
  • And a unit "Lot 60" with id "unit-60" under property "prop-60"
  • When they read the market performances of part "prop-60"
  • Then the request succeeds
  • And the view value part is named "Résidence Horizon"
  • And the view value part has 1 children

The market performances of an unknown part yield no content

FR — Les performances de marché d'un bien inconnu ne renvoient aucun contenu

Key: @i18n:views.market_performances_unknown_part_no_content

  • When they read the market performances of part "prop-does-not-exist"
  • Then the response is empty with no content
Edit this page
Last Updated:
Contributors: Grégory Horion