@classytic/revenue 2.5.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import { n as createEvent, t as REVENUE_EVENTS } from "./event-constants-DM_-A57
3
3
  import { g as SETTLEMENT_STATUS, r as SUBSCRIPTION_STATUS, w as HOLD_STATUS } from "./subscription.enums-95othr0i.mjs";
4
4
  import { _ as WrongTransactionKindError, g as ValidationError, h as TransactionNotFoundError, m as SubscriptionNotFoundError, n as BankFeedImportError, o as MethodKindLockedError, p as SettlementNotFoundError } from "./errors-Bt5NRVMq.mjs";
5
5
  import { SETTLEMENT_STATE_MACHINE, SUBSCRIPTION_STATE_MACHINE, TRANSACTION_STATE_MACHINE, smFor } from "./core/state-machines.mjs";
6
- import { a as reverseTax, c as reverseCommission, n as calculateSplits, s as calculateCommission, t as calculateOrganizationPayout } from "./splits-CNfQj92L.mjs";
6
+ import { c as calculateCommission, l as reverseCommission, n as calculateSplits, o as reverseTax, t as calculateOrganizationPayout } from "./splits-CEdZN6OT.mjs";
7
7
  import { Repository, repoOptionsFromCtx, withTransaction } from "@classytic/mongokit";
8
8
 
9
9
  //#region src/repositories/base.repository.ts
@@ -1402,6 +1402,38 @@ var SubscriptionRepository = class extends RevenueRepositoryBase {
1402
1402
 
1403
1403
  //#endregion
1404
1404
  //#region src/repositories/settlement.repository.ts
1405
+ function emptySettlementSummary() {
1406
+ return {
1407
+ currency: null,
1408
+ pending: 0,
1409
+ held: 0,
1410
+ available: 0,
1411
+ processing: 0,
1412
+ paidOut: 0,
1413
+ failed: 0,
1414
+ lifetime: 0
1415
+ };
1416
+ }
1417
+ /** Bucket one aggregated (status, due, total) row into a running summary. */
1418
+ function foldSettlementStatus(s, status, due, total) {
1419
+ switch (status) {
1420
+ case SETTLEMENT_STATUS.PENDING:
1421
+ s.pending += total;
1422
+ if (due) s.available += total;
1423
+ else s.held += total;
1424
+ break;
1425
+ case SETTLEMENT_STATUS.PROCESSING:
1426
+ s.processing += total;
1427
+ break;
1428
+ case SETTLEMENT_STATUS.COMPLETED:
1429
+ s.paidOut += total;
1430
+ break;
1431
+ case SETTLEMENT_STATUS.FAILED:
1432
+ s.failed += total;
1433
+ break;
1434
+ default: break;
1435
+ }
1436
+ }
1405
1437
  /**
1406
1438
  * SettlementRepository — payouts to recipients (organizations, vendors,
1407
1439
  * affiliates).
@@ -1563,12 +1595,16 @@ var SettlementRepository = class extends RevenueRepositoryBase {
1563
1595
  }), ctx);
1564
1596
  return updated;
1565
1597
  }
1566
- async recipientBalance(recipientId, options = {}, ctx = {}) {
1567
- const match = { recipientId };
1568
- if (options.recipientType !== void 0) match.recipientType = options.recipientType;
1569
- if (options.currency !== void 0) match.currency = options.currency;
1598
+ /**
1599
+ * Settlement money-state for an ARBITRARY filter — a recipient, an org, or
1600
+ * the whole platform (pass `_bypassTenant` in ctx to span every org). One
1601
+ * tenant-scoped `aggregatePipeline` (the `{ recipientId, status }` index backs
1602
+ * the `$match`), never a raw `Model.aggregate`. The reusable rollup behind
1603
+ * `recipientBalance`, platform reconciliation, and cross-org earnings.
1604
+ */
1605
+ async summary(filter = {}, ctx = {}) {
1570
1606
  const now = /* @__PURE__ */ new Date();
1571
- const rows = await this.aggregatePipeline([{ $match: match }, { $group: {
1607
+ const rows = await this.aggregatePipeline([{ $match: filter }, { $group: {
1572
1608
  _id: {
1573
1609
  status: "$status",
1574
1610
  due: { $lte: ["$scheduledAt", now] }
@@ -1576,39 +1612,64 @@ var SettlementRepository = class extends RevenueRepositoryBase {
1576
1612
  total: { $sum: "$amount" },
1577
1613
  currency: { $first: "$currency" }
1578
1614
  } }], this.optsFromCtx(ctx));
1579
- const balance = {
1580
- recipientId,
1581
- currency: options.currency ?? null,
1582
- pending: 0,
1583
- held: 0,
1584
- available: 0,
1585
- processing: 0,
1586
- paidOut: 0,
1587
- failed: 0,
1588
- lifetime: 0
1589
- };
1615
+ const out = emptySettlementSummary();
1590
1616
  for (const row of rows) {
1591
- switch (row._id.status) {
1592
- case SETTLEMENT_STATUS.PENDING:
1593
- balance.pending += row.total;
1594
- if (row._id.due) balance.available += row.total;
1595
- else balance.held += row.total;
1596
- break;
1597
- case SETTLEMENT_STATUS.PROCESSING:
1598
- balance.processing += row.total;
1599
- break;
1600
- case SETTLEMENT_STATUS.COMPLETED:
1601
- balance.paidOut += row.total;
1602
- break;
1603
- case SETTLEMENT_STATUS.FAILED:
1604
- balance.failed += row.total;
1605
- break;
1606
- default: break;
1617
+ foldSettlementStatus(out, row._id.status, row._id.due, row.total);
1618
+ if (row.currency && !out.currency) out.currency = row.currency;
1619
+ }
1620
+ out.lifetime = out.pending + out.processing + out.paidOut;
1621
+ return out;
1622
+ }
1623
+ /**
1624
+ * Per-recipient settlement rollup — one {@link RecipientBreakdown} per
1625
+ * distinct (recipientId, recipientType) matching `filter`. The reusable list
1626
+ * behind a platform "who is owed what" view; pass `_bypassTenant` to span all
1627
+ * orgs.
1628
+ */
1629
+ async breakdownByRecipient(filter = {}, ctx = {}) {
1630
+ const now = /* @__PURE__ */ new Date();
1631
+ const rows = await this.aggregatePipeline([{ $match: filter }, { $group: {
1632
+ _id: {
1633
+ recipientId: "$recipientId",
1634
+ recipientType: "$recipientType",
1635
+ status: "$status",
1636
+ due: { $lte: ["$scheduledAt", now] }
1637
+ },
1638
+ total: { $sum: "$amount" },
1639
+ currency: { $first: "$currency" },
1640
+ role: { $first: "$metadata.role" }
1641
+ } }], this.optsFromCtx(ctx));
1642
+ const byKey = /* @__PURE__ */ new Map();
1643
+ for (const row of rows) {
1644
+ const key = `${row._id.recipientType}:${row._id.recipientId}`;
1645
+ let r = byKey.get(key);
1646
+ if (!r) {
1647
+ r = {
1648
+ recipientId: row._id.recipientId,
1649
+ recipientType: row._id.recipientType,
1650
+ role: row.role ?? null,
1651
+ ...emptySettlementSummary()
1652
+ };
1653
+ byKey.set(key, r);
1607
1654
  }
1608
- if (row.currency && !balance.currency) balance.currency = row.currency;
1655
+ foldSettlementStatus(r, row._id.status, row._id.due, row.total);
1656
+ if (row.role && !r.role) r.role = row.role;
1657
+ if (row.currency && !r.currency) r.currency = row.currency;
1609
1658
  }
1610
- balance.lifetime = balance.pending + balance.processing + balance.paidOut;
1611
- return balance;
1659
+ for (const r of byKey.values()) r.lifetime = r.pending + r.processing + r.paidOut;
1660
+ return [...byKey.values()];
1661
+ }
1662
+ /** One recipient's wallet — a thin `summary` over `{ recipientId, … }`. */
1663
+ async recipientBalance(recipientId, options = {}, ctx = {}) {
1664
+ const filter = { recipientId };
1665
+ if (options.recipientType !== void 0) filter.recipientType = options.recipientType;
1666
+ if (options.currency !== void 0) filter.currency = options.currency;
1667
+ const s = await this.summary(filter, ctx);
1668
+ return {
1669
+ recipientId,
1670
+ ...s,
1671
+ currency: options.currency ?? s.currency
1672
+ };
1612
1673
  }
1613
1674
  };
1614
1675
 
@@ -1,2 +1,2 @@
1
- import { A as SplitInfo, B as validateTaxCalculation, C as multiplyMoney, D as toCurrencyCode, E as sumMoney, F as TaxConfig, H as calculateCommission, I as TaxType, L as calculateTax, M as calculateOrganizationPayout, N as calculateSplits, O as toMajor, P as TaxCalculation, R as getTaxType, S as money, T as subtractMoney, U as reverseCommission, V as CommissionInfo, _ as isMoney, a as CurrencyCode, b as isZeroMoney, c as Money, d as addMoney, f as compareMoney, g as isCurrencyCode, h as fromSmallestUnit, i as CURRENCIES, j as SplitRule, k as toSmallestUnit, l as MoneyValue, m as fromMajor, n as getAuditTrail, o as CurrencyMismatchError, p as equalsMoney, r as getLastStateChange, s as MINOR_UNIT_FACTOR, t as appendAuditEvent, u as absMoney, v as isNegativeMoney, w as negateMoney, x as minorUnitFactor, y as isPositiveMoney, z as reverseTax } from "../audit-DRKuLBFO.mjs";
2
- export { CURRENCIES, CommissionInfo, type CurrencyCode, CurrencyMismatchError, MINOR_UNIT_FACTOR, type Money, type MoneyValue, SplitInfo, SplitRule, TaxCalculation, TaxConfig, TaxType, absMoney, addMoney, appendAuditEvent, calculateCommission, calculateOrganizationPayout, calculateSplits, calculateTax, compareMoney, equalsMoney, fromMajor, fromSmallestUnit, getAuditTrail, getLastStateChange, getTaxType, isCurrencyCode, isMoney, isNegativeMoney, isPositiveMoney, isZeroMoney, minorUnitFactor, money, multiplyMoney, negateMoney, reverseCommission, reverseTax, subtractMoney, sumMoney, toCurrencyCode, toMajor, toSmallestUnit, validateTaxCalculation };
1
+ import { A as SplitInfo, B as reverseTax, C as multiplyMoney, D as toCurrencyCode, E as sumMoney, F as TaxCalculation, H as CommissionInfo, I as TaxConfig, L as TaxType, M as calculateOrganizationPayout, N as calculateSplits, O as toMajor, P as reverseSplits, R as calculateTax, S as money, T as subtractMoney, U as calculateCommission, V as validateTaxCalculation, W as reverseCommission, _ as isMoney, a as CurrencyCode, b as isZeroMoney, c as Money, d as addMoney, f as compareMoney, g as isCurrencyCode, h as fromSmallestUnit, i as CURRENCIES, j as SplitRule, k as toSmallestUnit, l as MoneyValue, m as fromMajor, n as getAuditTrail, o as CurrencyMismatchError, p as equalsMoney, r as getLastStateChange, s as MINOR_UNIT_FACTOR, t as appendAuditEvent, u as absMoney, v as isNegativeMoney, w as negateMoney, x as minorUnitFactor, y as isPositiveMoney, z as getTaxType } from "../audit-BkDdPSed.mjs";
2
+ export { CURRENCIES, CommissionInfo, type CurrencyCode, CurrencyMismatchError, MINOR_UNIT_FACTOR, type Money, type MoneyValue, SplitInfo, SplitRule, TaxCalculation, TaxConfig, TaxType, absMoney, addMoney, appendAuditEvent, calculateCommission, calculateOrganizationPayout, calculateSplits, calculateTax, compareMoney, equalsMoney, fromMajor, fromSmallestUnit, getAuditTrail, getLastStateChange, getTaxType, isCurrencyCode, isMoney, isNegativeMoney, isPositiveMoney, isZeroMoney, minorUnitFactor, money, multiplyMoney, negateMoney, reverseCommission, reverseSplits, reverseTax, subtractMoney, sumMoney, toCurrencyCode, toMajor, toSmallestUnit, validateTaxCalculation };
@@ -1,4 +1,4 @@
1
- import { a as reverseTax, c as reverseCommission, i as getTaxType, n as calculateSplits, o as validateTaxCalculation, r as calculateTax, s as calculateCommission, t as calculateOrganizationPayout } from "../splits-CNfQj92L.mjs";
1
+ import { a as getTaxType, c as calculateCommission, i as calculateTax, l as reverseCommission, n as calculateSplits, o as reverseTax, r as reverseSplits, s as validateTaxCalculation, t as calculateOrganizationPayout } from "../splits-CEdZN6OT.mjs";
2
2
  import { C as sumMoney, E as toSmallestUnit, S as subtractMoney, T as toMajor, _ as isZeroMoney, a as CurrencyMismatchError, b as multiplyMoney, c as addMoney, d as fromMajor, f as fromSmallestUnit, g as isPositiveMoney, h as isNegativeMoney, i as CURRENCIES, l as compareMoney, m as isMoney, n as getAuditTrail, o as MINOR_UNIT_FACTOR, p as isCurrencyCode, r as getLastStateChange, s as absMoney, t as appendAuditEvent, u as equalsMoney, v as minorUnitFactor, w as toCurrencyCode, x as negateMoney, y as money } from "../audit-Ba2XB2C4.mjs";
3
3
 
4
- export { CURRENCIES, CurrencyMismatchError, MINOR_UNIT_FACTOR, absMoney, addMoney, appendAuditEvent, calculateCommission, calculateOrganizationPayout, calculateSplits, calculateTax, compareMoney, equalsMoney, fromMajor, fromSmallestUnit, getAuditTrail, getLastStateChange, getTaxType, isCurrencyCode, isMoney, isNegativeMoney, isPositiveMoney, isZeroMoney, minorUnitFactor, money, multiplyMoney, negateMoney, reverseCommission, reverseTax, subtractMoney, sumMoney, toCurrencyCode, toMajor, toSmallestUnit, validateTaxCalculation };
4
+ export { CURRENCIES, CurrencyMismatchError, MINOR_UNIT_FACTOR, absMoney, addMoney, appendAuditEvent, calculateCommission, calculateOrganizationPayout, calculateSplits, calculateTax, compareMoney, equalsMoney, fromMajor, fromSmallestUnit, getAuditTrail, getLastStateChange, getTaxType, isCurrencyCode, isMoney, isNegativeMoney, isPositiveMoney, isZeroMoney, minorUnitFactor, money, multiplyMoney, negateMoney, reverseCommission, reverseSplits, reverseTax, subtractMoney, sumMoney, toCurrencyCode, toMajor, toSmallestUnit, validateTaxCalculation };
@@ -1,3 +1,5 @@
1
+ import { g as ValidationError } from "./errors-Bt5NRVMq.mjs";
2
+
1
3
  //#region src/shared/calculators/commission.ts
2
4
  function calculateCommission(amount, commissionRate, gatewayFeeRate = 0) {
3
5
  if (!commissionRate || commissionRate <= 0) return null;
@@ -17,9 +19,9 @@ function calculateCommission(amount, commissionRate, gatewayFeeRate = 0) {
17
19
  }
18
20
  function reverseCommission(originalCommission, originalAmount, refundAmount) {
19
21
  if (!originalCommission?.netAmount) return null;
20
- if (!originalAmount || originalAmount <= 0) throw new Error("Original amount must be greater than 0");
21
- if (refundAmount < 0) throw new Error("Refund amount cannot be negative");
22
- if (refundAmount > originalAmount) throw new Error("Refund amount exceeds original amount");
22
+ if (!originalAmount || originalAmount <= 0) throw new ValidationError("Original amount must be greater than 0");
23
+ if (refundAmount < 0) throw new ValidationError("Refund amount cannot be negative");
24
+ if (refundAmount > originalAmount) throw new ValidationError("Refund amount exceeds original amount");
23
25
  const refundRatio = refundAmount / originalAmount;
24
26
  return {
25
27
  rate: originalCommission.rate,
@@ -74,9 +76,9 @@ function reverseTax(originalTax, originalAmount, refundAmount) {
74
76
  totalAmount: refundAmount,
75
77
  pricesIncludeTax: false
76
78
  };
77
- if (!originalAmount || originalAmount <= 0) throw new Error("Original amount must be greater than 0");
78
- if (refundAmount < 0) throw new Error("Refund amount cannot be negative");
79
- if (refundAmount > originalAmount) throw new Error("Refund amount exceeds original amount");
79
+ if (!originalAmount || originalAmount <= 0) throw new ValidationError("Original amount must be greater than 0");
80
+ if (refundAmount < 0) throw new ValidationError("Refund amount cannot be negative");
81
+ if (refundAmount > originalAmount) throw new ValidationError("Refund amount exceeds original amount");
80
82
  const refundRatio = refundAmount / originalAmount;
81
83
  const reversedType = originalTax.type ? originalTax.type === "collected" ? "paid" : originalTax.type === "paid" ? "collected" : "exempt" : void 0;
82
84
  return {
@@ -118,6 +120,19 @@ function calculateSplits(amount, rules, gatewayFeeRate = 0) {
118
120
  function calculateOrganizationPayout(amount, splits) {
119
121
  return amount - splits.reduce((sum, s) => sum + s.grossAmount, 0);
120
122
  }
123
+ function reverseSplits(originalSplits, originalAmount, refundAmount) {
124
+ if (!originalAmount || originalAmount <= 0) throw new ValidationError("Original amount must be greater than 0");
125
+ if (refundAmount < 0) throw new ValidationError("Refund amount cannot be negative");
126
+ if (refundAmount > originalAmount) throw new ValidationError("Refund amount exceeds original amount");
127
+ const refundRatio = refundAmount / originalAmount;
128
+ return originalSplits.map((s) => ({
129
+ ...s,
130
+ grossAmount: Math.round(s.grossAmount * refundRatio),
131
+ gatewayFeeAmount: Math.round(s.gatewayFeeAmount * refundRatio),
132
+ netAmount: Math.round(s.netAmount * refundRatio),
133
+ status: "waived"
134
+ }));
135
+ }
121
136
 
122
137
  //#endregion
123
- export { reverseTax as a, reverseCommission as c, getTaxType as i, calculateSplits as n, validateTaxCalculation as o, calculateTax as r, calculateCommission as s, calculateOrganizationPayout as t };
138
+ export { getTaxType as a, calculateCommission as c, calculateTax as i, reverseCommission as l, calculateSplits as n, reverseTax as o, reverseSplits as r, validateTaxCalculation as s, calculateOrganizationPayout as t };
@@ -1,4 +1,4 @@
1
- import { A as transactionBaseSchema, C as subscriptionBaseSchema, D as TransactionCreateInput, E as subscriptionUpdateSchema, M as transactionListFilterSchema, N as transactionUpdateSchema, O as TransactionListFilter, S as SubscriptionUpdateInput, T as subscriptionListFilterSchema, _ as settlementCreateSchema, a as escrowReleaseSchema, b as SubscriptionCreateInput, c as PaymentVerifyInput, d as paymentVerifySchema, f as refundSchema, g as settlementBaseSchema, h as SettlementUpdateInput, i as escrowHoldSchema, j as transactionCreateSchema, k as TransactionUpdateInput, l as RefundInput, m as SettlementListFilter, n as EscrowReleaseInput, o as splitRuleSchema, p as SettlementCreateInput, r as SplitRuleInput, s as PaymentIntentInput, t as EscrowHoldInput, u as paymentIntentSchema, v as settlementListFilterSchema, w as subscriptionCreateSchema, x as SubscriptionListFilter, y as settlementUpdateSchema } from "../escrow.schema-o4qhjOca.mjs";
1
+ import { A as transactionBaseSchema, C as subscriptionBaseSchema, D as TransactionCreateInput, E as subscriptionUpdateSchema, M as transactionListFilterSchema, N as transactionUpdateSchema, O as TransactionListFilter, S as SubscriptionUpdateInput, T as subscriptionListFilterSchema, _ as settlementCreateSchema, a as escrowReleaseSchema, b as SubscriptionCreateInput, c as PaymentVerifyInput, d as paymentVerifySchema, f as refundSchema, g as settlementBaseSchema, h as SettlementUpdateInput, i as escrowHoldSchema, j as transactionCreateSchema, k as TransactionUpdateInput, l as RefundInput, m as SettlementListFilter, n as EscrowReleaseInput, o as splitRuleSchema, p as SettlementCreateInput, r as SplitRuleInput, s as PaymentIntentInput, t as EscrowHoldInput, u as paymentIntentSchema, v as settlementListFilterSchema, w as subscriptionCreateSchema, x as SubscriptionListFilter, y as settlementUpdateSchema } from "../escrow.schema-DSx7EywJ.mjs";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region src/validators/bank-feed.schema.d.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/revenue",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "Payment lifecycle engine — transactions, subscriptions, escrow, settlements, commissions. MongoKit-powered, Arc-compatible, framework-agnostic.",
5
5
  "type": "module",
6
6
  "sideEffects": false,