Skip to content

Noemie Decompte Regularization

Since September 2025, Ameli sends regularization decomptes that correct previously processed decomptes.

Decompte structure

A noemie decompte contains one or more payments, each identified by an archiving_link. Each payment contains one or more care acts. Most decomptes have a single payment, but ~5% have multiple payments with different archiving links.

A regularization targets specific payments (not necessarily the whole decompte). It references the original payment via original_payment_archiving_link and contains: - A negative phase: care acts with inverted signs, cancelling the original payment's care acts - Optionally a positive phase: replacement care acts with corrected data

Two types of regularization exist: - Full cancellation (negative-only): all care acts from the original payment are cancelled - Amendment (negative + positive): some care acts are cancelled and replaced with corrected ones

Each regularization carries a RegularizationReason ⧉ code (e.g. change of regime, pricing error) extracted from the NOEMIE data.

You can read more about the noemie norm in the official documentation ⧉ or see examples provided by Ameli ⧉

High-Level Flow

flowchart TD
    A[Regularization decompte received] --> B[Extraction]
    B --> C{Has negative-only CAPIs?}
    C -->|Yes| D[Find original CAPIs in DB]
    C -->|No| G[Positive CAPIs extracted normally]
    D --> E[Compute cancellation decision]
    E --> F[Execute automatic cancellations]
    F --> G
    G --> H[Matching]
    H --> I[Consolidation]
Hold "Alt" / "Option" to enable pan & zoom

Extraction

File: compute_extraction_decompte.py

When processing a decompte, each payment is checked for an original_payment_archiving_link. If present, it's a regularization payment.

Phase detection

_determine_regularization_phase() determines whether a payment belongs to the negative or positive phase:

Condition Phase
original_payment_archiving_link is None Not a regularization
First occurrence of archiving link, sign = "N" Negative
Second occurrence (via previous_archiving_link) Positive
First occurrence but sign = "P" and non-negative NOEMIE code Positive (rare edge case)

Grouping

Care acts are grouped into RegularizationGroupingResult objects keyed by original_payment_archiving_link in DecompteExtractionResult.regularizations. Each group accumulates its negative and positive phase CAPIs separately.

After extraction, for each group: 1. check_data_conformity() validates phase/reason consistency 2. compute_care_acts_to_cancel_decision() determines which care acts to cancel 3. Positive phase CAPIs are added to the normal extraction output

Identifying Care Acts to Cancel

File: decompte_regularization.py

This is the core logic. The sequence below shows how we go from raw regularization data to cancellation decisions.

sequenceDiagram
    participant E as Extraction
    participant R as RegularizationGroupingResult
    participant DB as Database

    E->>R: compute_care_acts_to_cancel_decision()
    R->>R: _get_cancelled_care_act_infos()
    Note over R: Match positive CAPIs to negative CAPIs<br/>Remaining negatives = care acts to cancel

    loop For each care act to cancel
        R->>DB: _find_original_capis_for_care_act_info()
        Note over DB: Query by archiving_link +<br/>insurance_profile + amounts + dates
        DB-->>R: Matching original CAPIs
        R->>R: _resolve_capi_to_cancel()
        Note over R: 0 matches → error<br/>1 match → use it<br/>N same decompte → pick first unmatched<br/>N different decomptes → ambiguous error
        R->>R: _compute_cancellation_status()
        Note over R: See decision tree below
    end
    R-->>E: list[CareActToCancelDecision]
Hold "Alt" / "Option" to enable pan & zoom

Step 1: Mini-matching — identify negative-only CAPIs

_get_cancelled_care_act_infos() separates negative CAPIs that have a positive counterpart (amendments) from those that don't (cancellations).

For each positive CAPI, it finds a matching negative CAPI via _get_matched_care_act_info() using progressive field matching. The discriminating fields (side, dental_locations) are always compared. Then combinations of start_date, secu_grouping_code, secu_reimbursement_base, total_spend are tried from most to least specific.

  • Matched negatives are removed from the cancelled list
  • Remaining negatives = care acts to cancel
  • Unmatched positives raise an error (cannot determine which negative they replace)

Step 2: Find original CAPIs from the original decompte

_find_original_capis_for_care_act_info() queries the database for the original CAPIs being regularized.

Primary lookup: match on archiving_link = the regularization's original_payment_archiving_link

Fallback: for non-backfilled CAPIs (~5%), match on noemie_decompte.unique_identifier

Match fields: insurance_profile_id, secu_grouping_code, start_date, total_spend, secu_reimbursement_base, side, dental_locations

When multiple CAPIs match, _resolve_capi_to_cancel() resolves ambiguity:

Scenario Resolution
0 matches Error (strict) or warning (non-strict)
1 match Use it
N matches, same decompte Pick first unmatched (GDP-1009 fix)
N matches, different decomptes Ambiguous, error

Step 3: Compute cancellation decision

_compute_cancellation_status() determines what to do with each matched care act:

Care act found
├── No linked CareAct → skip (return None)
├── Multiple noemie decompte sources → TO_KEEP (duplicate, other source still valid)
├── Already cancelled → ALREADY_CANCELLED
├── Single source: 1 noemie decompte only → TO_CANCEL_AUTOMATICALLY
└── Multiple source types (noemie + TP) → TO_CANCEL_MANUALLY

The auto-cancel check in _can_automatically_cancel_care_act() returns True only if the care act has exactly one source of type noemie_decompte.

Step 4: Execute automatic cancellations

File: process_noemie_decompte.py

_cancel_regularized_care_act_in_noemie_decompte() filters decisions for to_cancel_automatically and calls cancel_care_act() with skip_cancellation_review=True.

Matching Rule

File: source_care_act_extraction.py

A regularization CAPI must match a CareAct whose original CAPI has the same archiving_link as the regularization's original_payment_archiving_link.

  • _is_capi_regularization_of_caci() checks this against consolidated info
  • _is_capi_regularization_of_care_act() checks this against the care act's noemie decompte CAPIs, with a fallback to noemie_decompte.unique_identifier for non-backfilled data

When a regularization match is confirmed, data review checks (BRSS, total_spend, care_type, start_date) are skipped via _skip_review_reasons() — differences between original and regularization data are expected and handled in consolidation.

Consolidation Rule

File: compute_consolidation.py

reduce_care_act_partial_infos_to_consolidate() ensures the regularization decompte's data takes precedence over the original:

  1. Collect all original_payment_archiving_link values from the CAPIs
  2. Filter out any CAPI whose archiving_link appears in that set (i.e. the original decompte's CAPIs)
  3. Among remaining noemie decompte CAPIs, keep only the latest (by created_at)

This guarantees consolidated data always comes from the regularization decompte, not the original.

Key Data Structures

Type Location Description
RegularizationPhase enums/regularization.py negative / positive
CareActRegularizationStatus enums/regularization.py to_cancel_automatically, to_cancel_manually, to_keep, already_cancelled, unknown
RegularizationReason enums/ NOEMIE reason code for the regularization
CareActToCancelDecision entities/regularization.py Links a CAPI + CareAct ID + cancellation status
RegularizationCancellationDecision entities/regularization.py Groups decisions per original_payment_archiving_link
RegularizationGroupingResult decompte_regularization.py Core class: holds negative/positive CAPIs and all cancellation logic
DecompteExtractionResult decompte_extraction_result.py Extraction output: simple CAPIs + dict[str, RegularizationGroupingResult]