feat: implement issues #626–#629 — multi-currency FX, tax engine, payment reconciliation, cohort analytics - #665
Merged
Smartdevs17 merged 1 commit intoJul 29, 2026
Conversation
…cy FX, tax engine, payment reconciliation, cohort analytics Adds four platform-services features: - Smartdevs17#628 Automated payment reconciliation: exact/fuzzy matching engine, exception workflow, reconciliation reporting/analytics, REST API, and a daily scheduled task. Named `payment-reconciliation` throughout (routes, services, mount path) to avoid colliding with the existing unrelated on-chain balance-reconciliation feature (Smartdevs17#465) already at routes/reconciliation.ts and /api/v1/reconciliation. - Smartdevs17#627 Jurisdiction-aware tax engine: per-jurisdiction rule CRUD, automated calculation with exemption handling, compliance checks, and an audit trail — layered onto the existing tax-reports service/routes. - Smartdevs17#626 Multi-currency invoices: FX rate cache with TTL (and free history via cache rows), pluggable rate source, threshold alerts, and multi-currency invoice generation/reconversion/reporting on the existing invoice service. - Smartdevs17#629 Subscription cohort analytics: in-memory retention/revenue/churn cohort analysis, comparison, and CSV export, matching this codebase's existing lightweight analytics-service pattern. New Prisma models: ReconciliationBatch/Record/Match/Exception, TaxJurisdictionRule/TaxExemption/TaxCalculationAuditLog, FxRate/FxRateAlert, plus presentment-currency fields on Invoice. Known pre-existing issue (not introduced here, not fixed): schema.prisma on main currently defines `enum SubscriptionStatus` twice, which breaks `prisma generate`/`validate` for the whole schema. Verified our additions are valid in isolation; left the duplicate untouched since resolving it requires knowing which of the two call sites is authoritative. Closes Smartdevs17#626, Smartdevs17#627, Smartdevs17#628, Smartdevs17#629 Co-authored-by: Cursor <cursoragent@cursor.com>
|
@Abidoyesimze is attempting to deploy a commit to the smartdevs17's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@devsimze 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! 🚀 |
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.
Summary
Implements four platform-services issues:
#628 — Automated payment reconciliation
daily-payment-reconciliation,0 5 * * *).payment-reconciliationthroughout (routes/services/mount path) — this repo already has an unrelated on-chain balance reconciliation feature (Backend: Add Payment Reconciliation Report Generator with CSV/PDF Export #465) atroutes/reconciliation.ts//api/v1/reconciliation/services/reconciliation/, so a distinct name avoided clobbering it.backend/docs/PAYMENT_RECONCILIATION.md.#627 — Jurisdiction-aware tax engine
calculate()with exemption lookup and an audit-log entry per calculation.tax-reports.ts/routes/tax.ts(Issue Add tax report generation for merchant compliance #351) rather than duplicating it — new endpoints appended, existing ones untouched.backend/docs/TAX_ENGINE.md.#626 — Multi-currency invoices with FX conversion
FxService: TTL-based rate cache (each fetch is a newFxRaterow, so history comes for free), pluggable rate source (documented placeholder — swap in a real forex API), conversion, and threshold-based rate alerts.backend/docs/FX_CONVERSION.md.#629 — Subscription cohort retention analytics
analytics.tspattern rather than adding new schema, since there's no persisted subscription-lifecycle table to build on).backend/docs/COHORT_ANALYTICS.md.Schema
New Prisma models:
ReconciliationBatch/Record/Match/Exception,TaxJurisdictionRule/TaxExemption/TaxCalculationAuditLog,FxRate/FxRateAlert, pluspresentmentCurrency/presentmentAmount/fxRate/fxRateLockedAtonInvoice. Migration:backend/prisma/migrations/20260629130000_reconciliation_tax_fx/.Known pre-existing issue, not introduced or fixed by this PR:
schema.prismaonmaincurrently definesenum SubscriptionStatustwice (once for the Stripe-styleSubscriptionmodel, once under "New Enums"), which breaksprisma generate/prisma validatefor the whole schema. I verified this PR's additions are valid in isolation (temporarily renamed the second enum locally, confirmedprisma validatepasses, then reverted). Left it untouched since I don't know which of the two definitions is authoritative — flagging for maintainers.Test plan
npx vitest run src/services/__tests__/payment-reconciliation.test.ts src/services/__tests__/tax-engine.test.ts src/services/__tests__/fx-service.test.ts src/services/__tests__/cohort-analytics.test.ts src/services/__tests__/tax-reports.test.ts— 92/92 passingtsc --noEmit— zero new errors attributable to any file touched in this PR (verified by diffing against the pre-existing baseline error set)DATABASE_URLis unset, matching the pattern used byservices/archival/andservices/vaults/Closes #626
Closes #627
Closes #628
Closes #629