Skip to content

feat(admin): Say why collection will fail, when the data already knows - #246

Merged
saurabh6790 merged 1 commit into
frappe:developfrom
saurabh6790:feat/derived-outcomes
Aug 6, 2026
Merged

feat(admin): Say why collection will fail, when the data already knows#246
saurabh6790 merged 1 commit into
frappe:developfrom
saurabh6790:feat/derived-outcomes

Conversation

@saurabh6790

Copy link
Copy Markdown
Member

The simulator could say what a team would be billed and when we would ask for it. It could not say whether the ask would work. This adds that, but only where the answer is a fact rather than a forecast.

Three modes, and the output always names which produced it:

  • Optimistic — everything settles on time.
  • Assumed — the operator declares the outcome; the calendar follows their word.
  • Derived — failure is reported only where the team's state entails it.

Derived is the one that earns its keep, because every conclusion it reaches is already true in the database:

flowchart TD
    S["Team state"] --> A{"Anything decisive?"}
    A -->|no active method, no credits| F1["No way to pay"]
    A -->|Manual Checkout / Action Required| F2["Customer must act"]
    A -->|INR at or over silent threshold| F3["Lands in Action Required"]
    A -->|mandate ceiling below the bill| F4["Debit would be refused"]
    A -->|mandate awaiting re-consent| F5["Method is skipped"]
    A -->|card expires before the charge| F6["Expires mid-cycle"]
    A -->|credits-only, wallet short| F7["Shortfall"]
    A -->|nothing| OPEN["Open — not knowable from here"]

    style OPEN fill:#e4f5e9,stroke:#30a66d
Loading

An empty finding list is not a prediction of success. It means the data does not settle the question, and the page says exactly that rather than implying the charge will go through.

It reuses the collector, and avoids the one helper that writes

Every rule delegates to the production helper that governs it — silent_threshold, effective_cap, active_mandate_ceiling, reauth_pending, settlement_sources, ordered_methods — so the simulator and the charge loop cannot drift apart on who is chargeable.

collection_mode.evaluate() answers the threshold question too, and trips the profile into Action Required as a side effect. A projection asks; it does not push a team into a new collection mode. There is a test holding that.

Both branches stay visible

Marking the entailed arm must not hide the other one — an operator still needs to see what settling looks like even when it is not going to happen. The ruled-out branch is dimmed, not removed.

Reports only what the team's state already decides: no way to pay, an
on-session collection mode, an INR bill over the silent-debit threshold, a
mandate ceiling below the bill or awaiting re-consent, a card expiring
before the charge, a wallet that cannot cover it. Silence means the
question is open, not that it will succeed.

Reuses the production helpers so the simulator and the collector cannot
disagree, and deliberately avoids collection_mode.evaluate, which trips
the profile as a side effect.

Optimistic and Assumed modes sit alongside; both branches stay on screen
and the ruled-out one is dimmed rather than hidden.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@saurabh6790
saurabh6790 merged commit a6f4173 into frappe:develop Aug 6, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant