Skip to content

ENG-181: Deal-related and fee-related cash event integration (#253)#255

Merged
Connorbelez merged 1 commit intomainfrom
Connorbelez/fix-servicing-fee-args
Mar 22, 2026
Merged

ENG-181: Deal-related and fee-related cash event integration (#253)#255
Connorbelez merged 1 commit intomainfrom
Connorbelez/fix-servicing-fee-args

Conversation

@Connorbelez
Copy link
Copy Markdown
Owner

@Connorbelez Connorbelez commented Mar 22, 2026

ENG-181: Deal-related and fee-related cash event integration (#253)

Wire 4 deal/fee cash events through the posting pipeline

  • postDealBuyerFundsReceived (CASH_RECEIVED → TRUST_CASH/CASH_CLEARING)
  • postDealSellerPayout (LENDER_PAYOUT_SENT → LENDER_PAYABLE/TRUST_CASH)
  • postLockingFeeReceived (CASH_RECEIVED → TRUST_CASH/UNAPPLIED_CASH)
  • postCommitmentDepositReceived (CASH_RECEIVED → TRUST_CASH/UNAPPLIED_CASH)

Also fixes CREDIT_NORMAL_FAMILIES to include CASH_CLEARING and UNAPPLIED_CASH — these are liabilities (credit-normal) not assets, which corrects balance computation polarity for all future entries.

Changes

  • Adds 4 new integration functions in integrations.ts with proper idempotency keys and metadata
  • Expands CASH_RECEIVED family map to accept CASH_CLEARING and UNAPPLIED_CASH as credit accounts
  • Adds dealId field to journal entries schema with corresponding index
  • Includes comprehensive test coverage for all functions including idempotency and error handling
  • Corrects account normal balance classifications for proper accounting semantics

Summary by Sourcery

Bug Fixes:

  • Fix runtime crash in obligation settlement dispersal caused by missing paymentFrequency argument in servicing fee calculation.

Summary by CodeRabbit

  • New Features

    • Added cash posting functions for deal-related transactions: buyer funds received, seller payouts, locking fees, and commitment deposits.
    • Added support for associating cash ledger entries with specific deals for better transaction tracking.
  • Tests

    • Comprehensive test coverage for new cash posting functions, including idempotency and error handling scenarios.
  • Bug Fixes

    • Corrected cash account balance preconditions in existing tests.

ENG-224: Pass paymentFrequency to calculateServicingFee

ENG-152 added paymentFrequency as a required third parameter but the
caller in calculateServicingSplit was not updated, causing a runtime
crash on any obligation settlement triggering dispersal.

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

ENG-152 added paymentFrequency as a required third parameter but the
caller in calculateServicingSplit was not updated, causing a runtime
crash on any obligation settlement triggering dispersal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 22, 2026 23:44
@linear
Copy link
Copy Markdown

linear bot commented Mar 22, 2026

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Mar 22, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Implements end-to-end posting for deal- and fee-related cash events, associates journal entries with deals, fixes account normal-balance classifications, and updates a servicing-fee calculation to pass the required paymentFrequency parameter to avoid runtime crashes.

Sequence diagram for deal buyer funds received cash posting

sequenceDiagram
    actor CashSystem
    participant Integrations as DealCashIntegrations
    participant Posting as PostingPipeline
    participant Idem as IdempotencyStore
    participant Ledger as JournalEntryStore

    CashSystem->>Integrations: postDealBuyerFundsReceived(dealId, cashEventId, amount, currency)
    Integrations->>Idem: checkKeyExists(cashEventId)
    Idem-->>Integrations: exists?
    alt first_time_event
        Integrations->>Posting: buildAndPostEntry(dealId, debitAccount TRUST_CASH, creditAccount CASH_CLEARING, amount)
        Posting->>Ledger: createJournalEntry(dealId, debitAccount, creditAccount, amount, metadata)
        Ledger-->>Posting: journalEntryId
        Posting-->>Integrations: postingResult
        Integrations->>Idem: storeKey(cashEventId, journalEntryId)
        Idem-->>Integrations: stored
        Integrations-->>CashSystem: postingResult
    else duplicate_event
        Integrations-->>CashSystem: existingPostingResult
    end
Loading

Entity relationship diagram for journal entries associated to deals

erDiagram
    Deal {
        string id
        string externalId
        string status
    }

    JournalEntry {
        string id
        string dealId
        string debitAccountId
        string creditAccountId
        decimal amount
        string currency
        string metadata
        datetime createdAt
    }

    Deal ||--o{ JournalEntry : has
Loading

Class diagram for cash event integrations, journal entries, and servicing fee calculation

classDiagram

    class DealCashIntegrations {
        +postDealBuyerFundsReceived(dealId, cashEventId, amount, currency)
        +postDealSellerPayout(dealId, cashEventId, amount, currency)
        +postLockingFeeReceived(dealId, cashEventId, amount, currency)
        +postCommitmentDepositReceived(dealId, cashEventId, amount, currency)
        -buildIdempotencyKey(cashEventId)
    }

    class PostingPipeline {
        +postCashMovement(dealId, debitAccountCode, creditAccountCode, amount, currency, metadata)
    }

    class JournalEntry {
        +string id
        +string dealId
        +string debitAccountCode
        +string creditAccountCode
        +decimal amount
        +string currency
        +string metadata
        +datetime createdAt
    }

    class IdempotencyStore {
        +boolean hasKey(idempotencyKey)
        +string getResult(idempotencyKey)
        +void saveResult(idempotencyKey, postingResult)
    }

    class AccountFamilyMap {
        +boolean isCreditNormal(accountCode)
        +void setCreditNormal(accountCode, isCreditNormal)
    }

    class ServicingCalculator {
        +calculateServicingSplit(servicingConfig, mortgage, settledAmount)
        +calculateServicingFee(annualRate, principal, paymentFrequency)
    }

    class ServicingConfig {
        +decimal annualRate
    }

    class Mortgage {
        +decimal principal
        +string paymentFrequency
    }

    DealCashIntegrations --> PostingPipeline : uses
    DealCashIntegrations --> IdempotencyStore : uses
    PostingPipeline --> JournalEntry : creates
    AccountFamilyMap --> JournalEntry : validatesAccounts
    ServicingCalculator --> ServicingConfig : uses
    ServicingCalculator --> Mortgage : uses
Loading

File-Level Changes

Change Details Files
Implement new posting integrations for deal and fee cash events with idempotency and metadata.
  • Add four integration functions to wire buyer funds received, seller payout, locking fee received, and commitment deposit received cash events through the posting pipeline
  • Ensure each integration function uses a stable idempotency key to prevent duplicate postings
  • Attach appropriate metadata (including deal context where applicable) to each new cash posting integration
integrations.ts
Adjust accounting configuration for cash-related accounts and cash-received mappings.
  • Expand the CASH_RECEIVED family mapping to allow CASH_CLEARING and UNAPPLIED_CASH as valid credit accounts for received cash events
  • Classify CASH_CLEARING and UNAPPLIED_CASH as credit-normal families to reflect their liability nature and correct balance polarity
  • Update or fix any tests that assumed previous account balance behavior to align with corrected accounting semantics
accounting configuration files
related test files
Add deal association to journal entries to support deal-linked cash tracking.
  • Extend the journal entries schema to include a dealId field for associating entries with deals
  • Create an index on dealId to support efficient querying and reporting by deal
  • Update any affected code paths or tests to populate and assert on dealId where applicable
journal entries schema file
migration or schema index definition files
journal entry tests
Add comprehensive tests for new integrations including idempotency and error handling.
  • Introduce tests for each new cash posting integration covering normal flows, idempotent replays, and unhappy paths
  • Verify that posting results reflect correct account families and metadata, including dealId and cash account classifications
  • Adjust existing tests whose expected cash account behavior changed due to CREDIT_NORMAL_FAMILIES updates
integration/unit test files for posting pipeline
test utilities as needed
Fix servicing fee calculation by passing the required paymentFrequency argument.
  • Update calculateServicingSplit to call calculateServicingFee with annualRate, principal, and paymentFrequency from the mortgage args
  • Ensure this prevents runtime crashes when obligation settlement triggers dispersal and servicing fee calculation
  • Optionally adjust or add tests to cover the three-parameter calculateServicingFee usage
convex/dispersal/createDispersalEntries.ts
related servicing fee tests if present

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Connorbelez has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 22, 2026

Warning

Rate limit exceeded

@Connorbelez has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 19 minutes and 7 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4060a3ab-f6d8-455c-8e33-6a9b61582317

📥 Commits

Reviewing files that changed from the base of the PR and between f5cc8d2 and 947f120.

📒 Files selected for processing (1)
  • convex/dispersal/createDispersalEntries.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Connorbelez/fix-servicing-fee-args

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Owner Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • When passing args.mortgage.paymentFrequency into calculateServicingFee, consider handling or validating unexpected/legacy values (including undefined/null) to avoid new runtime failures where older records may not have this field populated.
  • If paymentFrequency has a constrained set of allowed values, it may be helpful to reflect that in the type of args.mortgage so that incorrect values are caught at compile time rather than during fee calculation.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- When passing `args.mortgage.paymentFrequency` into `calculateServicingFee`, consider handling or validating unexpected/legacy values (including `undefined`/`null`) to avoid new runtime failures where older records may not have this field populated.
- If `paymentFrequency` has a constrained set of allowed values, it may be helpful to reflect that in the type of `args.mortgage` so that incorrect values are caught at compile time rather than during fee calculation.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@Connorbelez Connorbelez merged commit 985fa01 into main Mar 22, 2026
3 of 5 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the dispersal pipeline’s servicing-fee calculation to use the mortgage’s payment frequency, aligning the call site with the updated calculateServicingFee API and preventing runtime failures during obligation settlement/dispersal.

Changes:

  • Passes mortgage.paymentFrequency into calculateServicingFee when computing servicing fee due.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Connorbelez added a commit that referenced this pull request Apr 20, 2026
…255)

ENG-181: Deal-related and fee-related cash event integration (#253)

## Wire 4 deal/fee cash events through the posting pipeline

- postDealBuyerFundsReceived (CASH_RECEIVED → TRUST_CASH/CASH_CLEARING)
- postDealSellerPayout (LENDER_PAYOUT_SENT → LENDER_PAYABLE/TRUST_CASH)
- postLockingFeeReceived (CASH_RECEIVED → TRUST_CASH/UNAPPLIED_CASH)
- postCommitmentDepositReceived (CASH_RECEIVED → TRUST_CASH/UNAPPLIED_CASH)

Also fixes CREDIT_NORMAL_FAMILIES to include CASH_CLEARING and UNAPPLIED_CASH — these are liabilities (credit-normal) not assets, which corrects balance computation polarity for all future entries.

### Changes

- Adds 4 new integration functions in `integrations.ts` with proper idempotency keys and metadata
- Expands CASH_RECEIVED family map to accept CASH_CLEARING and UNAPPLIED_CASH as credit accounts
- Adds dealId field to journal entries schema with corresponding index
- Includes comprehensive test coverage for all functions including idempotency and error handling
- Corrects account normal balance classifications for proper accounting semantics

## Summary by Sourcery

Bug Fixes:
- Fix runtime crash in obligation settlement dispersal caused by missing paymentFrequency argument in servicing fee calculation.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added cash posting functions for deal-related transactions: buyer funds received, seller payouts, locking fees, and commitment deposits.
  * Added support for associating cash ledger entries with specific deals for better transaction tracking.

* **Tests**
  * Comprehensive test coverage for new cash posting functions, including idempotency and error handling scenarios.

* **Bug Fixes**
  * Corrected cash account balance preconditions in existing tests.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

ENG-224: Pass paymentFrequency to calculateServicingFee

ENG-152 added paymentFrequency as a required third parameter but the
caller in calculateServicingSplit was not updated, causing a runtime
crash on any obligation settlement triggering dispersal.

Co-Authored-By: Claude Opus 4.6 (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.

2 participants