@develit-services/ledger 0.3.6 → 0.4.1

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?: {
@@ -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?: {
@@ -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?: {
@@ -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";
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { F as schema } from './ledger.CclKAyrw.js';
2
+ import { F as schema } from './ledger.B9mOFtFf.js';
3
3
  import { InferSelectModel, ExtractTablesWithRelations, DBQueryConfig, BuildQueryResult, InferInsertModel } from 'drizzle-orm';
4
4
 
5
5
  interface TSchema extends ExtractTablesWithRelations<typeof tables> {
@@ -1185,6 +1185,7 @@ declare const createTransactionInputSchema: z.ZodObject<{
1185
1185
  status: z.ZodEnum<{
1186
1186
  FAILED: "FAILED";
1187
1187
  WAITING_FOR_PAYMENT: "WAITING_FOR_PAYMENT";
1188
+ PARTIALLY_MATCHED: "PARTIALLY_MATCHED";
1188
1189
  WAITING_FOR_MANUAL_PROCESSING: "WAITING_FOR_MANUAL_PROCESSING";
1189
1190
  MATCHED: "MATCHED";
1190
1191
  RETURNING: "RETURNING";
@@ -1413,6 +1414,7 @@ declare const getTransactionsInputSchema: z.ZodObject<{
1413
1414
  filterTransactionStatus: z.ZodOptional<z.ZodEnum<{
1414
1415
  FAILED: "FAILED";
1415
1416
  WAITING_FOR_PAYMENT: "WAITING_FOR_PAYMENT";
1417
+ PARTIALLY_MATCHED: "PARTIALLY_MATCHED";
1416
1418
  WAITING_FOR_MANUAL_PROCESSING: "WAITING_FOR_MANUAL_PROCESSING";
1417
1419
  MATCHED: "MATCHED";
1418
1420
  RETURNING: "RETURNING";
@@ -1420,6 +1422,17 @@ declare const getTransactionsInputSchema: z.ZodObject<{
1420
1422
  CANCELLED: "CANCELLED";
1421
1423
  SETTLED: "SETTLED";
1422
1424
  }>>;
1425
+ filterTransactionStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1426
+ FAILED: "FAILED";
1427
+ WAITING_FOR_PAYMENT: "WAITING_FOR_PAYMENT";
1428
+ PARTIALLY_MATCHED: "PARTIALLY_MATCHED";
1429
+ WAITING_FOR_MANUAL_PROCESSING: "WAITING_FOR_MANUAL_PROCESSING";
1430
+ MATCHED: "MATCHED";
1431
+ RETURNING: "RETURNING";
1432
+ RETURNED: "RETURNED";
1433
+ CANCELLED: "CANCELLED";
1434
+ SETTLED: "SETTLED";
1435
+ }>>>;
1423
1436
  search: z.ZodOptional<z.ZodString>;
1424
1437
  }, z.core.$strip>;
1425
1438
  interface GetTransactionsInput extends z.input<typeof getTransactionsInputSchema> {
@@ -1549,6 +1562,7 @@ interface ListAccountsOutput {
1549
1562
  declare const matchTransactionInputSchema: z.ZodObject<{
1550
1563
  transactionId: z.ZodUUID;
1551
1564
  paymentId: z.ZodString;
1565
+ amount: z.ZodOptional<z.ZodNumber>;
1552
1566
  completedAt: z.ZodOptional<z.ZodDate>;
1553
1567
  }, z.core.$strip>;
1554
1568
  interface MatchTransactionInput extends z.infer<typeof matchTransactionInputSchema> {
@@ -1659,6 +1673,7 @@ declare const refundTransactionInputSchema: z.ZodObject<{
1659
1673
  transactionId: z.ZodUUID;
1660
1674
  amount: z.ZodOptional<z.ZodNumber>;
1661
1675
  reason: z.ZodString;
1676
+ performedBy: z.ZodOptional<z.ZodString>;
1662
1677
  }, z.core.$strip>;
1663
1678
  interface RefundTransactionInput extends z.infer<typeof refundTransactionInputSchema> {
1664
1679
  }
@@ -1667,11 +1682,23 @@ interface RefundTransactionOutput {
1667
1682
  originalTransaction: TransactionSelectType;
1668
1683
  }
1669
1684
 
1685
+ declare const resolveUnmatchedTransactionInputSchema: z.ZodObject<{
1686
+ unmatchedTransactionId: z.ZodUUID;
1687
+ targetTransactionId: z.ZodUUID;
1688
+ note: z.ZodString;
1689
+ performedBy: z.ZodString;
1690
+ }, z.core.$strip>;
1691
+ interface ResolveUnmatchedTransactionInput extends z.infer<typeof resolveUnmatchedTransactionInputSchema> {
1692
+ }
1693
+ interface ResolveUnmatchedTransactionOutput extends TransactionSelectType {
1694
+ }
1695
+
1670
1696
  declare const updateTransactionStatusInputSchema: z.ZodObject<{
1671
1697
  transactionId: z.ZodUUID;
1672
1698
  status: z.ZodEnum<{
1673
1699
  FAILED: "FAILED";
1674
1700
  WAITING_FOR_PAYMENT: "WAITING_FOR_PAYMENT";
1701
+ PARTIALLY_MATCHED: "PARTIALLY_MATCHED";
1675
1702
  WAITING_FOR_MANUAL_PROCESSING: "WAITING_FOR_MANUAL_PROCESSING";
1676
1703
  MATCHED: "MATCHED";
1677
1704
  RETURNING: "RETURNING";
@@ -1690,5 +1717,5 @@ interface UpdateTransactionStatusOutput extends TransactionSelectType {
1690
1717
 
1691
1718
  declare const tables: typeof schema;
1692
1719
 
1693
- export { createAccountInputSchema as $, cancelTransactionInputSchema as _, createTransactionInputSchema as a0, deleteAccountInputSchema as a1, failTransactionInputSchema as a2, findAccountByIdentifierInputSchema as a3, getAccountBalanceInputSchema as a4, getAccountIdentifierInputSchema as a5, getAccountInputSchema as a6, getAccountsByOwnerInputSchema as a7, getTransactionByIdInputSchema as a8, getTransactionsByReferenceIdInputSchema as a9, getTransactionsInputSchema as aa, listAccountIdentifiersInputSchema as ab, listAccountsInputSchema as ac, matchTransactionInputSchema as ad, refundTransactionInputSchema as ae, updateAccountInputSchema as af, updateTransactionConfirmationSentAtInputSchema as ag, updateTransactionStatusInputSchema as ah, tables as t };
1694
- export type { FindAccountByIdentifierOutput as A, GetAccountBalanceInput as B, CreateTransactionInput as C, DeleteAccountInput as D, GetAccountBalanceOutput as E, FailTransactionInput as F, GetTransactionByIdInput as G, UpdateTransactionConfirmationSentAtInput as H, UpdateTransactionConfirmationSentAtOutput as I, UpdateTransactionStatusInput as J, UpdateTransactionStatusOutput as K, ListAccountsInput as L, MatchTransactionInput as M, AccountIdentifierInsertType as N, AccountIdentifierMappingInsertType as O, AccountIdentifierMappingSelectType as P, AccountIdentifierSelectType as Q, RefundTransactionInput as R, AccountInsertType as S, TransactionSelectType as T, UpdateAccountInput as U, AccountSelectType as V, AccountWithIdentifiersSelectType as W, IncludeRelation as X, InferResultType as Y, TransactionInsertType as Z, CreateTransactionOutput as a, MatchTransactionOutput as b, FailTransactionOutput as c, CancelTransactionInput as d, CancelTransactionOutput as e, RefundTransactionOutput as f, GetTransactionByIdOutput as g, GetTransactionsByIdReferenceInput as h, GetTransactionsByReferenceIdOutput as i, GetTransactionsInput as j, GetTransactionsOutput as k, CreateAccountInput as l, CreateAccountOutput as m, UpdateAccountOutput as n, DeleteAccountOutput as o, GetAccountInput as p, GetAccountOutput as q, GetAccountsByOwnerInput as r, GetAccountsByOwnerOutput as s, ListAccountsOutput as u, GetAccountIdentifierInput as v, GetAccountIdentifierOutput as w, ListAccountIdentifiersInput as x, ListAccountIdentifiersOutput as y, FindAccountByIdentifierInput as z };
1720
+ export { cancelTransactionInputSchema as a0, createAccountInputSchema as a1, createTransactionInputSchema as a2, deleteAccountInputSchema as a3, failTransactionInputSchema as a4, findAccountByIdentifierInputSchema as a5, getAccountBalanceInputSchema as a6, getAccountIdentifierInputSchema as a7, getAccountInputSchema as a8, getAccountsByOwnerInputSchema as a9, getTransactionByIdInputSchema as aa, getTransactionsByReferenceIdInputSchema as ab, getTransactionsInputSchema as ac, listAccountIdentifiersInputSchema as ad, listAccountsInputSchema as ae, matchTransactionInputSchema as af, refundTransactionInputSchema as ag, resolveUnmatchedTransactionInputSchema as ah, updateAccountInputSchema as ai, updateTransactionConfirmationSentAtInputSchema as aj, updateTransactionStatusInputSchema as ak, tables as t };
1721
+ export type { TransactionInsertType as $, ListAccountIdentifiersOutput as A, FindAccountByIdentifierInput as B, CreateTransactionInput as C, DeleteAccountInput as D, FindAccountByIdentifierOutput as E, FailTransactionInput as F, GetTransactionByIdInput as G, GetAccountBalanceInput as H, GetAccountBalanceOutput as I, UpdateTransactionConfirmationSentAtInput as J, UpdateTransactionConfirmationSentAtOutput as K, ListAccountsInput as L, MatchTransactionInput as M, UpdateTransactionStatusInput as N, UpdateTransactionStatusOutput as O, AccountIdentifierInsertType as P, AccountIdentifierMappingInsertType as Q, ResolveUnmatchedTransactionInput as R, AccountIdentifierMappingSelectType as S, TransactionSelectType as T, UpdateAccountInput as U, AccountIdentifierSelectType as V, AccountInsertType as W, AccountSelectType as X, AccountWithIdentifiersSelectType as Y, IncludeRelation as Z, InferResultType as _, CreateTransactionOutput as a, MatchTransactionOutput as b, ResolveUnmatchedTransactionOutput as c, FailTransactionOutput as d, CancelTransactionInput as e, CancelTransactionOutput as f, RefundTransactionInput as g, RefundTransactionOutput as h, GetTransactionByIdOutput as i, GetTransactionsByIdReferenceInput as j, GetTransactionsByReferenceIdOutput as k, GetTransactionsInput as l, GetTransactionsOutput as m, CreateAccountInput as n, CreateAccountOutput as o, UpdateAccountOutput as p, DeleteAccountOutput as q, GetAccountInput as r, GetAccountOutput as s, GetAccountsByOwnerInput as u, GetAccountsByOwnerOutput as v, ListAccountsOutput as w, GetAccountIdentifierInput as x, GetAccountIdentifierOutput as y, ListAccountIdentifiersInput as z };
@@ -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