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

Three types of regularization exist: - Full cancellation (negative-only): all care acts from the original payment are cancelled - Amendment (negative + positive): negative care acts are matched 1:1 with positive counterparts (same care code, corrected data) - Drop-and-replace (negative + positive): - Amendment: some negative care acts match positive ones () - Cancellation: some negative care acts don't match any positive - Replacement: some positive care acts don't match any negative

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 CAPIs?}
    C -->|Yes| D[Find original CAPIs in DB]
    C -->|No| G[Positive CAPIs extracted normally]
    D --> E[Compute cancellation decision]
    E --> F{Has replacement care acts?}
    F -->|Yes| F1[Downgrade auto to manual cancellations]
    F1 --> F2[Replacement CAPIs extracted normally]
    F2 --> G
    F -->|No| F3[Execute automatic cancellations]
    F3 --> 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: _match_phases()
    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->>R: Downgrade auto→manual if replacements exist
    R-->>E: tuple[list[CareActToCancelDecision], list[InMemoryCareActInfo]]
Hold "Alt" / "Option" to enable pan & zoom

Step 1: Mini-matching β€” identify negative-only CAPIs

_match_phases() returns a RegularizationPhaseMatchingResult with three lists: - to_cancel_care_act_infos β€” negative CAPIs with no positive counterpart (care acts to cancel) - amended_care_act_infos β€” positive CAPIs matched 1:1 with a negative counterpart (amendments) - replacement_care_act_infos β€” positive CAPIs with no matching negative (drop-and-replace)

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, their positive counterparts go to amended_care_act_infos
  • Remaining negatives = to_cancel_care_act_infos
  • Unmatched positives are logged as a warning and returned as replacement_care_act_infos

When replacement_care_act_infos is non-empty (drop-and-replace), all to_cancel_automatically decisions are downgraded to to_cancel_manually. This prevents accidental overpayment when care codes change between original and replacement.

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
RegularizationDecision entities/regularization.py Groups decisions per original_payment_archiving_link
RegularizationPhaseMatchingResult entities/regularization.py Result of _match_phases(): to_cancel_care_act_infos, replacement_care_act_infos, amended_care_act_infos
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]