Skip to content

Feature/reconciliation - #12

Open
akeenkarkare wants to merge 14 commits into
old-developfrom
feature/reconciliation
Open

Feature/reconciliation#12
akeenkarkare wants to merge 14 commits into
old-developfrom
feature/reconciliation

Conversation

@akeenkarkare

Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai

coderabbitai Bot commented Jul 30, 2025

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (3)
  • uat
  • uat-dev
  • staging

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/reconciliation

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Rohitkumarthongar and others added 7 commits August 19, 2026 21:10
…ent-mode rules

Easebuzz settlement logs were stored but never turned into accounting entries,
so the Easebuzz PG suspense account never cleared. Build step 3 of the flow --
Dr Bank / Cr Easebuzz suspense -- as an automatic, idempotent posting pipeline.

Parse, preflight, segregate, validate, post, reconcile:

* Payload parsing. `settlement_hook` stores a Python repr of
  {'status', 'data', 'cmd'} and the settlement body lives in the *inner* JSON
  string. The old code parsed the outer wrapper, so split_payouts,
  total_amount and service_charge_amount were never reachable.
  `parse_settlement_payload` unwraps the envelope and also accepts bare JSON
  and already-parsed dicts.

* One Journal Entry per company, not per split payout. 364 of 400 production
  payloads span two companies (UESF and RESPL) and a Journal Entry belongs to
  exactly one company, so a single JE for the whole log is not representable
  in ERPNext. Each JE carries one debit row per split payout, one charges row,
  and one suspense credit; the credit is the sum of the rounded debits, so it
  balances exactly rather than to within a rounding error.

* Charge attribution. split_transactions[] carries no account_number -- only
  split_payout_id -- which is why the old charges loop resolved a Bank Account
  from None and never posted a single charge JE. Charges now join back to a
  company through split_payout_id, which resolves cleanly in all 400 payloads.

* Payment-mode rules. New `Easebuzz Payment Mode Rule` child table on Easebuzz
  Settings replaces the hardcoded transaction_type filter. A mode with
  Debit Charges unticked has its service charge and tax excluded from the
  charges line. Bank debits are never filtered -- that money physically
  reached the bank, so removing it would produce a JE that disagrees with the
  statement. Matching is on the raw Easebuzz string rather than the Mode of
  Payment link name, so renaming the ERPNext master cannot silently break a
  rule. Duplicate rows are rejected on save.

* Idempotency. Easebuzz redelivers settlements (payout PTOBWKJAUF arrives
  twice in the production log set), so an already-posted (payout_id, company)
  pair is recorded as Already Posted instead of posting again. A redelivery
  arrives as a new log with its own job_id, so `deduplicate` does not apply --
  posting is serialised on the payout with a filelock.

* Trigger moved from before_save to an enqueued after_insert. before_save
  re-posted the whole settlement on every subsequent save of the log.

* Failures are visible. Status (Pending / Processing / Processed /
  Needs Review / Failed / Skipped), the error text, and a per-company
  reconciliation row recording the JE, bank total, charges debited, charges
  skipped and the modes responsible. The bare except that logged to
  `logs/ease.log` -- a file no other easebuzz component writes to -- is gone.

* Preflight fails loudly with the company named when
  default_easebuzz_account or custom_easebuzz_charges is unset, rather than
  posting `account = None` and swallowing the validation error.

* First JE per company is held as Draft with the log at Needs Review so the
  mapping can be confirmed before anything reaches the ledger; auto-submit
  unlocks per company once one has been submitted. Settlements older than
  Reconcile Settlements On or After are marked Skipped and never posted.

Verified against 400 production payloads: 764 JEs, all balanced, bank totals
identical with rules on and off, included + skipped reconciles to the payload
header, and JEs whose charges are entirely skipped get no charges line rather
than a 0.00 one. Verified end to end against real ERPNext for payout
PT1J0YM50V: RESPL 1,018,437.87 + UESF 3,275,405.49 = 4,293,843.36 =
total_amount, difference 0.0, both submitted, then rolled back.

Note: split-level charge sums drift from the payload header by up to 0.13, so
the reconciliation tolerance defaults to 0.50 rather than 0.05.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The reconciliation branch never picked up the packaging metadata that prod
carries, so the app had only the legacy setup.py and no ruff configuration.
Restore prod's file verbatim so the two branches converge cleanly on merge
rather than diverging on build backend or lint rules.

Declares flit as the build backend with the version read from
easebuzz.__version__, requires Python >=3.10, pins frappe >=15.40.4,<16.0.0
for bench, and configures ruff (line length 110, target py310).

setup.py, requirements.txt and MANIFEST.in are left in place, matching prod.

Linting the app under this config reports 86 pre-existing findings across 10
legacy files -- mostly true-false comparisons, trailing whitespace and unsorted
imports. None are in the reconciliation code and none are fixed here; that
backlog is left for a separate pass so this commit stays packaging-only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e insert

Saving an Easebuzz Settlement Log did nothing, and with reconciliation switched
on it failed outright. Two causes, both fixed here.

Queueing could abort ingestion. `after_insert` called `frappe.enqueue`
unguarded, and enqueue raises ConnectionError when Redis is unreachable
(it only falls back to running synchronously during a migration). The
exception escaped `after_insert` and rolled back the insert, so on a bench
with no queue the settlement log was never saved at all -- the payload was
lost, and the gateway posts it exactly once. Reproduced on unity.local: with
Auto Create Journal Entry on and redis down, insert failed with
ConnectionError; with no settings record it saved but silently did nothing.

Queueing is now wrapped so nothing in it can raise. When the queue is
unreachable the work is registered on `frappe.db.after_commit` instead, which
runs it outside the save cycle, so a bench without workers still reconciles.

Trigger moved from `after_insert` to `on_update`, which fires on insert and on
every later save. Saving a log now starts reconciliation, and re-saving a
Failed one retries it -- fix the bank account mapping, hit save, it posts.
This is safe now in a way the original `before_save` hook was not: the pipeline
is idempotent, so a re-run records Already Posted rather than posting twice.
Only Pending and Failed logs auto-fire; Processed, Processing, Needs Review and
Skipped are left alone so re-saving cannot refire them. `_finish` marks its own
save with a flag so writing the outcome does not start another round.

Silent no-ops are now explained on the record. Previously a missing Easebuzz
Settings record or an unticked Auto Create Journal Entry returned without a
trace, which is indistinguishable from a broken hook. The reason is written to
the log's Error Message instead, and cleared once reconciliation starts.

Verified on unity.local across all four paths: no settings record -> saves,
Pending, reason recorded; Auto Create off -> saves, reason names the settings
record; Auto Create on with redis down -> saves (previously aborted) and the
after-commit fallback runs and records the real failure reason; re-saving a
Failed log retries it and terminates. 19 unit tests pass, ruff clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

4 participants