Mutations et régularisation comptable — référence DEV/PO¶
Cette page décrit le comportement effectivement implémenté pour les mutations (OwnershipTransfer) et leur régularisation comptable (OwnershipTransferSettlement). Elle s'adresse aux développeurs, Product Owners et analystes fonctionnels.
Le code est la source de vérité. Les sections « Limites et écarts connus » signalent explicitement les différences entre l'intention métier initiale et le comportement courant.
Pour un parcours orienté écran, consulter le guide utilisateur des mutations.
Périmètre fonctionnel¶
Une mutation couvre actuellement :
- une copropriété ;
- un dossier de propriété vendeur (
old_ownership_id) ; - un dossier de propriété acquéreur (
new_ownership_id) ; - un ou plusieurs lots ;
- une date de transfert, qui correspond à la date de l'acte ;
- les échanges documentaires avec le notaire ;
- une régularisation comptable active liée depuis la mutation.
Le traitement comptable analyse les fonds de roulement, les appels de fonds comptabilisés et les décomptes périodiques comptabilisés. Les pièces sources déjà comptabilisées ne sont pas modifiées : le delta est matérialisé par des opérations diverses (MiscOperation).
Les classes principales sont :
| Rôle | Classe |
|---|---|
| Dossier juridique et documentaire | realestate\property\OwnershipTransfer |
| Dossier de régularisation comptable | realestate\property\transfer\OwnershipTransferSettlement |
| Mouvement calculé par source et par lot | OwnershipTransferSettlementLine |
| Lien idempotent entre une source et une OD | OwnershipTransferSettlementOperation |
| Courrier vendeur ou acquéreur | OwnershipTransferSettlementCorrespondence |
Deux workflows coordonnés¶
Workflow de la mutation¶
| Statut technique | Libellé UI | Actions et destination |
|---|---|---|
pending |
Brouillon | open → open |
open |
Ouvert | send → seller_documents_sent ; confirm → confirmed |
seller_documents_sent |
Documents vendeur envoyés | confirm → confirmed ; to_complete → open |
confirmed |
Confirmé | revert_to_open → open ; settle → settled |
financial_statement_sent |
État financier envoyé | revert_to_open → open ; settle → settled ; to_complete → confirmed |
settled |
Vente confirmée | revert_to_confirmed → confirmed ; prepare_accounting → accounting_pending |
accounting_pending |
Régularisation comptable en cours | unlock → settled ; close → closed |
closed |
Clôturé | unlock → settled |
L'action d'envoi du courrier notarial génère un PDF, l'enregistre comme Document, ajoute les pièces sélectionnées, met l'e-mail en file via la boîte du processus legal, puis ajoute une ligne à l'historique. Elle ne fait avancer automatiquement le workflow que depuis open, vers seller_documents_sent.
Le statut financial_statement_sent existe dans le modèle et les vues, mais aucune transition du workflow courant ni l'action d'envoi ne positionne ce statut. Il peut donc surtout être rencontré dans des données historiques ou positionnées par un autre mécanisme.
Workflow de la régularisation¶
| Statut | Signification | Transitions |
|---|---|---|
pending |
Calcul et OD en préparation | validate → validated ; cancel → cancelled |
validated |
OD comptabilisées | close → closed ; cancel → cancelled |
closed |
Régularisation clôturée | cancel → cancelled |
cancelled |
Régularisation annulée et conservée pour traçabilité | aucune |
Dans le flux standard, la transition validate exécute successivement le transfert des lots, la comptabilisation des OD, la génération et la planification des correspondances, puis la transition close. La régularisation et la mutation arrivent donc normalement à closed au cours de la même chaîne de validation.
Volet notarial¶
La fiche mutation structure les informations communiquées au titre des paragraphes 1, 2 et 3 :
- §1 : soldes des fonds, arriérés du vendeur, appels votés, procédures, PV d'assemblée, décomptes et dernier bilan ;
- §2 : dépenses décidées, appels de fonds, acquisitions de parties communes, dettes de copropriété, situation actualisée du vendeur et emprunts ;
- §3 : date de l'acte, lots cédés et acquéreur.
À l'ouverture du dossier, les textes descriptifs sont initialisés depuis les modèles documentaires ownership_transfer_paragraph_1 et ownership_transfer_paragraph_2. Les soldes de fonds, appels planifiés et arriérés sont ensuite rafraîchis. La confirmation actualise à nouveau les arriérés.
Informations du §1¶
Le premier volet correspond à la situation communiquée avant le compromis. La fiche permet de préparer :
- les soldes des fonds de la copropriété et leur ventilation sur les lots concernés ;
- les arriérés du vendeur et les frais de dossier de mutation ;
- les appels destinés aux fonds et déjà décidés ;
- les procédures judiciaires en cours ;
- les procès-verbaux d'assemblée et décomptes périodiques joints ;
- le dernier bilan approuvé ;
- les informations complémentaires, notamment la citerne et le dossier d'intervention ultérieure.
Les arriérés sont calculés à partir des Funding du vendeur. Ils portent sur son dossier de propriété dans la copropriété et ne sont pas limités aux seuls lots vendus. Les soldes de fonds et les appels affichés dans ce volet sont des informations destinées au courrier notarial ; ils sont distincts des lignes du settlement comptable créées après l'acte.
Informations du §2¶
Le second volet prépare les informations complémentaires demandées après le compromis :
- dépenses de conservation, entretien, réparation et réfection déjà décidées ;
- appels de fonds approuvés ou planifiés ;
- acquisition de parties communes ;
- dettes certaines de la copropriété liées à des litiges ;
- situation actualisée des arriérés du vendeur ;
- emprunts bancaires et tableaux d'amortissement éventuels.
Le champ with_both_paragraphs permet au rendu notarial d'inclure les deux ensembles d'informations dans une même correspondance.
Les délais métier généralement associés au dossier sont de 15 jours après request_date pour le §1 et de 30 jours après confirmation_date pour le §2. Les champs de date permettent cette traçabilité, mais le workflow inspecté n'impose pas ces délais et ne planifie pas de rappel automatique lorsque le §3 tarde à arriver.
Informations du §3 et dates¶
transfer_date est la date économique et juridique déterminante pour le transfert des lots et les calculs comptables. La date de jouissance ou une date de réception d'un immeuble neuf ne joue aucun rôle dans l'algorithme décrit ici.
Les champs de suivi disponibles sont request_date, confirmation_date, seller_documents_sent_date, financial_statement_sent_date et transfer_date. Le modèle ne calcule pas de délai ni de rappel automatique à partir de ces dates. En particulier, les délais métier de 15 jours pour le §1, de 30 jours pour le §2 ou le rappel après l'acte ne sont pas contrôlés par ce workflow.
L'exercice et la période comptables sont calculés d'après la première date disponible selon l'étape du dossier, la date de transfert devenant la référence prioritaire lorsqu'elle est renseignée.
Création de la régularisation¶
La transition prepare_accounting, accessible depuis settled, exige :
- une copropriété ;
- un vendeur et un acquéreur distincts ;
- au moins un lot ;
- une date de transfert.
Son hook onbeforePrepareAccounting() crée, s'il n'existe pas déjà, un OwnershipTransferSettlement avec :
- la mutation, la copropriété, le vendeur et l'acquéreur ;
- la date de transfert ;
snapshot_at = now();accounting_date = now()par défaut ;- le statut
pending.
Le lien ownership_transfer_settlement_id est écrit sur la mutation, puis celle-ci passe à accounting_pending.
Cette étape ne génère ni ligne ni OD. Ces opérations sont déclenchées depuis la fiche de régularisation avec Générer les lignes, puis Générer les OD.
Convention de signe¶
Une ligne exprime un transfert économique entre vendeur et acquéreur :
applied_amount > 0
vendeur crédité
acquéreur débité
applied_amount < 0
vendeur débité
acquéreur crédité
Le calcul de base est :
calculated_amount = actual_seller_amount - theoretical_seller_amount
Exemple : 100 EUR ont été comptabilisés au vendeur, mais 40 EUR seulement lui reviennent après prise en compte de la mutation. Le montant calculé est 100 - 40 = 60 EUR : le vendeur est crédité de 60 EUR et l'acquéreur est débité de 60 EUR.
calculated_amount conserve le résultat avant arrondi final. applied_amount, modifiable tant que la régularisation est pending, est le montant à comptabiliser.
Génération des lignes¶
Préconditions¶
La génération n'est autorisée que si :
- la régularisation est
pending; - toute OD déjà liée est encore
proforma; - le dossier et son snapshot sont complets ;
snapshot_atn'est pas antérieur à la date de transfert ;- la copropriété possède au moins un fonds de roulement ;
- chaque fonds de roulement possède un compte, une clé de répartition et un total de quotes-parts strictement positif ;
- chaque lot vendu possède une quote-part pour la clé de chaque fonds de roulement.
Fonds de roulement¶
Pour chaque CondoFund de type working_fund :
- la position est prise à
transfer_date - 1 jour, car le jour de la mutation appartient à l'acquéreur ; - le dernier
AccountBalanceChangedisponible à cette date fournit le soldecredit_balance - debit_balance; - ce solde est réparti entre les lots vendus selon
property_lot_shares / total_shares; - une ligne
working_fund_transferest calculée par lot.
Le vendeur est considéré comme détenteur réel du montant avant transfert et l'acquéreur comme détenteur théorique après transfert.
Seuls les soldes des fonds de type working_fund sont transférés directement. Les soldes des fonds de réserve ne font pas l'objet de cette étape. En revanche, une exécution comptabilisée d'appel de fonds peut être analysée quel que soit le type de l'appel, son affectation comptable étant déterminée par FundRequestExecution::getDebitOperationAssignment().
Appels de fonds comptabilisés¶
Toutes les FundRequestExecution de la copropriété qui sont posted et dont posting_date <= snapshot_at sont examinées. Le traitement retient seulement les entrées relatives aux lots vendus et au vendeur ou à l'acquéreur.
La période économique utilise date_from et date_to. À défaut, posting_date sert de date ponctuelle. Les bornes sont inclusives :
- période entièrement avant la mutation : 100 % vendeur ;
- période commençant le jour de la mutation ou après : 100 % acquéreur ;
- période à cheval : prorata journalier, le jour de mutation étant le premier jour acquéreur.
Le type de correction est post_transfer_call si period_from >= transfer_date, sinon current_period_provision. Une source entièrement antérieure peut donc être classée comme provision de période courante, mais son delta est normalement nul et sa ligne n'est pas persistée.
Décomptes périodiques comptabilisés¶
Toutes les ExpenseStatement posted avant le snapshot sont examinées. Sont exclus les décomptes qui cumulent les deux conditions suivantes :
- l'exercice est
closed; - la période du décompte est la dernière période de l'exercice.
Un décompte sans ligne vendeur/acquéreur pour les lots transférés est ignoré. Pour les autres, le code :
- agrège les montants réellement comptabilisés par lot et par partie ;
- appelle
ExpenseStatement::simulateOwnershipTransferData()pour simuler la ventilation après mutation ; - compare réel et théorique ;
- crée des lignes
expense_statement_adjustmentsans modifier le décompte source.
Arrondis et persistance¶
Les lignes sont groupées par source comptable. Dans chaque groupe :
- les lots sont triés par identifiant croissant ;
- le total brut est arrondi à deux décimales ;
- chaque ligne sauf la dernière est arrondie à deux décimales ;
- la dernière absorbe le reliquat de centimes ;
- les lignes dont
abs(applied_amount) < 0,005ne sont pas enregistrées.
Les montants réel, théorique et calculé sont enregistrés avec quatre décimales ; le montant retenu l'est avec deux.
has_accounted_sources est déterminé par la présence de groupes analysés, pas par la présence finale d'un delta non nul. Comme chaque fonds de roulement configuré produit un groupe avant filtrage des montants nuls, cet indicateur peut être vrai alors qu'aucune ligne n'est finalement enregistrée. alert_summary est un simple résumé textuel du nombre de lignes et de sources.
Effet d'une régénération¶
La régénération n'effectue pas de fusion avec les lignes existantes. Elle supprime d'abord les lignes des OD pro forma, supprime toutes les lignes de régularisation, puis recrée le résultat complet.
En conséquence, toute modification manuelle de applied_amount est perdue si l'utilisateur relance Générer les lignes. Il n'existe actuellement ni justification d'override, ni exclusion, ni marquage d'obsolescence, ni empreinte de calcul.
Génération des opérations diverses¶
Seules les lignes dont le montant retenu est non nul sont groupées. Il existe au plus une OD par source grâce à la clé :
settlement:{settlement_id}:{source_type}:{source_id}
| Source | Regroupement | Date de comptabilisation |
|---|---|---|
| Fonds de roulement | régularisation + fonds | date de transfert |
| Appel couvrant la mutation | régularisation + exécution | date de transfert |
| Appel postérieur | régularisation + exécution | posting_date de l'exécution |
| Décompte | régularisation + décompte | accounting_date de la régularisation |
Avant création, chaque date doit être aujourd'hui ou antérieure, couverte par une période open ou preclosed, dans un exercice preopen, open ou preclosed. La copropriété doit également disposer :
- d'un journal
MISC; - d'un compte bancaire principal ;
- des comptes copropriétaire correspondant à l'affectation du fonds ou de l'appel.
Pour chaque lot, une ligne crédite/débite le compte vendeur et une ligne miroir débite/crédite le compte acquéreur. Pour le fonds de roulement, deux lignes supplémentaires sur le compte du fonds déplacent l'affectation analytique vendeur/acquéreur sans modifier le solde global du fonds.
L'OD est créée puis publiée en proforma. Une nouvelle génération remplace une OD encore pro forma. Une OD déjà posted est réutilisée uniquement si sa clé, sa date et son montant correspondent ; une divergence bloque le traitement.
Validation, écritures et Funding¶
La validation exige que toutes les OD liées soient encore proforma. Le code n'impose toutefois pas qu'il existe au moins une ligne ou une OD : une régularisation sans correction peut être validée.
Après la transition vers validated, le hook :
- transfère les lots au nouvel acquéreur ;
- poste chaque OD ;
- renseigne
validated_at; - génère les correspondances ;
- planifie leur émission ;
- clôture la régularisation.
Le post d'une MiscOperation utilise le workflow générique : génération et validation de l'écriture comptable, création des Funding, attribution du numéro d'opération. Pour chaque ligne de compte propriétaire :
due_amount = debit - credit
Un débit acquéreur produit donc un Funding positif ; un crédit vendeur, un Funding négatif. Ces objets ont funding_type = misc_operation et sont liés à l'OD, pas directement au settlement.
Le comportement générique tente ensuite de compenser automatiquement le nouveau Funding avec les Funding ouverts de signe opposé sur le même compte de contrôle. Il peut créer des FundingAllocation et tenter un lettrage des lignes comptables. Aucun Payment ni mouvement bancaire n'est créé directement par la régularisation.
Transfert des lots et historique de propriété¶
Le transfert physique n'a pas lieu lors de prepare_accounting, mais après validation de la régularisation :
- l'acquéreur est validé si nécessaire, avec
date_from = transfer_date; PropertyLot.active_ownership_idest remplacé par l'acquéreur ;- le lien historique ouvert du vendeur reçoit
date_to = transfer_date - 1 jour; - un lien historique ouvert acquéreur est créé s'il n'en existe pas déjà ;
- si le vendeur ne conserve aucun lot actif dans la copropriété, son
Ownership.date_toprend la veille de la mutation ; - les exécutions des appels actifs dont la période couvre la date de mutation sont régénérées via
FundRequest::generate_executions().
Cette dernière opération porte sur les appels actifs couvrant la date de mutation. Le code ne parcourt pas explicitement « tous les proformas de l'exercice » ; il délègue la régénération au mécanisme standard du FundRequest.
Correspondances et clôture¶
Après validation, les correspondances existantes sont supprimées puis régénérées selon les préférences technical_communication du vendeur et de l'acquéreur. Une correspondance est créée pour chaque canal activé : email, postal, postal_registered ou postal_registered_receipt. En l'absence de préférence active, le recommandé simple est utilisé. Le destinataire est le représentant du dossier de propriété ; sans représentant, aucune correspondance n'est créée pour ce rôle.
La planification :
- programme l'action d'envoi différé des e-mails environ une minute plus tard ;
- crée une
ExportingTaskavec une ligne par méthode postale ; - renseigne
correspondences_dispatch_started_at.
L'e-mail est marqué émis lorsque le message avec PDF est accepté dans la file (is_sent = true, sent_date = now()). L'export postal fusionne les PDF et crée un document d'export.
Dans le code courant, la policy can_close ne contient aucun contrôle actif : le bloc qui attendait les e-mails et le téléchargement postal est commenté. La validation enchaîne donc immédiatement la clôture du settlement et de la mutation après planification, sans attendre l'envoi réel ni le téléchargement de l'export. L'export postal ne met pas non plus is_sent à jour.
Annulation et déverrouillage¶
L'action Déverrouiller de la mutation appelle cancel sur la régularisation, efface le lien actif, puis remet la mutation à settled.
La policy d'annulation vérifie notamment :
- que les OD comptabilisées et leurs écritures peuvent être annulées dans des périodes et exercices encore admissibles ;
- qu'aucune écriture n'est déjà extournée ;
- que les lots sont encore affectés soit au vendeur, soit à l'acquéreur ;
- si le lot est chez l'acquéreur, que les deux segments attendus de l'historique existent.
Le rollback :
- annule les OD
postedou supprime les brouillons/proformas ; - remet les lots encore détenus par l'acquéreur chez le vendeur ;
- rouvre les segments historiques du vendeur et supprime les segments acquéreur créés à la date de mutation ;
- rouvre le dossier vendeur si sa date de fin correspond à la veille de la mutation ;
- régénère les exécutions des appels actifs couvrant la date de mutation.
L'annulation générique d'une OD supprime ses Funding seulement s'ils n'ont pas été envoyés. Les Funding déjà envoyés ne sont pas supprimés par ce mécanisme et doivent être pris en compte lors de l'analyse d'une annulation.
Modèle de données utile¶
OwnershipTransferSettlement¶
Conserve le contexte figé (snapshot_at), la date comptable, les montants nets, la présence de sources, le résumé du calcul, les lignes, les OD, les correspondances, les informations de planification, les timestamps de validation/clôture et les logs techniques.
seller_net_amount et buyer_net_amount reçoivent actuellement le même total signé. Leur sens est donné par la convention de signe, et non par deux soldes opposés.
OwnershipTransferSettlementLine¶
Conserve la source et le lot, la période et les jours par partie, les montants réels et théoriques, le montant calculé, le montant retenu et le lien vers le wrapper d'OD. Seul applied_amount est destiné à l'ajustement manuel. Une ligne ne peut plus être modifiée ou supprimée après validation du settlement.
OwnershipTransferSettlementOperation¶
Relie une source, une clé idempotente et une MiscOperation. Les contraintes d'unicité portent sur (settlement_id, operation_key) et (settlement_id, misc_operation_id).
OwnershipTransferSettlementCorrespondence¶
Étend DocumentCorrespondence et ajoute le rôle du destinataire, l'indicateur de document généré, l'accusé de réception, les e-mails liés et le lien de téléchargement. L'unicité est définie par (settlement_id, recipient_role, communication_method).
Idempotence et atomicité observables¶
prepare_accountingne recrée pas de settlement tant que la mutation en référence déjà un.- La clé d'opération empêche la duplication d'une OD pour une même source.
- Les créations de Funding compensent les montants déjà générés pour la même OD et le même tiers avant de créer un delta complémentaire.
- La régénération des correspondances supprime et recrée les lignes tant que l'émission n'a pas commencé.
- Une fois
correspondences_dispatch_started_atrenseigné, une nouvelle planification est refusée.
Le code ne déclare pas de transaction explicite englobant toute la préparation ou toute la validation. Les hooks enchaînent plusieurs écritures et actions ; le niveau d'atomicité réel dépend donc du moteur ORM et du contrôleur de transition.
Limites et écarts connus¶
Les points suivants ne doivent pas être supposés comme implémentés :
- il n'existe pas de champ unique en base imposant « un settlement historique par mutation » ; le lien de la mutation désigne seulement le settlement actif ; après annulation, une nouvelle régularisation peut être créée ;
- les domaines des champs guident la sélection de lots et de dossiers de propriété dans l'UI, mais
can_prepare_accountingne revalide pas explicitement que chaque lot est encore détenu par le vendeur ni que les deux dossiers appartiennent à la copropriété ; - la préparation ne transfère pas les lots et ne génère pas automatiquement les lignes ;
- les ajustements manuels ne survivent pas à une régénération des lignes ;
- il n'existe ni inclusion/exclusion motivée, ni override justifié, ni ligne obsolète, ni hash/version de calcul ;
- les Funding utilisent le type générique
misc_operationet peuvent être compensés automatiquement ; - aucune liaison directe Funding → settlement/wrapper n'est définie ; la traçabilité passe par l'OD ;
- la clôture n'attend actuellement pas l'émission effective des correspondances ;
- le statut
financial_statement_sentn'est pas atteint par le workflow courant ; - la validation n'impose pas explicitement la présence de lignes ou d'OD ;
alert_summaryn'est pas une alerte applicative et aucune alerte dédiée au settlement n'est créée ;- aucune prise en charge dédiée des mutations successives sur une même période n'est visible dans ce workflow.
Ces écarts sont importants pour les critères d'acceptation : une évolution future devra décider si elle préserve le comportement courant ou si elle rétablit l'intention métier initiale.
Points d'entrée techniques¶
| Besoin | Point d'entrée |
|---|---|
| Préparer la régularisation | transition prepare_accounting sur OwnershipTransfer |
| Calculer/recalculer les lignes | action ORM generate_lines sur le settlement |
| Générer les OD pro forma | action ORM generate_operations |
| Valider et comptabiliser | transition validate |
| Transférer les lots | action interne transfer_property_lots |
| Régénérer les appels actifs | action interne refresh_fund_request_executions |
| Générer les correspondances | action interne generate_correspondences |
| Planifier les envois | action dispatch_correspondences |
| Annuler et restaurer | transition cancel / action rollback |