@managemint-solutions/sdk 0.40.1 → 0.41.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.
@@ -19,6 +19,8 @@ export declare class BillingReadsResource {
19
19
  openTransaction(openStatuses: string[]): Promise<BillingTransactionRow | null>;
20
20
  /** The replay lookup: the same key must never raise a second charge. */
21
21
  transactionByIdempotencyKey(idempotencyKey: string): Promise<BillingTransactionRow | null>;
22
+ /** The organization's newest renewal in this status, or null. */
23
+ latestRenewalIn(transactionStatus: string): Promise<BillingTransactionRow | null>;
22
24
  /**
23
25
  * The organization's payments, newest first: every ledger row, whatever its type or
24
26
  * status. RLS scopes the rows; the eq is belt-and-braces.
@@ -26,7 +26,7 @@ const BILLING_TRANSACTIONS_TABLE = {
26
26
  entityType: enum_1.AuditTrailEntityType.BILLING_TRANSACTIONS,
27
27
  };
28
28
  /** The wire shape of a payment list row - never the internal bigint id, never the checkout secrets. */
29
- const PAYMENT_SUMMARY_SELECT = 'mms_id, created_at, transaction_type, transaction_status, amount_minor, currency, paid_at, failure_reason';
29
+ const PAYMENT_SUMMARY_SELECT = 'mms_id, created_at, transaction_type, transaction_status, amount_minor, currency, paid_at, failure_reason, refunded_at, refund_amount_minor';
30
30
  /**
31
31
  * PaymentEntity, column for column, plus the renewal's invoice through the unique
32
32
  * `invoices.billing_transaction_id` foreign key. The Paystack access code and
@@ -70,6 +70,21 @@ class BillingReadsResource {
70
70
  throw (0, errors_1.mapPostgrestError)(error);
71
71
  return data ? (0, audit_1.withoutInternalId)(data) : null;
72
72
  }
73
+ /** The organization's newest renewal in this status, or null. */
74
+ async latestRenewalIn(transactionStatus) {
75
+ const { data, error } = await this.supabase
76
+ .from('billing_transactions')
77
+ .select('*')
78
+ .eq('organization_id', this.actor.organization_id)
79
+ .eq('transaction_type', enum_2.BillingTransactionType.RENEWAL)
80
+ .eq('transaction_status', transactionStatus)
81
+ .order('created_at', { ascending: false })
82
+ .limit(1)
83
+ .maybeSingle();
84
+ if (error)
85
+ throw (0, errors_1.mapPostgrestError)(error);
86
+ return data ? (0, audit_1.withoutInternalId)(data) : null;
87
+ }
73
88
  /**
74
89
  * The organization's payments, newest first: every ledger row, whatever its type or
75
90
  * status. RLS scopes the rows; the eq is belt-and-braces.
@@ -21,14 +21,26 @@ export declare class InvoicesReadsResource {
21
21
  /**
22
22
  * The document writes, on the service client only - every write policy on `invoices` is
23
23
  * `false` for `authenticated`. Rows are written by the api's billing service beside a
24
- * renewal's ledger write - a renewal is inserted already paid, so an invoice never moves
25
- * status after it is issued - never by a caller.
24
+ * renewal's ledger row, never by a caller; an invoice mirrors its ledger row's status.
26
25
  */
27
26
  export declare class InvoicesResource extends InvoicesReadsResource {
28
27
  private readonly invoices;
29
28
  constructor(supabase: TypedSupabaseClient, actor: AuditActor, audit: AuditResource);
30
29
  /** Audited insert of the accounting document beside its ledger row. */
31
30
  create(payload: AuditRow): Promise<AuditRow>;
31
+ /** Audited status change: the row follows its ledger row (paid, refunded). */
32
+ update(invoiceId: string, patch: AuditRow): Promise<AuditRow>;
33
+ /**
34
+ * The invoice Paystack named with this INV_… code, or null. The invoice events
35
+ * (create, payment_failed, update) all carry the code, so it is how a later event finds
36
+ * the row an earlier one opened.
37
+ */
38
+ byPaystackInvoiceCode(invoiceCode: string): Promise<InvoiceEntity | null>;
39
+ /**
40
+ * The invoice behind a ledger row, or null. `billing_transaction_id` is unique, so a
41
+ * renewal has at most one; every other transaction type has none.
42
+ */
43
+ byTransactionId(transactionId: string): Promise<InvoiceEntity | null>;
32
44
  /**
33
45
  * The organization's highest invoice number, 0 when it has none. The api assigns
34
46
  * `max + 1` and lets the `(organization_id, invoice_number)` unique index arbitrate a
@@ -27,7 +27,7 @@ const INVOICES_TABLE = {
27
27
  /** The wire shape of an invoice list row - never the internal bigint id. */
28
28
  const INVOICE_SUMMARY_SELECT = 'mms_id, invoice_number, transaction_type, status, issued_at, paid_at, amount_minor, currency';
29
29
  /** InvoiceEntity, column for column: the summary plus the frozen document data. */
30
- const INVOICE_ENTITY_SELECT = `${INVOICE_SUMMARY_SELECT}, billing_transaction_id, paystack_reference, period_start, period_end, line_items, billed_to`;
30
+ const INVOICE_ENTITY_SELECT = `${INVOICE_SUMMARY_SELECT}, billing_transaction_id, paystack_reference, paystack_invoice_code, period_start, period_end, line_items, billed_to`;
31
31
  /**
32
32
  * The readable half. `invoices` is SELECT-only for `authenticated` with an org-predicate
33
33
  * policy - the same posture as the ledger it mirrors - so reads run on the caller's own
@@ -78,8 +78,7 @@ exports.InvoicesReadsResource = InvoicesReadsResource;
78
78
  /**
79
79
  * The document writes, on the service client only - every write policy on `invoices` is
80
80
  * `false` for `authenticated`. Rows are written by the api's billing service beside a
81
- * renewal's ledger write - a renewal is inserted already paid, so an invoice never moves
82
- * status after it is issued - never by a caller.
81
+ * renewal's ledger row, never by a caller; an invoice mirrors its ledger row's status.
83
82
  */
84
83
  class InvoicesResource extends InvoicesReadsResource {
85
84
  invoices;
@@ -91,6 +90,44 @@ class InvoicesResource extends InvoicesReadsResource {
91
90
  async create(payload) {
92
91
  return this.invoices.insert(payload);
93
92
  }
93
+ /** Audited status change: the row follows its ledger row (paid, refunded). */
94
+ async update(invoiceId, patch) {
95
+ return this.invoices.update(invoiceId, {
96
+ ...patch,
97
+ updated_at: new Date().toISOString(),
98
+ });
99
+ }
100
+ /**
101
+ * The invoice Paystack named with this INV_… code, or null. The invoice events
102
+ * (create, payment_failed, update) all carry the code, so it is how a later event finds
103
+ * the row an earlier one opened.
104
+ */
105
+ async byPaystackInvoiceCode(invoiceCode) {
106
+ const { data, error } = await this.supabase
107
+ .from('invoices')
108
+ .select(INVOICE_ENTITY_SELECT)
109
+ .eq('paystack_invoice_code', invoiceCode)
110
+ .eq('organization_id', this.actor.organization_id)
111
+ .maybeSingle();
112
+ if (error)
113
+ throw (0, errors_1.mapPostgrestError)(error);
114
+ return data ?? null;
115
+ }
116
+ /**
117
+ * The invoice behind a ledger row, or null. `billing_transaction_id` is unique, so a
118
+ * renewal has at most one; every other transaction type has none.
119
+ */
120
+ async byTransactionId(transactionId) {
121
+ const { data, error } = await this.supabase
122
+ .from('invoices')
123
+ .select(INVOICE_ENTITY_SELECT)
124
+ .eq('billing_transaction_id', transactionId)
125
+ .eq('organization_id', this.actor.organization_id)
126
+ .maybeSingle();
127
+ if (error)
128
+ throw (0, errors_1.mapPostgrestError)(error);
129
+ return data ?? null;
130
+ }
94
131
  /**
95
132
  * The organization's highest invoice number, 0 when it has none. The api assigns
96
133
  * `max + 1` and lets the `(organization_id, invoice_number)` unique index arbitrate a
@@ -103,5 +103,12 @@ export declare class SupabaseServiceClient {
103
103
  * transaction id in its metadata, and the row is what says which tenant it belongs to.
104
104
  */
105
105
  findTransaction(transactionId: string): Promise<Record<string, unknown> | null>;
106
+ /**
107
+ * A ledger row by its Paystack reference. Unscoped for the same reason as
108
+ * `findTransaction`: a refund event names only `transaction_reference`, and the row is
109
+ * what says which tenant it belongs to. The partial unique index on
110
+ * `paystack_reference` makes at most one row match.
111
+ */
112
+ findTransactionByReference(reference: string): Promise<Record<string, unknown> | null>;
106
113
  }
107
114
  export declare const createSupabaseServiceClient: (config: SupabaseServiceClientConfig) => SupabaseServiceClient;
@@ -182,6 +182,22 @@ class SupabaseServiceClient {
182
182
  throw (0, errors_1.mapPostgrestError)(error);
183
183
  return data ? (0, audit_1.withoutInternalId)(data) : null;
184
184
  }
185
+ /**
186
+ * A ledger row by its Paystack reference. Unscoped for the same reason as
187
+ * `findTransaction`: a refund event names only `transaction_reference`, and the row is
188
+ * what says which tenant it belongs to. The partial unique index on
189
+ * `paystack_reference` makes at most one row match.
190
+ */
191
+ async findTransactionByReference(reference) {
192
+ const { data, error } = await this.supabase
193
+ .from('billing_transactions')
194
+ .select('*')
195
+ .eq('paystack_reference', reference)
196
+ .maybeSingle();
197
+ if (error)
198
+ throw (0, errors_1.mapPostgrestError)(error);
199
+ return data ? (0, audit_1.withoutInternalId)(data) : null;
200
+ }
185
201
  }
186
202
  exports.SupabaseServiceClient = SupabaseServiceClient;
187
203
  const createSupabaseServiceClient = (config) => new SupabaseServiceClient(config);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@managemint-solutions/sdk",
3
- "version": "0.40.1",
3
+ "version": "0.41.0",
4
4
  "description": "Typed Supabase data-access SDK for ManageMint Solutions",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Scott Bebington <scottbebington@gmail.com>",
@@ -51,7 +51,7 @@
51
51
  "testEnvironment": "node"
52
52
  },
53
53
  "dependencies": {
54
- "@managemint-solutions/entities": "^1.23.1"
54
+ "@managemint-solutions/entities": "^1.24.0"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "@nestjs/common": "^11.0.0",