Repository navigation
Fix/invoice investment fixes batch - #653
Merged
TS-mfon merged 5 commits intoOct 17, 2026
Merged
Conversation
Postgres `date` columns come back from TypeORM as a bare "YYYY-MM-DD" string, which new Date(...) parses as that day's midnight UTC. Comparing that directly against "now" in investInInvoice, createInvestment, and the investor analytics overdue calc closed invoices for investment at the start of their due date instead of at the end of it. Add src/utils/date.utils.ts (isPastDueDate/endOfDayUTC) and use it consistently across investment.service.ts and Invoice.model.ts's isOverdue/isExpired, which shared the same bug. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
POST /invoices (self-serve submission) checked for an InvoiceService. submitInvoice method that doesn't exist, so it always fell through to a fallback that wrote the entity straight to the repository. That bypassed InvoiceStateMachine entirely: no transition validation, no invoice_status_history row, and none of the transition side effects ran. submit.ts now calls InvoiceService.createInvoice (drafts the invoice) followed by submitInvoiceForReview (drives the draft -> pending transition through the state machine, with its guard, history and notification side effects). Extend CreateInvoiceInput/InvoiceDTO with the sellerWallet/title/faceValue/fundingTarget/yieldBps/fundingDeadline/ ipfsDocumentUrl columns the self-serve flow captures, which createInvoice previously had no way to persist. Also fix the error handler to read ServiceError's statusCode (it was reading a nonexistent `status` field, so every service error surfaced as a 500). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
investInInvoice and createInvestment only checked invoice.dueDate (the
repayment date) to decide whether an invoice could still accept
investment, ignoring invoice.fundingDeadline (the funding campaign's own
cutoff). A long-tenor invoice could have its funding deadline pass while
dueDate was still months out, letting investors keep buying into a
closed campaign.
Add a fundingDeadline check alongside the existing dueDate check in both
methods, throwing ServiceError("funding_deadline_passed", ..., 422) when
it has passed. fundingDeadline is a `timestamptz` column (unlike the
`date`-typed dueDate), so a plain Date comparison is correct here.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
# Conflicts:
# tests/unit/invoice-invest-endpoint.test.ts
EventIndexerService was constructed with contractIds: [config.sorobanEscrow.contractId] only. The read-model projections wired onto its eventBus (creator key, ACL, curve migration, atomic swap, royalty, dividend) dispatch by event topic rather than contract id, so they only ever see an event if the contract that emits it is actually polled - and none of them are emitted by the escrow contract alone. In production this meant those projections never updated. Build contractIds from every configured Soroban contract address (escrow, payment distributor, subscription, token) via the sorobanConfig already loaded earlier in this function, deduplicated, and gate the indexer on at least one of them being present. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
@The-Big-Danny Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
PR StellarState#653's 4 CI checks (Build, Lint, Type Check, Test) were failing, but verification showed dev itself is red on the identical errors at the same commit - unrelated to the 4 invoice/investment fixes in this PR. Fixing them here so the PR can go green rather than leaving dev broken. Type Check / Build: - InvestorAccreditation.model.ts, SecondaryMarketPriceHistory.model.ts: add missing `!` definite-assignment on TypeORM columns. - secondary-market-price.service.ts: fix the entity/field references to point at the real SecondaryListing model (askPrice/soldAt never existed; the actual fields are pricePerFraction/status). - accreditation.routes.ts, refund.routes.ts, price-history.routes.ts: Express 5's ParamsDictionary types a route param as `string | string[]` to support wildcard segments, so narrow req.params to the route's own shape per this repo's existing PublishInvoiceRequest convention; fix HttpError's swapped (message, status) call sites to (status, message); fix req.user?.wallet (no such field) to req.user?.stellarAddress; requireAdmin() missing its adminWallets argument. - invoice-refund.service.ts: ServiceError(message, status) swapped to (code, message, status); Investment has no `amount` field, only investmentAmount. Also fixed getRefundableInvoices, which returned every expired invoice platform-wide regardless of the wallet argument it was given - a real cross-investor data leak, not just a type error. Lint: removed 9 unused imports/vars across 6 files (investor-risk-profile .controller.ts, invoice-draft.controller.ts, investor.routes.ts, investor-accreditation.service.ts, invoice-refund.service.ts, issuer-reputation.service.ts, including a dead TIER_LABELS map that duplicated computeTier's logic). Test: tests/fee-schedule.test.ts's DataSource never initialized - User's relations reference Transaction/KYCVerification/KycHistory/ Notification/SecondaryListing/Watchlist by string name, none of which were registered in tests/helpers/test-data-source.ts, so TypeORM's metadata builder couldn't resolve the inverse side. Registering them surfaced a second, pre-existing issue: FeeSchedule's enum/timestamptz columns aren't valid sqlite types, worked around the same way nine other integration tests in this repo already do (rewriting column metadata before the DataSource is built). That in turn surfaced two more pre-existing test bugs once they could finally run: beforeEach cleared the FK parent (FeeSchedule) before the child (FeeScheduleHistory), and three assertions checked error.message for a ServiceError's `code` instead of its `message`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Closes #623
Closes #624
Closes #622
Closes #617
Summary of all 4 fixes (branch: fix/invoice-investment-fixes-batch, 4 commits ahead of dev)
Verification: full suite run after each fix and again on the consolidated branch — 1644 tests passing; the only failure (fee-schedule.test.ts, 7 tests, pre-existing TypeORM metadata error unrelated to these changes) is present identically on dev. Type-check and lint clean on every touched file.