@develit-services/ledger 0.3.6 → 0.4.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.
@@ -1,9 +1,9 @@
1
1
  import { first, develitWorker, uuidv4, useResult, createInternalError, action, service } from '@develit-io/backend-sdk';
2
2
  import { WorkerEntrypoint } from 'cloudflare:workers';
3
3
  import { drizzle } from 'drizzle-orm/d1';
4
- import { s as schema } from '../shared/ledger.CGMCq9Gr.mjs';
4
+ import { s as schema } from '../shared/ledger.Dpxzz64a.mjs';
5
5
  import { eq, and, inArray, or, count, gte, lte, sql, asc, desc } from 'drizzle-orm';
6
- import { k as createTransactionInputSchema, x as matchTransactionInputSchema, m as failTransactionInputSchema, i as cancelTransactionInputSchema, y as refundTransactionInputSchema, g as REFUNDABLE_TRANSACTION_STATUSES, s as getTransactionByIdInputSchema, t as getTransactionsByReferenceIdInputSchema, u as getTransactionsInputSchema, j as createAccountInputSchema, z as updateAccountInputSchema, l as deleteAccountInputSchema, q as getAccountInputSchema, r as getAccountsByOwnerInputSchema, w as listAccountsInputSchema, p as getAccountIdentifierInputSchema, v as listAccountIdentifiersInputSchema, n as findAccountByIdentifierInputSchema, o as getAccountBalanceInputSchema, D as updateTransactionConfirmationSentAtInputSchema, F as updateTransactionStatusInputSchema } from '../shared/ledger.DvlQNN83.mjs';
6
+ import { k as createTransactionInputSchema, x as matchTransactionInputSchema, z as resolveUnmatchedTransactionInputSchema, m as failTransactionInputSchema, i as cancelTransactionInputSchema, y as refundTransactionInputSchema, g as REFUNDABLE_TRANSACTION_STATUSES, s as getTransactionByIdInputSchema, t as getTransactionsByReferenceIdInputSchema, u as getTransactionsInputSchema, j as createAccountInputSchema, D as updateAccountInputSchema, l as deleteAccountInputSchema, q as getAccountInputSchema, r as getAccountsByOwnerInputSchema, w as listAccountsInputSchema, p as getAccountIdentifierInputSchema, v as listAccountIdentifiersInputSchema, n as findAccountByIdentifierInputSchema, o as getAccountBalanceInputSchema, F as updateTransactionConfirmationSentAtInputSchema, G as updateTransactionStatusInputSchema } from '../shared/ledger.De19PrJa.mjs';
7
7
  import '@develit-io/general-codes';
8
8
  import 'drizzle-orm/sqlite-core';
9
9
  import 'zod';
@@ -26,6 +26,11 @@ const deleteAccountCommand = async (db, id) => {
26
26
  return { accountIdentifierMappingCommand, accountCommand };
27
27
  };
28
28
 
29
+ const deleteTransactionCommand = (db, transactionId) => {
30
+ const command = db.delete(tables.transaction).where(eq(tables.transaction.id, transactionId));
31
+ return { command };
32
+ };
33
+
29
34
  const linkAccountIdentifierCommand = async (db, accountId, identifierId) => {
30
35
  const command = db.insert(tables.accountIdentifierMapping).values({ accountId, identifierId }).returning();
31
36
  return { command };
@@ -37,13 +42,23 @@ const updateAccountCommand = async (db, id, input) => {
37
42
  };
38
43
 
39
44
  const updateTransactionStatusCommand = (db, data) => {
40
- const { transactionId, status, paymentId, completedAt, statusReason } = data;
45
+ const {
46
+ transactionId,
47
+ status,
48
+ paymentId,
49
+ completedAt,
50
+ statusReason,
51
+ receivedValue,
52
+ metadata
53
+ } = data;
41
54
  const command = db.update(tables.transaction).set({
42
55
  status,
43
56
  statusReason,
44
57
  updatedAt: /* @__PURE__ */ new Date(),
45
58
  completedAt,
46
- paymentId
59
+ paymentId,
60
+ ...receivedValue !== void 0 && { receivedValue },
61
+ ...metadata !== void 0 && { metadata }
47
62
  }).where(eq(tables.transaction.id, transactionId)).returning();
48
63
  return { command };
49
64
  };
@@ -233,6 +248,7 @@ const getTransactionsWithPaginationQuery = async ({
233
248
  filterTransactionDateTo,
234
249
  filterTransactionCompletedAt,
235
250
  filterTransactionStatus,
251
+ filterTransactionStatuses,
236
252
  search
237
253
  }
238
254
  }) => {
@@ -253,7 +269,8 @@ const getTransactionsWithPaginationQuery = async ({
253
269
  filterTransactionDateFrom ? gte(tables.transaction.createdAt, filterTransactionDateFrom) : void 0,
254
270
  filterTransactionDateTo ? lte(tables.transaction.createdAt, filterTransactionDateTo) : void 0,
255
271
  filterTransactionCompletedAt ? eq(tables.transaction.completedAt, filterTransactionCompletedAt) : void 0,
256
- filterTransactionStatus ? eq(tables.transaction.status, filterTransactionStatus) : void 0
272
+ filterTransactionStatus ? eq(tables.transaction.status, filterTransactionStatus) : void 0,
273
+ filterTransactionStatuses?.length ? inArray(tables.transaction.status, filterTransactionStatuses) : void 0
257
274
  );
258
275
  const sortColumn = resolveColumn(tables.transaction, sort.column);
259
276
  const [{ totalCount }] = await db.select({ totalCount: sql`count(*)` }).from(tables.transaction).where(whereConditions);
@@ -391,12 +408,75 @@ let LedgerServiceBase = class extends develitWorker(
391
408
  }
392
409
  );
393
410
  }
411
+ /**
412
+ * Internal method that applies match amount logic to a transaction.
413
+ * Shared by matchTransaction and resolveUnmatchedTransaction.
414
+ */
415
+ async _applyMatchAmount({
416
+ existingTransaction,
417
+ paymentId,
418
+ amount,
419
+ completedAt,
420
+ extraMetadata
421
+ }) {
422
+ const transactionId = existingTransaction.id;
423
+ if (amount !== void 0) {
424
+ const previousReceived = existingTransaction.receivedValue ?? 0;
425
+ const newReceivedValue = previousReceived + amount;
426
+ const expectedValue = existingTransaction.value ?? 0;
427
+ const isFullyMatched = newReceivedValue >= expectedValue;
428
+ const matchEntry = {
429
+ paymentId,
430
+ amount,
431
+ matchedAt: (/* @__PURE__ */ new Date()).toISOString()
432
+ };
433
+ const existingMatches = existingTransaction.metadata?.partialMatches ?? [];
434
+ const updatedMetadata = {
435
+ ...existingTransaction.metadata,
436
+ ...extraMetadata,
437
+ partialMatches: [...existingMatches, matchEntry]
438
+ };
439
+ const transaction2 = await updateTransactionStatusCommand(this.db, {
440
+ transactionId,
441
+ status: isFullyMatched ? "MATCHED" : "PARTIALLY_MATCHED",
442
+ completedAt: isFullyMatched ? completedAt : void 0,
443
+ paymentId,
444
+ receivedValue: newReceivedValue,
445
+ metadata: updatedMetadata
446
+ }).command.execute().then(first);
447
+ if (!transaction2) {
448
+ throw createInternalError(null, {
449
+ message: "Failed to update transaction",
450
+ status: 500,
451
+ code: "INTERNAL_ERROR"
452
+ });
453
+ }
454
+ return {
455
+ transaction: transaction2,
456
+ signal: isFullyMatched ? "matched" : "partiallyMatched"
457
+ };
458
+ }
459
+ const transaction = await updateTransactionStatusCommand(this.db, {
460
+ transactionId,
461
+ status: "MATCHED",
462
+ completedAt,
463
+ paymentId
464
+ }).command.execute().then(first);
465
+ if (!transaction) {
466
+ throw createInternalError(null, {
467
+ message: "Failed to update transaction",
468
+ status: 500,
469
+ code: "INTERNAL_ERROR"
470
+ });
471
+ }
472
+ return { transaction, signal: "matched" };
473
+ }
394
474
  async matchTransaction(input) {
395
475
  return this.handleAction(
396
476
  { data: input, schema: matchTransactionInputSchema },
397
477
  { successMessage: "Transaction successfully matched." },
398
478
  async (params) => {
399
- const { transactionId, completedAt, paymentId } = params;
479
+ const { transactionId, completedAt, paymentId, amount } = params;
400
480
  const existingTransaction = await getTransactionByIdQuery(
401
481
  this.db,
402
482
  transactionId
@@ -408,24 +488,112 @@ let LedgerServiceBase = class extends develitWorker(
408
488
  code: "NOT_FOUND"
409
489
  });
410
490
  }
411
- const transaction = await updateTransactionStatusCommand(this.db, {
412
- transactionId,
413
- status: "MATCHED",
414
- completedAt,
415
- paymentId
416
- }).command.execute().then(first);
417
- if (!transaction) {
491
+ if (existingTransaction.status !== "WAITING_FOR_PAYMENT" && existingTransaction.status !== "PARTIALLY_MATCHED") {
418
492
  throw createInternalError(null, {
419
- message: "Failed to update transaction",
420
- status: 500,
421
- code: "INTERNAL_ERROR"
493
+ message: `Transaction is in status ${existingTransaction.status}, expected WAITING_FOR_PAYMENT or PARTIALLY_MATCHED`,
494
+ status: 400,
495
+ code: "INVALID_STATUS"
496
+ });
497
+ }
498
+ const { transaction, signal } = await this._applyMatchAmount({
499
+ existingTransaction,
500
+ paymentId,
501
+ amount,
502
+ completedAt
503
+ });
504
+ await this.pushToQueue(
505
+ this.env.QUEUE_BUS_QUEUE,
506
+ {
507
+ eventType: "LEDGER_TRANSACTION",
508
+ eventSignal: signal,
509
+ ledgerTransaction: transaction,
510
+ metadata: {
511
+ correlationId: uuidv4(),
512
+ entityId: transaction.id,
513
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
514
+ }
515
+ }
516
+ );
517
+ return transaction;
518
+ }
519
+ );
520
+ }
521
+ async resolveUnmatchedTransaction(input) {
522
+ return this.handleAction(
523
+ { data: input, schema: resolveUnmatchedTransactionInputSchema },
524
+ { successMessage: "Unmatched transaction successfully resolved." },
525
+ async (params) => {
526
+ const {
527
+ unmatchedTransactionId,
528
+ targetTransactionId,
529
+ note,
530
+ performedBy
531
+ } = params;
532
+ const unmatchedTx = await getTransactionByIdQuery(
533
+ this.db,
534
+ unmatchedTransactionId
535
+ );
536
+ if (!unmatchedTx) {
537
+ throw createInternalError(null, {
538
+ message: "Unmatched transaction not found",
539
+ status: 404,
540
+ code: "NOT_FOUND"
541
+ });
542
+ }
543
+ if (unmatchedTx.type !== "UNMATCHED") {
544
+ throw createInternalError(null, {
545
+ message: `Transaction type is ${unmatchedTx.type}, expected UNMATCHED`,
546
+ status: 400,
547
+ code: "INVALID_TYPE"
548
+ });
549
+ }
550
+ if (unmatchedTx.status !== "WAITING_FOR_MANUAL_PROCESSING") {
551
+ throw createInternalError(null, {
552
+ message: `Unmatched transaction is in status ${unmatchedTx.status}, expected WAITING_FOR_MANUAL_PROCESSING`,
553
+ status: 400,
554
+ code: "INVALID_STATUS"
555
+ });
556
+ }
557
+ const targetTx = await getTransactionByIdQuery(
558
+ this.db,
559
+ targetTransactionId
560
+ );
561
+ if (!targetTx) {
562
+ throw createInternalError(null, {
563
+ message: "Target transaction not found",
564
+ status: 404,
565
+ code: "NOT_FOUND"
566
+ });
567
+ }
568
+ if (targetTx.status !== "WAITING_FOR_PAYMENT" && targetTx.status !== "PARTIALLY_MATCHED") {
569
+ throw createInternalError(null, {
570
+ message: `Target transaction is in status ${targetTx.status}, expected WAITING_FOR_PAYMENT or PARTIALLY_MATCHED`,
571
+ status: 400,
572
+ code: "INVALID_STATUS"
422
573
  });
423
574
  }
575
+ const amount = unmatchedTx.value ?? 0;
576
+ const paymentId = unmatchedTx.paymentId ?? unmatchedTx.id;
577
+ const { transaction, signal } = await this._applyMatchAmount({
578
+ existingTransaction: targetTx,
579
+ paymentId,
580
+ amount,
581
+ completedAt: /* @__PURE__ */ new Date(),
582
+ extraMetadata: {
583
+ note,
584
+ performedBy
585
+ }
586
+ });
587
+ const { command } = deleteTransactionCommand(
588
+ this.db,
589
+ unmatchedTransactionId
590
+ );
591
+ await command.execute();
424
592
  await this.pushToQueue(
425
593
  this.env.QUEUE_BUS_QUEUE,
426
594
  {
427
595
  eventType: "LEDGER_TRANSACTION",
428
- eventSignal: "matched",
596
+ eventSignal: signal,
429
597
  ledgerTransaction: transaction,
430
598
  metadata: {
431
599
  correlationId: uuidv4(),
@@ -539,7 +707,7 @@ let LedgerServiceBase = class extends develitWorker(
539
707
  { data: input, schema: refundTransactionInputSchema },
540
708
  { successMessage: "Transaction successfully refunded." },
541
709
  async (params) => {
542
- const { transactionId, amount, reason } = params;
710
+ const { transactionId, amount, reason, performedBy } = params;
543
711
  const originalTx = await getTransactionByIdQuery(this.db, transactionId);
544
712
  if (!originalTx) {
545
713
  throw createInternalError(null, {
@@ -607,6 +775,7 @@ let LedgerServiceBase = class extends develitWorker(
607
775
  metadata: {
608
776
  tags: ["refund", `refund-${originalTx.type.toLowerCase()}`],
609
777
  note: reason,
778
+ performedBy,
610
779
  payment: {
611
780
  amount: refundAmount,
612
781
  currency: originalPayment.currency,
@@ -990,6 +1159,9 @@ __decorateClass([
990
1159
  __decorateClass([
991
1160
  action("match-transaction")
992
1161
  ], LedgerServiceBase.prototype, "matchTransaction", 1);
1162
+ __decorateClass([
1163
+ action("resolve-unmatched-transaction")
1164
+ ], LedgerServiceBase.prototype, "resolveUnmatchedTransaction", 1);
993
1165
  __decorateClass([
994
1166
  action("fail-transaction")
995
1167
  ], LedgerServiceBase.prototype, "failTransaction", 1);
@@ -704,7 +704,7 @@ declare const IDENTIFIER_KINDS: readonly ["IBAN", "LOCAL_CZ", "SWIFT", "CRYPTO_A
704
704
  type IdentifierKind = (typeof IDENTIFIER_KINDS)[number];
705
705
  declare const ENTRY_STATUSES: readonly ["PENDING", "REALIZED", "FAILED", "CANCELED"];
706
706
  type EntryStatus = (typeof ENTRY_STATUSES)[number];
707
- declare const TRANSACTION_STATUSES: readonly ["WAITING_FOR_PAYMENT", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
707
+ declare const TRANSACTION_STATUSES: readonly ["WAITING_FOR_PAYMENT", "PARTIALLY_MATCHED", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
708
708
  type TransactionStatus = (typeof TRANSACTION_STATUSES)[number];
709
709
  declare const TRANSACTION_TYPES: readonly ["CLIENT_FUND_IN", "CLIENT_FUND_OUT", "PROVIDER_FUND_IN", "PROVIDER_FUND_OUT", "COLLATERAL_FUND_IN", "COLLATERAL_FUND_OUT", "EXCHANGE", "UNMATCHED", "ADJUSTMENT", "TRANSFER", "REFUND"];
710
710
  type TransactionType = (typeof TRANSACTION_TYPES)[number];
@@ -773,6 +773,18 @@ interface TransactionMetadata {
773
773
  refund?: {
774
774
  isPartialRefund: boolean;
775
775
  };
776
+ /**
777
+ * History of partial matches — each incoming payment that contributed to this transaction
778
+ */
779
+ partialMatches?: Array<{
780
+ paymentId: string;
781
+ amount: number;
782
+ matchedAt: string;
783
+ }>;
784
+ /**
785
+ * ID of the user who performed a manual action (resolve, refund, etc.)
786
+ */
787
+ performedBy?: string;
776
788
  }
777
789
  interface EntryMetadata {
778
790
  context?: {
@@ -1181,7 +1193,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1181
1193
  tableName: "transaction";
1182
1194
  dataType: "string";
1183
1195
  columnType: "SQLiteText";
1184
- data: "PAYMENT" | "EXCHANGE" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1196
+ data: "EXCHANGE" | "PAYMENT" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1185
1197
  driverParam: string;
1186
1198
  notNull: true;
1187
1199
  hasDefault: false;
@@ -1194,7 +1206,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1194
1206
  generated: undefined;
1195
1207
  }, {}, {
1196
1208
  length: number | undefined;
1197
- $type: "PAYMENT" | "EXCHANGE" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1209
+ $type: "EXCHANGE" | "PAYMENT" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1198
1210
  }>;
1199
1211
  referenceId: drizzle_orm_sqlite_core.SQLiteColumn<{
1200
1212
  name: "reference_id";
@@ -1220,7 +1232,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1220
1232
  tableName: "transaction";
1221
1233
  dataType: "string";
1222
1234
  columnType: "SQLiteText";
1223
- data: "EXCHANGE" | "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1235
+ data: "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "EXCHANGE" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1224
1236
  driverParam: string;
1225
1237
  notNull: true;
1226
1238
  hasDefault: false;
@@ -1233,7 +1245,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1233
1245
  generated: undefined;
1234
1246
  }, {}, {
1235
1247
  length: number | undefined;
1236
- $type: "EXCHANGE" | "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1248
+ $type: "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "EXCHANGE" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1237
1249
  }>;
1238
1250
  description: drizzle_orm_sqlite_core.SQLiteColumn<{
1239
1251
  name: "description";
@@ -1293,20 +1305,20 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1293
1305
  tableName: "transaction";
1294
1306
  dataType: "string";
1295
1307
  columnType: "SQLiteText";
1296
- data: "FAILED" | "WAITING_FOR_PAYMENT" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1308
+ data: "FAILED" | "WAITING_FOR_PAYMENT" | "PARTIALLY_MATCHED" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1297
1309
  driverParam: string;
1298
1310
  notNull: true;
1299
1311
  hasDefault: false;
1300
1312
  isPrimaryKey: false;
1301
1313
  isAutoincrement: false;
1302
1314
  hasRuntimeDefault: false;
1303
- enumValues: ["WAITING_FOR_PAYMENT", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
1315
+ enumValues: ["WAITING_FOR_PAYMENT", "PARTIALLY_MATCHED", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
1304
1316
  baseColumn: never;
1305
1317
  identity: undefined;
1306
1318
  generated: undefined;
1307
1319
  }, {}, {
1308
1320
  length: number | undefined;
1309
- $type: "FAILED" | "WAITING_FOR_PAYMENT" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1321
+ $type: "FAILED" | "WAITING_FOR_PAYMENT" | "PARTIALLY_MATCHED" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1310
1322
  }>;
1311
1323
  statusReason: drizzle_orm_sqlite_core.SQLiteColumn<{
1312
1324
  name: "status_reason";
@@ -1382,6 +1394,23 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1382
1394
  identity: undefined;
1383
1395
  generated: undefined;
1384
1396
  }, {}, {}>;
1397
+ receivedValue: drizzle_orm_sqlite_core.SQLiteColumn<{
1398
+ name: "received_value";
1399
+ tableName: "transaction";
1400
+ dataType: "number";
1401
+ columnType: "SQLiteReal";
1402
+ data: number;
1403
+ driverParam: number;
1404
+ notNull: false;
1405
+ hasDefault: false;
1406
+ isPrimaryKey: false;
1407
+ isAutoincrement: false;
1408
+ hasRuntimeDefault: false;
1409
+ enumValues: undefined;
1410
+ baseColumn: never;
1411
+ identity: undefined;
1412
+ generated: undefined;
1413
+ }, {}, {}>;
1385
1414
  currency: drizzle_orm_sqlite_core.SQLiteColumn<{
1386
1415
  name: "currency";
1387
1416
  tableName: "transaction";
@@ -704,7 +704,7 @@ declare const IDENTIFIER_KINDS: readonly ["IBAN", "LOCAL_CZ", "SWIFT", "CRYPTO_A
704
704
  type IdentifierKind = (typeof IDENTIFIER_KINDS)[number];
705
705
  declare const ENTRY_STATUSES: readonly ["PENDING", "REALIZED", "FAILED", "CANCELED"];
706
706
  type EntryStatus = (typeof ENTRY_STATUSES)[number];
707
- declare const TRANSACTION_STATUSES: readonly ["WAITING_FOR_PAYMENT", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
707
+ declare const TRANSACTION_STATUSES: readonly ["WAITING_FOR_PAYMENT", "PARTIALLY_MATCHED", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
708
708
  type TransactionStatus = (typeof TRANSACTION_STATUSES)[number];
709
709
  declare const TRANSACTION_TYPES: readonly ["CLIENT_FUND_IN", "CLIENT_FUND_OUT", "PROVIDER_FUND_IN", "PROVIDER_FUND_OUT", "COLLATERAL_FUND_IN", "COLLATERAL_FUND_OUT", "EXCHANGE", "UNMATCHED", "ADJUSTMENT", "TRANSFER", "REFUND"];
710
710
  type TransactionType = (typeof TRANSACTION_TYPES)[number];
@@ -773,6 +773,18 @@ interface TransactionMetadata {
773
773
  refund?: {
774
774
  isPartialRefund: boolean;
775
775
  };
776
+ /**
777
+ * History of partial matches — each incoming payment that contributed to this transaction
778
+ */
779
+ partialMatches?: Array<{
780
+ paymentId: string;
781
+ amount: number;
782
+ matchedAt: string;
783
+ }>;
784
+ /**
785
+ * ID of the user who performed a manual action (resolve, refund, etc.)
786
+ */
787
+ performedBy?: string;
776
788
  }
777
789
  interface EntryMetadata {
778
790
  context?: {
@@ -1181,7 +1193,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1181
1193
  tableName: "transaction";
1182
1194
  dataType: "string";
1183
1195
  columnType: "SQLiteText";
1184
- data: "PAYMENT" | "EXCHANGE" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1196
+ data: "EXCHANGE" | "PAYMENT" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1185
1197
  driverParam: string;
1186
1198
  notNull: true;
1187
1199
  hasDefault: false;
@@ -1194,7 +1206,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1194
1206
  generated: undefined;
1195
1207
  }, {}, {
1196
1208
  length: number | undefined;
1197
- $type: "PAYMENT" | "EXCHANGE" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1209
+ $type: "EXCHANGE" | "PAYMENT" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1198
1210
  }>;
1199
1211
  referenceId: drizzle_orm_sqlite_core.SQLiteColumn<{
1200
1212
  name: "reference_id";
@@ -1220,7 +1232,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1220
1232
  tableName: "transaction";
1221
1233
  dataType: "string";
1222
1234
  columnType: "SQLiteText";
1223
- data: "EXCHANGE" | "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1235
+ data: "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "EXCHANGE" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1224
1236
  driverParam: string;
1225
1237
  notNull: true;
1226
1238
  hasDefault: false;
@@ -1233,7 +1245,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1233
1245
  generated: undefined;
1234
1246
  }, {}, {
1235
1247
  length: number | undefined;
1236
- $type: "EXCHANGE" | "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1248
+ $type: "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "EXCHANGE" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1237
1249
  }>;
1238
1250
  description: drizzle_orm_sqlite_core.SQLiteColumn<{
1239
1251
  name: "description";
@@ -1293,20 +1305,20 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1293
1305
  tableName: "transaction";
1294
1306
  dataType: "string";
1295
1307
  columnType: "SQLiteText";
1296
- data: "FAILED" | "WAITING_FOR_PAYMENT" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1308
+ data: "FAILED" | "WAITING_FOR_PAYMENT" | "PARTIALLY_MATCHED" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1297
1309
  driverParam: string;
1298
1310
  notNull: true;
1299
1311
  hasDefault: false;
1300
1312
  isPrimaryKey: false;
1301
1313
  isAutoincrement: false;
1302
1314
  hasRuntimeDefault: false;
1303
- enumValues: ["WAITING_FOR_PAYMENT", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
1315
+ enumValues: ["WAITING_FOR_PAYMENT", "PARTIALLY_MATCHED", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
1304
1316
  baseColumn: never;
1305
1317
  identity: undefined;
1306
1318
  generated: undefined;
1307
1319
  }, {}, {
1308
1320
  length: number | undefined;
1309
- $type: "FAILED" | "WAITING_FOR_PAYMENT" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1321
+ $type: "FAILED" | "WAITING_FOR_PAYMENT" | "PARTIALLY_MATCHED" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1310
1322
  }>;
1311
1323
  statusReason: drizzle_orm_sqlite_core.SQLiteColumn<{
1312
1324
  name: "status_reason";
@@ -1382,6 +1394,23 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1382
1394
  identity: undefined;
1383
1395
  generated: undefined;
1384
1396
  }, {}, {}>;
1397
+ receivedValue: drizzle_orm_sqlite_core.SQLiteColumn<{
1398
+ name: "received_value";
1399
+ tableName: "transaction";
1400
+ dataType: "number";
1401
+ columnType: "SQLiteReal";
1402
+ data: number;
1403
+ driverParam: number;
1404
+ notNull: false;
1405
+ hasDefault: false;
1406
+ isPrimaryKey: false;
1407
+ isAutoincrement: false;
1408
+ hasRuntimeDefault: false;
1409
+ enumValues: undefined;
1410
+ baseColumn: never;
1411
+ identity: undefined;
1412
+ generated: undefined;
1413
+ }, {}, {}>;
1385
1414
  currency: drizzle_orm_sqlite_core.SQLiteColumn<{
1386
1415
  name: "currency";
1387
1416
  tableName: "transaction";
@@ -704,7 +704,7 @@ declare const IDENTIFIER_KINDS: readonly ["IBAN", "LOCAL_CZ", "SWIFT", "CRYPTO_A
704
704
  type IdentifierKind = (typeof IDENTIFIER_KINDS)[number];
705
705
  declare const ENTRY_STATUSES: readonly ["PENDING", "REALIZED", "FAILED", "CANCELED"];
706
706
  type EntryStatus = (typeof ENTRY_STATUSES)[number];
707
- declare const TRANSACTION_STATUSES: readonly ["WAITING_FOR_PAYMENT", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
707
+ declare const TRANSACTION_STATUSES: readonly ["WAITING_FOR_PAYMENT", "PARTIALLY_MATCHED", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
708
708
  type TransactionStatus = (typeof TRANSACTION_STATUSES)[number];
709
709
  declare const TRANSACTION_TYPES: readonly ["CLIENT_FUND_IN", "CLIENT_FUND_OUT", "PROVIDER_FUND_IN", "PROVIDER_FUND_OUT", "COLLATERAL_FUND_IN", "COLLATERAL_FUND_OUT", "EXCHANGE", "UNMATCHED", "ADJUSTMENT", "TRANSFER", "REFUND"];
710
710
  type TransactionType = (typeof TRANSACTION_TYPES)[number];
@@ -773,6 +773,18 @@ interface TransactionMetadata {
773
773
  refund?: {
774
774
  isPartialRefund: boolean;
775
775
  };
776
+ /**
777
+ * History of partial matches — each incoming payment that contributed to this transaction
778
+ */
779
+ partialMatches?: Array<{
780
+ paymentId: string;
781
+ amount: number;
782
+ matchedAt: string;
783
+ }>;
784
+ /**
785
+ * ID of the user who performed a manual action (resolve, refund, etc.)
786
+ */
787
+ performedBy?: string;
776
788
  }
777
789
  interface EntryMetadata {
778
790
  context?: {
@@ -1181,7 +1193,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1181
1193
  tableName: "transaction";
1182
1194
  dataType: "string";
1183
1195
  columnType: "SQLiteText";
1184
- data: "PAYMENT" | "EXCHANGE" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1196
+ data: "EXCHANGE" | "PAYMENT" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1185
1197
  driverParam: string;
1186
1198
  notNull: true;
1187
1199
  hasDefault: false;
@@ -1194,7 +1206,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1194
1206
  generated: undefined;
1195
1207
  }, {}, {
1196
1208
  length: number | undefined;
1197
- $type: "PAYMENT" | "EXCHANGE" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1209
+ $type: "EXCHANGE" | "PAYMENT" | "ORDER" | "INTERNAL-TRANSFER" | "FORWARD" | "TRANSACTION";
1198
1210
  }>;
1199
1211
  referenceId: drizzle_orm_sqlite_core.SQLiteColumn<{
1200
1212
  name: "reference_id";
@@ -1220,7 +1232,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1220
1232
  tableName: "transaction";
1221
1233
  dataType: "string";
1222
1234
  columnType: "SQLiteText";
1223
- data: "EXCHANGE" | "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1235
+ data: "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "EXCHANGE" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1224
1236
  driverParam: string;
1225
1237
  notNull: true;
1226
1238
  hasDefault: false;
@@ -1233,7 +1245,7 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1233
1245
  generated: undefined;
1234
1246
  }, {}, {
1235
1247
  length: number | undefined;
1236
- $type: "EXCHANGE" | "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1248
+ $type: "CLIENT_FUND_IN" | "CLIENT_FUND_OUT" | "PROVIDER_FUND_IN" | "PROVIDER_FUND_OUT" | "COLLATERAL_FUND_IN" | "COLLATERAL_FUND_OUT" | "EXCHANGE" | "UNMATCHED" | "ADJUSTMENT" | "TRANSFER" | "REFUND";
1237
1249
  }>;
1238
1250
  description: drizzle_orm_sqlite_core.SQLiteColumn<{
1239
1251
  name: "description";
@@ -1293,20 +1305,20 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1293
1305
  tableName: "transaction";
1294
1306
  dataType: "string";
1295
1307
  columnType: "SQLiteText";
1296
- data: "FAILED" | "WAITING_FOR_PAYMENT" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1308
+ data: "FAILED" | "WAITING_FOR_PAYMENT" | "PARTIALLY_MATCHED" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1297
1309
  driverParam: string;
1298
1310
  notNull: true;
1299
1311
  hasDefault: false;
1300
1312
  isPrimaryKey: false;
1301
1313
  isAutoincrement: false;
1302
1314
  hasRuntimeDefault: false;
1303
- enumValues: ["WAITING_FOR_PAYMENT", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
1315
+ enumValues: ["WAITING_FOR_PAYMENT", "PARTIALLY_MATCHED", "WAITING_FOR_MANUAL_PROCESSING", "MATCHED", "RETURNING", "RETURNED", "FAILED", "CANCELLED", "SETTLED"];
1304
1316
  baseColumn: never;
1305
1317
  identity: undefined;
1306
1318
  generated: undefined;
1307
1319
  }, {}, {
1308
1320
  length: number | undefined;
1309
- $type: "FAILED" | "WAITING_FOR_PAYMENT" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1321
+ $type: "FAILED" | "WAITING_FOR_PAYMENT" | "PARTIALLY_MATCHED" | "WAITING_FOR_MANUAL_PROCESSING" | "MATCHED" | "RETURNING" | "RETURNED" | "CANCELLED" | "SETTLED";
1310
1322
  }>;
1311
1323
  statusReason: drizzle_orm_sqlite_core.SQLiteColumn<{
1312
1324
  name: "status_reason";
@@ -1382,6 +1394,23 @@ declare const transaction: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
1382
1394
  identity: undefined;
1383
1395
  generated: undefined;
1384
1396
  }, {}, {}>;
1397
+ receivedValue: drizzle_orm_sqlite_core.SQLiteColumn<{
1398
+ name: "received_value";
1399
+ tableName: "transaction";
1400
+ dataType: "number";
1401
+ columnType: "SQLiteReal";
1402
+ data: number;
1403
+ driverParam: number;
1404
+ notNull: false;
1405
+ hasDefault: false;
1406
+ isPrimaryKey: false;
1407
+ isAutoincrement: false;
1408
+ hasRuntimeDefault: false;
1409
+ enumValues: undefined;
1410
+ baseColumn: never;
1411
+ identity: undefined;
1412
+ generated: undefined;
1413
+ }, {}, {}>;
1385
1414
  currency: drizzle_orm_sqlite_core.SQLiteColumn<{
1386
1415
  name: "currency";
1387
1416
  tableName: "transaction";
@@ -3,7 +3,7 @@
3
3
  const backendSdk = require('@develit-io/backend-sdk');
4
4
  const drizzleOrm = require('drizzle-orm');
5
5
  const sqliteCore = require('drizzle-orm/sqlite-core');
6
- const updateTransactionStatus = require('./ledger.qc5F0gBL.cjs');
6
+ const updateTransactionStatus = require('./ledger.CAoxOU3F.cjs');
7
7
  require('@develit-io/general-codes');
8
8
 
9
9
  const account = sqliteCore.sqliteTable("account", {
@@ -92,6 +92,7 @@ const transaction = sqliteCore.sqliteTable("transaction", {
92
92
  paymentId: sqliteCore.text("payment_id"),
93
93
  metadata: sqliteCore.text("metadata", { mode: "json" }).$type().notNull(),
94
94
  value: sqliteCore.real("value"),
95
+ receivedValue: sqliteCore.real("received_value"),
95
96
  currency: sqliteCore.text("currency").$type()
96
97
  });
97
98