@things-factory/accounting 10.1.140 → 10.1.141
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.
- package/dist-server/service/account/account-match.d.ts +32 -0
- package/dist-server/service/account/account-match.js +74 -0
- package/dist-server/service/account/account-match.js.map +1 -0
- package/dist-server/service/account/index.d.ts +1 -1
- package/dist-server/service/accounting-category/index.d.ts +1 -1
- package/dist-server/service/accounting-document/index.d.ts +1 -1
- package/dist-server/service/bank-account/index.d.ts +1 -1
- package/dist-server/service/bank-statement/index.d.ts +1 -1
- package/dist-server/service/financial-institution/index.d.ts +1 -1
- package/dist-server/service/financial-statement/index.d.ts +2 -2
- package/dist-server/service/fiscal-month/index.d.ts +1 -1
- package/dist-server/service/fiscal-quarter/index.d.ts +1 -1
- package/dist-server/service/fiscal-year/index.d.ts +1 -1
- package/dist-server/service/index.d.ts +4 -2
- package/dist-server/service/index.js +2 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/payment/document-targets.d.ts +16 -0
- package/dist-server/service/payment/document-targets.js +99 -0
- package/dist-server/service/payment/document-targets.js.map +1 -0
- package/dist-server/service/payment/payment-service.js +3 -0
- package/dist-server/service/payment/payment-service.js.map +1 -1
- package/dist-server/service/payment/payment-targets.d.ts +12 -0
- package/dist-server/service/payment/payment-targets.js +9 -0
- package/dist-server/service/payment/payment-targets.js.map +1 -1
- package/dist-server/service/transaction/transaction-query.js +13 -1
- package/dist-server/service/transaction/transaction-query.js.map +1 -1
- package/dist-server/service/transaction/transaction-service.js +2 -0
- package/dist-server/service/transaction/transaction-service.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -7
- package/translations/en.json +10 -1
- package/translations/ko.json +10 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EntityManager } from 'typeorm';
|
|
2
|
+
export interface AccountForMatch {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
type: string;
|
|
6
|
+
aliases?: string | null;
|
|
7
|
+
}
|
|
8
|
+
/** An account's name and its aliases (one per line, or comma separated). */
|
|
9
|
+
export declare function aliasesOf(account: Pick<AccountForMatch, 'name' | 'aliases'>): string[];
|
|
10
|
+
/**
|
|
11
|
+
* A function telling which account some words mean. `type` limits it to that direction - an expense is never filed
|
|
12
|
+
* under an income account because a word happens to match.
|
|
13
|
+
*/
|
|
14
|
+
export declare function accountMatcher<T extends AccountForMatch>(accounts: readonly T[]): (texts: readonly (string | null | undefined)[], type?: string | null) => T | undefined;
|
|
15
|
+
export interface AccountLinking {
|
|
16
|
+
/** Rows given an account, per kind: confirmed transactions, forecasts, recurring items. */
|
|
17
|
+
linked: {
|
|
18
|
+
transactions: number;
|
|
19
|
+
forecasts: number;
|
|
20
|
+
recurring: number;
|
|
21
|
+
};
|
|
22
|
+
/** The words of confirmed transactions no account matched. */
|
|
23
|
+
unmatched: string[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Give every row without an account the one its words mean: a confirmed transaction by its description then its
|
|
27
|
+
* ledger category, a forecast by its description, a recurring item by its name. `apply` false writes nothing. Rows a
|
|
28
|
+
* person already filed are left alone.
|
|
29
|
+
*/
|
|
30
|
+
export declare function linkAccounts(tx: EntityManager, domain: {
|
|
31
|
+
id: string;
|
|
32
|
+
}, match: ReturnType<typeof accountMatcher>, apply: boolean): Promise<AccountLinking>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.aliasesOf = aliasesOf;
|
|
4
|
+
exports.accountMatcher = accountMatcher;
|
|
5
|
+
exports.linkAccounts = linkAccounts;
|
|
6
|
+
const typeorm_1 = require("typeorm");
|
|
7
|
+
const recurring_cash_item_1 = require("../recurring-cash-item/recurring-cash-item");
|
|
8
|
+
const transaction_1 = require("../transaction/transaction");
|
|
9
|
+
const key = (text) => String(text ?? '')
|
|
10
|
+
.normalize('NFC')
|
|
11
|
+
.replace(/[\s\-_.·,()]+/g, '')
|
|
12
|
+
.toLowerCase();
|
|
13
|
+
/** An account's name and its aliases (one per line, or comma separated). */
|
|
14
|
+
function aliasesOf(account) {
|
|
15
|
+
return [account.name, ...String(account.aliases ?? '').split(/[\n,]/)].map(one => one.trim()).filter(Boolean);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A function telling which account some words mean. `type` limits it to that direction - an expense is never filed
|
|
19
|
+
* under an income account because a word happens to match.
|
|
20
|
+
*/
|
|
21
|
+
function accountMatcher(accounts) {
|
|
22
|
+
const words = [];
|
|
23
|
+
for (const account of accounts)
|
|
24
|
+
for (const alias of aliasesOf(account))
|
|
25
|
+
if (key(alias).length >= 2)
|
|
26
|
+
words.push({ word: key(alias), account });
|
|
27
|
+
words.sort((a, b) => b.word.length - a.word.length);
|
|
28
|
+
return (texts, type) => {
|
|
29
|
+
for (const text of texts) {
|
|
30
|
+
const hay = key(text);
|
|
31
|
+
if (!hay)
|
|
32
|
+
continue;
|
|
33
|
+
const hits = words.filter(one => (!type || one.account.type === type) && hay.includes(one.word));
|
|
34
|
+
if (!hits.length)
|
|
35
|
+
continue;
|
|
36
|
+
const longest = hits[0].word.length;
|
|
37
|
+
const top = new Set(hits.filter(one => one.word.length === longest).map(one => one.account.id));
|
|
38
|
+
return top.size === 1 ? hits[0].account : undefined;
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Give every row without an account the one its words mean: a confirmed transaction by its description then its
|
|
45
|
+
* ledger category, a forecast by its description, a recurring item by its name. `apply` false writes nothing. Rows a
|
|
46
|
+
* person already filed are left alone.
|
|
47
|
+
*/
|
|
48
|
+
async function linkAccounts(tx, domain, match, apply) {
|
|
49
|
+
const report = { linked: { transactions: 0, forecasts: 0, recurring: 0 }, unmatched: [] };
|
|
50
|
+
const run = async (label, entity, texts, typeOf, scope = {}) => {
|
|
51
|
+
const rows = (await tx.getRepository(entity).find({ where: { domain: { id: domain.id }, ...scope, account: (0, typeorm_1.IsNull)() } }));
|
|
52
|
+
const groups = new Map();
|
|
53
|
+
for (const row of rows) {
|
|
54
|
+
const account = match(texts(row), typeOf(row));
|
|
55
|
+
if (!account) {
|
|
56
|
+
if (label === 'transactions')
|
|
57
|
+
report.unmatched.push(String(texts(row)[0] ?? ''));
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
groups.set(account.id, [...(groups.get(account.id) ?? []), row.id]);
|
|
61
|
+
}
|
|
62
|
+
report.linked[label] = [...groups.values()].reduce((sum, ids) => sum + ids.length, 0);
|
|
63
|
+
if (!apply)
|
|
64
|
+
return;
|
|
65
|
+
for (const [id, ids] of groups)
|
|
66
|
+
for (let i = 0; i < ids.length; i += 500)
|
|
67
|
+
await tx.getRepository(entity).update(ids.slice(i, i + 500), { account: { id } });
|
|
68
|
+
};
|
|
69
|
+
await run('transactions', transaction_1.Transaction, row => [row.description, row.ledgerCategory], row => row.type, { category: transaction_1.TransactionCategory.CONFIRMED });
|
|
70
|
+
await run('forecasts', transaction_1.Transaction, row => [row.description], row => row.type, { category: transaction_1.TransactionCategory.FORECAST });
|
|
71
|
+
await run('recurring', recurring_cash_item_1.RecurringCashItem, row => [row.name], row => row.type);
|
|
72
|
+
return report;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=account-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-match.js","sourceRoot":"","sources":["../../../server/service/account/account-match.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, IsNull } from 'typeorm'\n\nimport { RecurringCashItem } from '../recurring-cash-item/recurring-cash-item'\nimport { Transaction, TransactionCategory } from '../transaction/transaction'\n\n/*\n * Which account some words mean. An account is known by its name and its aliases; the longest one contained in the\n * text wins, among accounts of the row's direction. Two different accounts matching equally long is no answer, and\n * nothing matching is no answer - a row nobody can place stays unplaced, to be listed, not filed under a catch-all.\n */\n\nexport interface AccountForMatch {\n id: string\n name: string\n type: string\n aliases?: string | null\n}\n\nconst key = (text: string | null | undefined) =>\n String(text ?? '')\n .normalize('NFC')\n .replace(/[\\s\\-_.·,()]+/g, '')\n .toLowerCase()\n\n/** An account's name and its aliases (one per line, or comma separated). */\nexport function aliasesOf(account: Pick<AccountForMatch, 'name' | 'aliases'>): string[] {\n return [account.name, ...String(account.aliases ?? '').split(/[\\n,]/)].map(one => one.trim()).filter(Boolean)\n}\n\n/**\n * A function telling which account some words mean. `type` limits it to that direction - an expense is never filed\n * under an income account because a word happens to match.\n */\nexport function accountMatcher<T extends AccountForMatch>(accounts: readonly T[]) {\n const words: { word: string; account: T }[] = []\n for (const account of accounts) for (const alias of aliasesOf(account)) if (key(alias).length >= 2) words.push({ word: key(alias), account })\n words.sort((a, b) => b.word.length - a.word.length)\n\n return (texts: readonly (string | null | undefined)[], type?: string | null): T | undefined => {\n for (const text of texts) {\n const hay = key(text)\n if (!hay) continue\n const hits = words.filter(one => (!type || one.account.type === type) && hay.includes(one.word))\n if (!hits.length) continue\n const longest = hits[0].word.length\n const top = new Set(hits.filter(one => one.word.length === longest).map(one => one.account.id))\n return top.size === 1 ? hits[0].account : undefined\n }\n return undefined\n }\n}\n\nexport interface AccountLinking {\n /** Rows given an account, per kind: confirmed transactions, forecasts, recurring items. */\n linked: { transactions: number; forecasts: number; recurring: number }\n /** The words of confirmed transactions no account matched. */\n unmatched: string[]\n}\n\n/**\n * Give every row without an account the one its words mean: a confirmed transaction by its description then its\n * ledger category, a forecast by its description, a recurring item by its name. `apply` false writes nothing. Rows a\n * person already filed are left alone.\n */\nexport async function linkAccounts(\n tx: EntityManager,\n domain: { id: string },\n match: ReturnType<typeof accountMatcher>,\n apply: boolean\n): Promise<AccountLinking> {\n const report: AccountLinking = { linked: { transactions: 0, forecasts: 0, recurring: 0 }, unmatched: [] }\n\n const run = async <T extends { id: string }>(\n label: keyof AccountLinking['linked'],\n entity: new () => T,\n texts: (row: T) => (string | null | undefined)[],\n typeOf: (row: T) => string | null,\n scope: Record<string, any> = {}\n ) => {\n const rows = (await tx.getRepository(entity as any).find({ where: { domain: { id: domain.id }, ...scope, account: IsNull() } as any })) as T[]\n const groups = new Map<string, string[]>()\n for (const row of rows) {\n const account = match(texts(row), typeOf(row))\n if (!account) {\n if (label === 'transactions') report.unmatched.push(String(texts(row)[0] ?? ''))\n continue\n }\n groups.set(account.id, [...(groups.get(account.id) ?? []), row.id])\n }\n report.linked[label] = [...groups.values()].reduce((sum, ids) => sum + ids.length, 0)\n if (!apply) return\n for (const [id, ids] of groups)\n for (let i = 0; i < ids.length; i += 500) await tx.getRepository(entity as any).update(ids.slice(i, i + 500), { account: { id } } as any)\n }\n\n await run('transactions', Transaction, row => [row.description, row.ledgerCategory], row => row.type, { category: TransactionCategory.CONFIRMED })\n await run('forecasts', Transaction, row => [row.description], row => row.type, { category: TransactionCategory.FORECAST })\n await run('recurring', RecurringCashItem, row => [row.name], row => row.type)\n return report\n}\n"]}
|
|
@@ -3,5 +3,5 @@ import { AccountQuery } from './account-query';
|
|
|
3
3
|
import { AccountMutation } from './account-mutation';
|
|
4
4
|
import { AccountHistoryEntitySubscriber } from './event-subscriber';
|
|
5
5
|
export declare const entities: (typeof Account)[];
|
|
6
|
-
export declare const resolvers: (typeof
|
|
6
|
+
export declare const resolvers: (typeof AccountQuery | typeof AccountMutation)[];
|
|
7
7
|
export declare const subscribers: (typeof AccountHistoryEntitySubscriber)[];
|
|
@@ -3,5 +3,5 @@ import { AccountingCategoryQuery } from './accounting-category-query';
|
|
|
3
3
|
import { AccountingCategoryMutation } from './accounting-category-mutation';
|
|
4
4
|
import { AccountingCategoryHistoryEntitySubscriber } from './event-subscriber';
|
|
5
5
|
export declare const entities: (typeof AccountingCategory)[];
|
|
6
|
-
export declare const resolvers: (typeof
|
|
6
|
+
export declare const resolvers: (typeof AccountingCategoryQuery | typeof AccountingCategoryMutation)[];
|
|
7
7
|
export declare const subscribers: (typeof AccountingCategoryHistoryEntitySubscriber)[];
|
|
@@ -3,5 +3,5 @@ import { AccountingDocumentHistoryEntitySubscriber } from './event-subscriber';
|
|
|
3
3
|
import { AccountingDocumentQuery } from './accounting-document-query';
|
|
4
4
|
import { AccountingDocumentMutation } from './accounting-document-mutation';
|
|
5
5
|
export declare const entities: (typeof AccountingDocument)[];
|
|
6
|
-
export declare const resolvers: (typeof
|
|
6
|
+
export declare const resolvers: (typeof AccountingDocumentQuery | typeof AccountingDocumentMutation)[];
|
|
7
7
|
export declare const subscribers: (typeof AccountingDocumentHistoryEntitySubscriber)[];
|
|
@@ -3,5 +3,5 @@ import { BankAccountHistoryEntitySubscriber } from './event-subscriber';
|
|
|
3
3
|
import { BankAccountQuery } from './bank-account-query';
|
|
4
4
|
import { BankAccountMutation } from './bank-account-mutation';
|
|
5
5
|
export declare const entities: (typeof BankAccount)[];
|
|
6
|
-
export declare const resolvers: (typeof
|
|
6
|
+
export declare const resolvers: (typeof BankAccountQuery | typeof BankAccountMutation)[];
|
|
7
7
|
export declare const subscribers: (typeof BankAccountHistoryEntitySubscriber)[];
|
|
@@ -2,5 +2,5 @@ import { BankStatement } from './bank-statement';
|
|
|
2
2
|
import { BankStatementQuery } from './bank-statement-query';
|
|
3
3
|
import { BankImportMutation } from './bank-import-mutation';
|
|
4
4
|
export declare const entities: (typeof BankStatement)[];
|
|
5
|
-
export declare const resolvers: (typeof
|
|
5
|
+
export declare const resolvers: (typeof BankStatementQuery | typeof BankImportMutation)[];
|
|
6
6
|
export declare const subscribers: any[];
|
|
@@ -2,5 +2,5 @@ import { FinancialInstitution } from './financial-institution';
|
|
|
2
2
|
import { FinancialInstitutionQuery } from './financial-institution-query';
|
|
3
3
|
import { FinancialInstitutionMutation } from './financial-institution-mutation';
|
|
4
4
|
export declare const entities: (typeof FinancialInstitution)[];
|
|
5
|
-
export declare const resolvers: (typeof
|
|
5
|
+
export declare const resolvers: (typeof FinancialInstitutionQuery | typeof FinancialInstitutionMutation)[];
|
|
6
6
|
export declare const subscribers: any[];
|
|
@@ -4,6 +4,6 @@ import { FinancialStatementHistory } from './financial-statement-history';
|
|
|
4
4
|
import { FinancialStatementLineQuery, FinancialStatementQuery } from './financial-statement-query';
|
|
5
5
|
import { FinancialStatementMutation } from './financial-statement-mutation';
|
|
6
6
|
import { FinancialStatementHistoryEntitySubscriber } from './event-subscriber';
|
|
7
|
-
export declare const entities: (typeof
|
|
8
|
-
export declare const resolvers: (typeof
|
|
7
|
+
export declare const entities: (typeof FinancialStatementLine | typeof FinancialStatement | typeof FinancialStatementHistory)[];
|
|
8
|
+
export declare const resolvers: (typeof FinancialStatementQuery | typeof FinancialStatementLineQuery | typeof FinancialStatementMutation)[];
|
|
9
9
|
export declare const subscribers: (typeof FinancialStatementHistoryEntitySubscriber)[];
|
|
@@ -2,5 +2,5 @@ import { FiscalMonth } from './fiscal-month';
|
|
|
2
2
|
import { FiscalMonthQuery } from './fiscal-month-query';
|
|
3
3
|
import { FiscalMonthMutation } from './fiscal-month-mutation';
|
|
4
4
|
export declare const entities: (typeof FiscalMonth)[];
|
|
5
|
-
export declare const resolvers: (typeof
|
|
5
|
+
export declare const resolvers: (typeof FiscalMonthQuery | typeof FiscalMonthMutation)[];
|
|
6
6
|
export declare const subscribers: any[];
|
|
@@ -2,5 +2,5 @@ import { FiscalQuarter } from './fiscal-quarter';
|
|
|
2
2
|
import { FiscalQuarterQuery } from './fiscal-quarter-query';
|
|
3
3
|
import { FiscalQuarterMutation } from './fiscal-quarter-mutation';
|
|
4
4
|
export declare const entities: (typeof FiscalQuarter)[];
|
|
5
|
-
export declare const resolvers: (typeof
|
|
5
|
+
export declare const resolvers: (typeof FiscalQuarterQuery | typeof FiscalQuarterMutation)[];
|
|
6
6
|
export declare const subscribers: any[];
|
|
@@ -2,5 +2,5 @@ import { FiscalYear } from './fiscal-year';
|
|
|
2
2
|
import { FiscalYearQuery } from './fiscal-year-query';
|
|
3
3
|
import { FiscalYearMutation } from './fiscal-year-mutation';
|
|
4
4
|
export declare const entities: (typeof FiscalYear)[];
|
|
5
|
-
export declare const resolvers: (typeof
|
|
5
|
+
export declare const resolvers: (typeof FiscalYearQuery | typeof FiscalYearMutation)[];
|
|
6
6
|
export declare const subscribers: any[];
|
|
@@ -17,17 +17,19 @@ export * from './transaction/transaction-service';
|
|
|
17
17
|
export * from './bank-account/bank-account-service';
|
|
18
18
|
export * from './payment/payment-targets';
|
|
19
19
|
export * from './payment/payment-service';
|
|
20
|
+
export * from './payment/document-targets';
|
|
20
21
|
export * from './financial-statement/financial-statement-line';
|
|
21
22
|
export * from './financial-statement/financial-statement-service';
|
|
22
23
|
export * from './recurring-cash-item/recurring-schedule';
|
|
23
24
|
export * from './recurring-cash-item/recurring-cash-item-service';
|
|
25
|
+
export * from './account/account-match';
|
|
24
26
|
export * from './bank-statement/bank-source';
|
|
25
27
|
export * from './bank-statement/bank-parser';
|
|
26
28
|
export * from './bank-statement/bank-reconcile';
|
|
27
29
|
export * from './bank-statement/bank-import-type';
|
|
28
30
|
export * from './bank-statement/bank-import';
|
|
29
|
-
export declare const entities: (typeof import("./
|
|
31
|
+
export declare const entities: (typeof import("./financial-statement/financial-statement-line").FinancialStatementLine | typeof import("./financial-statement/financial-statement").FinancialStatement | typeof import("./accounting-category/accounting-category").AccountingCategory | typeof import("./account/account").Account | typeof import("./bank-account/bank-account").BankAccount | typeof import("./bank-statement/bank-statement").BankStatement | typeof import("./payment/payment").Payment | typeof import("./transaction/transaction").Transaction | typeof import("./accounting-document/accounting-document").AccountingDocument | typeof import("./recurring-cash-item/recurring-cash-item").RecurringCashItem | typeof import("./financial-institution/financial-institution").FinancialInstitution | typeof import("./fiscal-year/fiscal-year").FiscalYear | typeof import("./financial-statement/financial-statement-history").FinancialStatementHistory | typeof import("./transaction/transaction-history").TransactionHistory)[];
|
|
30
32
|
export declare const subscribers: any[];
|
|
31
33
|
export declare const schema: {
|
|
32
|
-
resolverClasses: (typeof import("./
|
|
34
|
+
resolverClasses: (typeof import("./financial-statement/financial-statement-query").FinancialStatementQuery | typeof import("./financial-statement/financial-statement-query").FinancialStatementLineQuery | typeof import("./financial-statement/financial-statement-mutation").FinancialStatementMutation | typeof import("./accounting-category/accounting-category-query").AccountingCategoryQuery | typeof import("./accounting-category/accounting-category-mutation").AccountingCategoryMutation | typeof import("./transaction/transaction-query").TransactionQuery | typeof import("./transaction/transaction-mutation").TransactionMutation | typeof import("./bank-account/bank-account-query").BankAccountQuery | typeof import("./bank-account/bank-account-mutation").BankAccountMutation | typeof import("./bank-statement/bank-statement-query").BankStatementQuery | typeof import("./bank-statement/bank-import-mutation").BankImportMutation | typeof import("./payment/payment-query").PaymentQuery | typeof import("./payment/payment-mutation").PaymentMutation | typeof import("./account/account-query").AccountQuery | typeof import("./account/account-mutation").AccountMutation | typeof import("./accounting-document/accounting-document-query").AccountingDocumentQuery | typeof import("./accounting-document/accounting-document-mutation").AccountingDocumentMutation | typeof import("./recurring-cash-item/recurring-cash-item-query").RecurringCashItemQuery | typeof import("./recurring-cash-item/recurring-cash-item-mutation").RecurringCashItemMutation | typeof import("./financial-institution/financial-institution-query").FinancialInstitutionQuery | typeof import("./financial-institution/financial-institution-mutation").FinancialInstitutionMutation | typeof import("./fiscal-month/fiscal-month-query").FiscalMonthQuery | typeof import("./fiscal-month/fiscal-month-mutation").FiscalMonthMutation | typeof import("./fiscal-quarter/fiscal-quarter-query").FiscalQuarterQuery | typeof import("./fiscal-quarter/fiscal-quarter-mutation").FiscalQuarterMutation | typeof import("./fiscal-year/fiscal-year-query").FiscalYearQuery | typeof import("./fiscal-year/fiscal-year-mutation").FiscalYearMutation)[];
|
|
33
35
|
};
|
|
@@ -23,10 +23,12 @@ tslib_1.__exportStar(require("./transaction/transaction-service"), exports);
|
|
|
23
23
|
tslib_1.__exportStar(require("./bank-account/bank-account-service"), exports);
|
|
24
24
|
tslib_1.__exportStar(require("./payment/payment-targets"), exports);
|
|
25
25
|
tslib_1.__exportStar(require("./payment/payment-service"), exports);
|
|
26
|
+
tslib_1.__exportStar(require("./payment/document-targets"), exports);
|
|
26
27
|
tslib_1.__exportStar(require("./financial-statement/financial-statement-line"), exports);
|
|
27
28
|
tslib_1.__exportStar(require("./financial-statement/financial-statement-service"), exports);
|
|
28
29
|
tslib_1.__exportStar(require("./recurring-cash-item/recurring-schedule"), exports);
|
|
29
30
|
tslib_1.__exportStar(require("./recurring-cash-item/recurring-cash-item-service"), exports);
|
|
31
|
+
tslib_1.__exportStar(require("./account/account-match"), exports);
|
|
30
32
|
tslib_1.__exportStar(require("./bank-statement/bank-source"), exports);
|
|
31
33
|
tslib_1.__exportStar(require("./bank-statement/bank-parser"), exports);
|
|
32
34
|
tslib_1.__exportStar(require("./bank-statement/bank-reconcile"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,2DAAgC;AAChC,oFAAyD;AACzD,oFAAyD;AACzD,oEAAyC;AACzC,sEAA2C;AAC3C,0EAA+C;AAC/C,4DAAiC;AACjC,4DAAiC;AACjC,oFAAyD;AACzD,oFAAyD;AACzD,wFAA6D;AAC7D,sEAA2C;AAC3C,0EAA+C;AAC/C,oEAAyC;AAEzC,qBAAqB;AACrB,2EAAgD;AAChD,4EAAiD;AACjD,8EAAmD;AACnD,oEAAyC;AACzC,oEAAyC;AACzC,yFAA8D;AAC9D,4FAAiE;AACjE,mFAAwD;AACxD,4FAAiE;AACjE,uEAA4C;AAC5C,uEAA4C;AAC5C,0EAA+C;AAC/C,4EAAiD;AACjD,uEAA4C;AAE5C,mCAAmC;AACnC,+DAI8B;AAC9B,+DAI8B;AAC9B,+CAIsB;AACtB,iDAIuB;AACvB,qDAIyB;AACzB,uCAIkB;AAClB,uCAIkB;AAClB,+DAI8B;AAC9B,+DAI8B;AAC9B,mEAIgC;AAChC,iDAIuB;AACvB,qDAIyB;AACzB,+CAIsB;AAET,QAAA,QAAQ,GAAG;IACtB,GAAG,8BAA0B;IAC7B,GAAG,8BAA0B;IAC7B,GAAG,sBAAmB;IACtB,GAAG,uBAAmB;IACtB,GAAG,yBAAqB;IACxB,GAAG,kBAAe;IAClB,GAAG,kBAAe;IAClB,GAAG,8BAA0B;IAC7B,GAAG,8BAAyB;IAC5B,GAAG,gCAA4B;IAC/B,GAAG,uBAAmB;IACtB,GAAG,yBAAqB;IACxB,GAAG,sBAAkB;CACtB,CAAA;AAEY,QAAA,WAAW,GAAG;IACzB,GAAG,iCAA6B;IAChC,GAAG,iCAA6B;IAChC,GAAG,yBAAsB;IACzB,GAAG,0BAAsB;IACzB,GAAG,4BAAwB;IAC3B,GAAG,qBAAkB;IACrB,GAAG,qBAAkB;IACrB,GAAG,iCAA6B;IAChC,GAAG,iCAA4B;IAC/B,GAAG,mCAA+B;IAClC,GAAG,0BAAsB;IACzB,GAAG,4BAAwB;IAC3B,GAAG,yBAAqB;CACzB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,GAAG,+BAA2B;QAC9B,GAAG,+BAA2B;QAC9B,GAAG,uBAAoB;QACvB,GAAG,wBAAoB;QACvB,GAAG,0BAAsB;QACzB,GAAG,mBAAgB;QACnB,GAAG,mBAAgB;QACnB,GAAG,+BAA2B;QAC9B,GAAG,+BAA0B;QAC7B,GAAG,iCAA6B;QAChC,GAAG,wBAAoB;QACvB,GAAG,0BAAsB;QACzB,GAAG,uBAAmB;KACvB;CACF,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './cash-flow-type'\nexport * from './financial-statement/financial-statement'\nexport * from './accounting-category/accounting-category'\nexport * from './transaction/transaction'\nexport * from './bank-account/bank-account'\nexport * from './bank-statement/bank-statement'\nexport * from './payment/payment'\nexport * from './account/account'\nexport * from './accounting-document/accounting-document'\nexport * from './recurring-cash-item/recurring-cash-item'\nexport * from './financial-institution/financial-institution'\nexport * from './fiscal-month/fiscal-month'\nexport * from './fiscal-quarter/fiscal-quarter'\nexport * from './fiscal-year/fiscal-year'\n\n/* EXPORT SERVICES */\nexport * from './transaction/transaction-period'\nexport * from './transaction/transaction-service'\nexport * from './bank-account/bank-account-service'\nexport * from './payment/payment-targets'\nexport * from './payment/payment-service'\nexport * from './financial-statement/financial-statement-line'\nexport * from './financial-statement/financial-statement-service'\nexport * from './recurring-cash-item/recurring-schedule'\nexport * from './recurring-cash-item/recurring-cash-item-service'\nexport * from './bank-statement/bank-source'\nexport * from './bank-statement/bank-parser'\nexport * from './bank-statement/bank-reconcile'\nexport * from './bank-statement/bank-import-type'\nexport * from './bank-statement/bank-import'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport {\n entities as FinancialStatementEntities,\n resolvers as FinancialStatementResolvers,\n subscribers as FinancialStatementSubscribers\n} from './financial-statement'\nimport {\n entities as AccountingCategoryEntities,\n resolvers as AccountingCategoryResolvers,\n subscribers as AccountingCategorySubscribers\n} from './accounting-category'\nimport {\n entities as TransactionEntities,\n resolvers as TransactionResolvers,\n subscribers as TransactionSubscribers\n} from './transaction'\nimport {\n entities as BankAccountEntities,\n resolvers as BankAccountResolvers,\n subscribers as BankAccountSubscribers\n} from './bank-account'\nimport {\n entities as BankStatementEntities,\n resolvers as BankStatementResolvers,\n subscribers as BankStatementSubscribers\n} from './bank-statement'\nimport {\n entities as PaymentEntities,\n resolvers as PaymentResolvers,\n subscribers as PaymentSubscribers\n} from './payment'\nimport {\n entities as AccountEntities,\n resolvers as AccountResolvers,\n subscribers as AccountSubscribers\n} from './account'\nimport {\n entities as AccountingDocumentEntities,\n resolvers as AccountingDocumentResolvers,\n subscribers as AccountingDocumentSubscribers\n} from './accounting-document'\nimport {\n entities as RecurringCashItemEntities,\n resolvers as RecurringCashItemResolvers,\n subscribers as RecurringCashItemSubscribers\n} from './recurring-cash-item'\nimport {\n entities as FinancialInstitutionEntities,\n resolvers as FinancialInstitutionResolvers,\n subscribers as FinancialInstitutionSubscribers\n} from './financial-institution'\nimport {\n entities as FiscalMonthEntities,\n resolvers as FiscalMonthResolvers,\n subscribers as FiscalMonthSubscribers\n} from './fiscal-month'\nimport {\n entities as FiscalQuarterEntities,\n resolvers as FiscalQuarterResolvers,\n subscribers as FiscalQuarterSubscribers\n} from './fiscal-quarter'\nimport {\n entities as FiscalYearEntities,\n resolvers as FiscalYearResolvers,\n subscribers as FiscalYearSubscribers\n} from './fiscal-year'\n\nexport const entities = [\n ...FinancialStatementEntities,\n ...AccountingCategoryEntities,\n ...TransactionEntities,\n ...BankAccountEntities,\n ...BankStatementEntities,\n ...PaymentEntities,\n ...AccountEntities,\n ...AccountingDocumentEntities,\n ...RecurringCashItemEntities,\n ...FinancialInstitutionEntities,\n ...FiscalMonthEntities,\n ...FiscalQuarterEntities,\n ...FiscalYearEntities\n]\n\nexport const subscribers = [\n ...FinancialStatementSubscribers,\n ...AccountingCategorySubscribers,\n ...TransactionSubscribers,\n ...BankAccountSubscribers,\n ...BankStatementSubscribers,\n ...PaymentSubscribers,\n ...AccountSubscribers,\n ...AccountingDocumentSubscribers,\n ...RecurringCashItemSubscribers,\n ...FinancialInstitutionSubscribers,\n ...FiscalMonthSubscribers,\n ...FiscalQuarterSubscribers,\n ...FiscalYearSubscribers\n]\n\nexport const schema = {\n resolverClasses: [\n ...FinancialStatementResolvers,\n ...AccountingCategoryResolvers,\n ...TransactionResolvers,\n ...BankAccountResolvers,\n ...BankStatementResolvers,\n ...PaymentResolvers,\n ...AccountResolvers,\n ...AccountingDocumentResolvers,\n ...RecurringCashItemResolvers,\n ...FinancialInstitutionResolvers,\n ...FiscalMonthResolvers,\n ...FiscalQuarterResolvers,\n ...FiscalYearResolvers\n ]\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,2DAAgC;AAChC,oFAAyD;AACzD,oFAAyD;AACzD,oEAAyC;AACzC,sEAA2C;AAC3C,0EAA+C;AAC/C,4DAAiC;AACjC,4DAAiC;AACjC,oFAAyD;AACzD,oFAAyD;AACzD,wFAA6D;AAC7D,sEAA2C;AAC3C,0EAA+C;AAC/C,oEAAyC;AAEzC,qBAAqB;AACrB,2EAAgD;AAChD,4EAAiD;AACjD,8EAAmD;AACnD,oEAAyC;AACzC,oEAAyC;AACzC,qEAA0C;AAC1C,yFAA8D;AAC9D,4FAAiE;AACjE,mFAAwD;AACxD,4FAAiE;AACjE,kEAAuC;AACvC,uEAA4C;AAC5C,uEAA4C;AAC5C,0EAA+C;AAC/C,4EAAiD;AACjD,uEAA4C;AAE5C,mCAAmC;AACnC,+DAI8B;AAC9B,+DAI8B;AAC9B,+CAIsB;AACtB,iDAIuB;AACvB,qDAIyB;AACzB,uCAIkB;AAClB,uCAIkB;AAClB,+DAI8B;AAC9B,+DAI8B;AAC9B,mEAIgC;AAChC,iDAIuB;AACvB,qDAIyB;AACzB,+CAIsB;AAET,QAAA,QAAQ,GAAG;IACtB,GAAG,8BAA0B;IAC7B,GAAG,8BAA0B;IAC7B,GAAG,sBAAmB;IACtB,GAAG,uBAAmB;IACtB,GAAG,yBAAqB;IACxB,GAAG,kBAAe;IAClB,GAAG,kBAAe;IAClB,GAAG,8BAA0B;IAC7B,GAAG,8BAAyB;IAC5B,GAAG,gCAA4B;IAC/B,GAAG,uBAAmB;IACtB,GAAG,yBAAqB;IACxB,GAAG,sBAAkB;CACtB,CAAA;AAEY,QAAA,WAAW,GAAG;IACzB,GAAG,iCAA6B;IAChC,GAAG,iCAA6B;IAChC,GAAG,yBAAsB;IACzB,GAAG,0BAAsB;IACzB,GAAG,4BAAwB;IAC3B,GAAG,qBAAkB;IACrB,GAAG,qBAAkB;IACrB,GAAG,iCAA6B;IAChC,GAAG,iCAA4B;IAC/B,GAAG,mCAA+B;IAClC,GAAG,0BAAsB;IACzB,GAAG,4BAAwB;IAC3B,GAAG,yBAAqB;CACzB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,GAAG,+BAA2B;QAC9B,GAAG,+BAA2B;QAC9B,GAAG,uBAAoB;QACvB,GAAG,wBAAoB;QACvB,GAAG,0BAAsB;QACzB,GAAG,mBAAgB;QACnB,GAAG,mBAAgB;QACnB,GAAG,+BAA2B;QAC9B,GAAG,+BAA0B;QAC7B,GAAG,iCAA6B;QAChC,GAAG,wBAAoB;QACvB,GAAG,0BAAsB;QACzB,GAAG,uBAAmB;KACvB;CACF,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './cash-flow-type'\nexport * from './financial-statement/financial-statement'\nexport * from './accounting-category/accounting-category'\nexport * from './transaction/transaction'\nexport * from './bank-account/bank-account'\nexport * from './bank-statement/bank-statement'\nexport * from './payment/payment'\nexport * from './account/account'\nexport * from './accounting-document/accounting-document'\nexport * from './recurring-cash-item/recurring-cash-item'\nexport * from './financial-institution/financial-institution'\nexport * from './fiscal-month/fiscal-month'\nexport * from './fiscal-quarter/fiscal-quarter'\nexport * from './fiscal-year/fiscal-year'\n\n/* EXPORT SERVICES */\nexport * from './transaction/transaction-period'\nexport * from './transaction/transaction-service'\nexport * from './bank-account/bank-account-service'\nexport * from './payment/payment-targets'\nexport * from './payment/payment-service'\nexport * from './payment/document-targets'\nexport * from './financial-statement/financial-statement-line'\nexport * from './financial-statement/financial-statement-service'\nexport * from './recurring-cash-item/recurring-schedule'\nexport * from './recurring-cash-item/recurring-cash-item-service'\nexport * from './account/account-match'\nexport * from './bank-statement/bank-source'\nexport * from './bank-statement/bank-parser'\nexport * from './bank-statement/bank-reconcile'\nexport * from './bank-statement/bank-import-type'\nexport * from './bank-statement/bank-import'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport {\n entities as FinancialStatementEntities,\n resolvers as FinancialStatementResolvers,\n subscribers as FinancialStatementSubscribers\n} from './financial-statement'\nimport {\n entities as AccountingCategoryEntities,\n resolvers as AccountingCategoryResolvers,\n subscribers as AccountingCategorySubscribers\n} from './accounting-category'\nimport {\n entities as TransactionEntities,\n resolvers as TransactionResolvers,\n subscribers as TransactionSubscribers\n} from './transaction'\nimport {\n entities as BankAccountEntities,\n resolvers as BankAccountResolvers,\n subscribers as BankAccountSubscribers\n} from './bank-account'\nimport {\n entities as BankStatementEntities,\n resolvers as BankStatementResolvers,\n subscribers as BankStatementSubscribers\n} from './bank-statement'\nimport {\n entities as PaymentEntities,\n resolvers as PaymentResolvers,\n subscribers as PaymentSubscribers\n} from './payment'\nimport {\n entities as AccountEntities,\n resolvers as AccountResolvers,\n subscribers as AccountSubscribers\n} from './account'\nimport {\n entities as AccountingDocumentEntities,\n resolvers as AccountingDocumentResolvers,\n subscribers as AccountingDocumentSubscribers\n} from './accounting-document'\nimport {\n entities as RecurringCashItemEntities,\n resolvers as RecurringCashItemResolvers,\n subscribers as RecurringCashItemSubscribers\n} from './recurring-cash-item'\nimport {\n entities as FinancialInstitutionEntities,\n resolvers as FinancialInstitutionResolvers,\n subscribers as FinancialInstitutionSubscribers\n} from './financial-institution'\nimport {\n entities as FiscalMonthEntities,\n resolvers as FiscalMonthResolvers,\n subscribers as FiscalMonthSubscribers\n} from './fiscal-month'\nimport {\n entities as FiscalQuarterEntities,\n resolvers as FiscalQuarterResolvers,\n subscribers as FiscalQuarterSubscribers\n} from './fiscal-quarter'\nimport {\n entities as FiscalYearEntities,\n resolvers as FiscalYearResolvers,\n subscribers as FiscalYearSubscribers\n} from './fiscal-year'\n\nexport const entities = [\n ...FinancialStatementEntities,\n ...AccountingCategoryEntities,\n ...TransactionEntities,\n ...BankAccountEntities,\n ...BankStatementEntities,\n ...PaymentEntities,\n ...AccountEntities,\n ...AccountingDocumentEntities,\n ...RecurringCashItemEntities,\n ...FinancialInstitutionEntities,\n ...FiscalMonthEntities,\n ...FiscalQuarterEntities,\n ...FiscalYearEntities\n]\n\nexport const subscribers = [\n ...FinancialStatementSubscribers,\n ...AccountingCategorySubscribers,\n ...TransactionSubscribers,\n ...BankAccountSubscribers,\n ...BankStatementSubscribers,\n ...PaymentSubscribers,\n ...AccountSubscribers,\n ...AccountingDocumentSubscribers,\n ...RecurringCashItemSubscribers,\n ...FinancialInstitutionSubscribers,\n ...FiscalMonthSubscribers,\n ...FiscalQuarterSubscribers,\n ...FiscalYearSubscribers\n]\n\nexport const schema = {\n resolverClasses: [\n ...FinancialStatementResolvers,\n ...AccountingCategoryResolvers,\n ...TransactionResolvers,\n ...BankAccountResolvers,\n ...BankStatementResolvers,\n ...PaymentResolvers,\n ...AccountResolvers,\n ...AccountingDocumentResolvers,\n ...RecurringCashItemResolvers,\n ...FinancialInstitutionResolvers,\n ...FiscalMonthResolvers,\n ...FiscalQuarterResolvers,\n ...FiscalYearResolvers\n ]\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { EntityManager } from 'typeorm';
|
|
2
|
+
import { Payment } from './payment';
|
|
3
|
+
export declare const INVOICE_TARGET = "Invoice";
|
|
4
|
+
export declare const TAX_INVOICE_TARGET = "TaxInvoice";
|
|
5
|
+
/** What a payment pays of its document, in the document's currency. */
|
|
6
|
+
export declare const paidOfDocument: (payment: Payment) => number;
|
|
7
|
+
/** The payments on some documents of a kind, or of some bank lines, with their line. */
|
|
8
|
+
export declare function documentPaymentsOf(tx: EntityManager, domain: {
|
|
9
|
+
id: string;
|
|
10
|
+
}, targetType: string, of: {
|
|
11
|
+
documentIds?: string[];
|
|
12
|
+
transactionIds?: string[];
|
|
13
|
+
}, options?: {
|
|
14
|
+
withDeleted?: boolean;
|
|
15
|
+
relations?: string[];
|
|
16
|
+
}): Promise<Payment[]>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.paidOfDocument = exports.TAX_INVOICE_TARGET = exports.INVOICE_TARGET = void 0;
|
|
4
|
+
exports.documentPaymentsOf = documentPaymentsOf;
|
|
5
|
+
const typeorm_1 = require("typeorm");
|
|
6
|
+
const invoice_1 = require("@things-factory/invoice");
|
|
7
|
+
const shell_1 = require("@things-factory/shell");
|
|
8
|
+
const tax_invoice_1 = require("@things-factory/tax-invoice");
|
|
9
|
+
const transaction_1 = require("../transaction/transaction");
|
|
10
|
+
const payment_1 = require("./payment");
|
|
11
|
+
const payment_targets_1 = require("./payment-targets");
|
|
12
|
+
/*
|
|
13
|
+
* The bank lines that paid a commercial invoice or a tax invoice (ADR-0122). Both documents evidence the money - a
|
|
14
|
+
* contract round may settle it too - so they are EVIDENCE, each kind on its own: a local-L/C export's zero-rated tax
|
|
15
|
+
* invoice and its commercial invoice each evidence the whole deposit. The rules are the same in every application,
|
|
16
|
+
* so accounting registers both kinds; the document modules do not know accounting.
|
|
17
|
+
*/
|
|
18
|
+
exports.INVOICE_TARGET = 'Invoice';
|
|
19
|
+
exports.TAX_INVOICE_TARGET = 'TaxInvoice';
|
|
20
|
+
/** What a payment pays of its document, in the document's currency. */
|
|
21
|
+
const paidOfDocument = (payment) => Number(payment.targetAmount ?? payment.amount);
|
|
22
|
+
exports.paidOfDocument = paidOfDocument;
|
|
23
|
+
/** The payments on some documents of a kind, or of some bank lines, with their line. */
|
|
24
|
+
async function documentPaymentsOf(tx, domain, targetType, of, options = {}) {
|
|
25
|
+
return tx.getRepository(payment_1.Payment).find({
|
|
26
|
+
where: {
|
|
27
|
+
domain: { id: domain.id },
|
|
28
|
+
targetType,
|
|
29
|
+
...(of.documentIds ? { targetId: (0, typeorm_1.In)(of.documentIds) } : {}),
|
|
30
|
+
...(of.transactionIds ? { transaction: { id: (0, typeorm_1.In)(of.transactionIds) } } : {})
|
|
31
|
+
},
|
|
32
|
+
withDeleted: options.withDeleted,
|
|
33
|
+
relations: ['transaction', ...(options.relations ?? [])]
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const fits = (amount, currency) => {
|
|
37
|
+
const scale = 10 ** (0, invoice_1.currencyDigits)(currency);
|
|
38
|
+
return Math.abs(amount * scale - Math.round(amount * scale)) <= 0.00001;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* A document is paid by bank lines on its side - money in for a sale, out for a purchase, the other way for a
|
|
42
|
+
* negative one - and by no more than its total, in its own currency. A line in another currency says what it paid of
|
|
43
|
+
* the document (targetAmount) and on what basis (notes); a line in the same currency says nothing more.
|
|
44
|
+
*/
|
|
45
|
+
async function checkDocuments(tx, domain, targetType, documents, codes) {
|
|
46
|
+
const payments = await documentPaymentsOf(tx, domain, targetType, { documentIds: documents.map(one => one.id) });
|
|
47
|
+
for (const document of documents) {
|
|
48
|
+
const ofDocument = payments.filter(one => one.targetId === document.id);
|
|
49
|
+
const incoming = document.sale === document.total >= 0;
|
|
50
|
+
for (const one of ofDocument) {
|
|
51
|
+
const line = one.transaction;
|
|
52
|
+
if (line.category !== transaction_1.TransactionCategory.CONFIRMED || line.type !== (incoming ? 'INCOME' : 'EXPENSE'))
|
|
53
|
+
throw new shell_1.Refusal(codes.side, {}, 'choose a deposit for a sale, a withdrawal for a purchase; the other way for a negative document');
|
|
54
|
+
const lineCurrency = line.currency ?? 'KRW';
|
|
55
|
+
if (lineCurrency === document.currency) {
|
|
56
|
+
if (one.targetAmount !== null && one.targetAmount !== undefined)
|
|
57
|
+
throw new shell_1.Refusal('document-payment-same-currency', {}, 'a line in the document currency says its amount once');
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
if (one.targetAmount === null || one.targetAmount === undefined)
|
|
61
|
+
throw new shell_1.Refusal('document-payment-amount-required', { currency: document.currency }, 'record what the line paid in the document currency');
|
|
62
|
+
if (!one.notes?.trim())
|
|
63
|
+
throw new shell_1.Refusal('document-payment-basis-required', {}, 'record the exchange rate or settlement basis in notes');
|
|
64
|
+
}
|
|
65
|
+
if (!fits(Number(one.amount), lineCurrency) || !fits((0, exports.paidOfDocument)(one), document.currency))
|
|
66
|
+
throw new shell_1.Refusal('document-payment-precision', {}, 'an amount has more decimals than its currency');
|
|
67
|
+
}
|
|
68
|
+
if (ofDocument.reduce((sum, one) => sum + (0, exports.paidOfDocument)(one), 0) > Math.abs(document.total) + 0.00001)
|
|
69
|
+
throw new shell_1.Refusal(codes.over, {}, 'the payments are more than the document');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
(0, payment_targets_1.registerPaymentTarget)(exports.INVOICE_TARGET, {
|
|
73
|
+
purpose: payment_1.PaymentPurpose.EVIDENCE,
|
|
74
|
+
async label(tx, domain, ids) {
|
|
75
|
+
const invoices = await tx.getRepository(invoice_1.Invoice).find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(ids) } });
|
|
76
|
+
return new Map(invoices.map(one => [one.id, [one.number, one.direction === 'SALES' ? one.buyerName : one.supplierName].filter(Boolean).join(' ')]));
|
|
77
|
+
},
|
|
78
|
+
async validate(tx, domain, { targetIds }) {
|
|
79
|
+
const invoices = await tx.getRepository(invoice_1.Invoice).find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(targetIds) } });
|
|
80
|
+
if (invoices.length !== new Set(targetIds).size)
|
|
81
|
+
throw new shell_1.Refusal('invoice-not-found', {}, 'the invoice was not found - reload and try again');
|
|
82
|
+
await checkDocuments(tx, domain, exports.INVOICE_TARGET, invoices.map(one => ({ id: one.id, currency: one.currency, total: Number(one.totalAmount), sale: one.direction === 'SALES' })), { side: 'invoice-payment-side', over: 'invoice-payment-over' });
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
(0, payment_targets_1.registerPaymentTarget)(exports.TAX_INVOICE_TARGET, {
|
|
86
|
+
purpose: payment_1.PaymentPurpose.EVIDENCE,
|
|
87
|
+
async label(tx, domain, ids) {
|
|
88
|
+
const invoices = await tx.getRepository(tax_invoice_1.TaxInvoice).find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(ids) } });
|
|
89
|
+
return new Map(invoices.map(one => [one.id, [one.issueDate, one.category === tax_invoice_1.Category.EXPORT ? one.buyerBusinessName : one.supplierBusinessName, one.approvalNo].filter(Boolean).join(' ')]));
|
|
90
|
+
},
|
|
91
|
+
/* A tax invoice is in won. */
|
|
92
|
+
async validate(tx, domain, { targetIds }) {
|
|
93
|
+
const invoices = await tx.getRepository(tax_invoice_1.TaxInvoice).find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(targetIds) } });
|
|
94
|
+
if (invoices.length !== new Set(targetIds).size)
|
|
95
|
+
throw new shell_1.Refusal('tax-invoice-not-found', {}, 'the tax invoice was not found - reload and try again');
|
|
96
|
+
await checkDocuments(tx, domain, exports.TAX_INVOICE_TARGET, invoices.map(one => ({ id: one.id, currency: 'KRW', total: Number(one.totalAmount), sale: one.category === tax_invoice_1.Category.EXPORT })), { side: 'tax-invoice-payment-side', over: 'tax-invoice-payment-over' });
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
//# sourceMappingURL=document-targets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-targets.js","sourceRoot":"","sources":["../../../server/service/payment/document-targets.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { currencyDigits, Invoice } from '@things-factory/invoice'\nimport { Refusal } from '@things-factory/shell'\nimport { Category, TaxInvoice } from '@things-factory/tax-invoice'\n\nimport { Transaction, TransactionCategory } from '../transaction/transaction'\nimport { Payment, PaymentPurpose } from './payment'\nimport { registerPaymentTarget } from './payment-targets'\n\n/*\n * The bank lines that paid a commercial invoice or a tax invoice (ADR-0122). Both documents evidence the money - a\n * contract round may settle it too - so they are EVIDENCE, each kind on its own: a local-L/C export's zero-rated tax\n * invoice and its commercial invoice each evidence the whole deposit. The rules are the same in every application,\n * so accounting registers both kinds; the document modules do not know accounting.\n */\n\nexport const INVOICE_TARGET = 'Invoice'\nexport const TAX_INVOICE_TARGET = 'TaxInvoice'\n\n/** What a payment pays of its document, in the document's currency. */\nexport const paidOfDocument = (payment: Payment) => Number(payment.targetAmount ?? payment.amount)\n\n/** The payments on some documents of a kind, or of some bank lines, with their line. */\nexport async function documentPaymentsOf(\n tx: EntityManager,\n domain: { id: string },\n targetType: string,\n of: { documentIds?: string[]; transactionIds?: string[] },\n options: { withDeleted?: boolean; relations?: string[] } = {}\n): Promise<Payment[]> {\n return tx.getRepository(Payment).find({\n where: {\n domain: { id: domain.id },\n targetType,\n ...(of.documentIds ? { targetId: In(of.documentIds) } : {}),\n ...(of.transactionIds ? { transaction: { id: In(of.transactionIds) } } : {})\n } as any,\n withDeleted: options.withDeleted,\n relations: ['transaction', ...(options.relations ?? [])]\n })\n}\n\nconst fits = (amount: number, currency: string) => {\n const scale = 10 ** currencyDigits(currency)\n return Math.abs(amount * scale - Math.round(amount * scale)) <= 0.00001\n}\n\n/** A document as its payments are held to it: its currency, its signed total, and whether its money comes in. */\ninterface PaidDocument {\n id: string\n currency: string\n total: number\n /** A sale's money comes in - a negative (corrective) one's goes back out. */\n sale: boolean\n}\n\n/**\n * A document is paid by bank lines on its side - money in for a sale, out for a purchase, the other way for a\n * negative one - and by no more than its total, in its own currency. A line in another currency says what it paid of\n * the document (targetAmount) and on what basis (notes); a line in the same currency says nothing more.\n */\nasync function checkDocuments(tx: EntityManager, domain: { id: string }, targetType: string, documents: PaidDocument[], codes: { side: string; over: string }) {\n const payments = await documentPaymentsOf(tx, domain, targetType, { documentIds: documents.map(one => one.id) })\n for (const document of documents) {\n const ofDocument = payments.filter(one => one.targetId === document.id)\n const incoming = document.sale === document.total >= 0\n for (const one of ofDocument) {\n const line: Transaction = one.transaction\n if (line.category !== TransactionCategory.CONFIRMED || line.type !== (incoming ? 'INCOME' : 'EXPENSE'))\n throw new Refusal(codes.side, {}, 'choose a deposit for a sale, a withdrawal for a purchase; the other way for a negative document')\n const lineCurrency = line.currency ?? 'KRW'\n if (lineCurrency === document.currency) {\n if (one.targetAmount !== null && one.targetAmount !== undefined)\n throw new Refusal('document-payment-same-currency', {}, 'a line in the document currency says its amount once')\n } else {\n if (one.targetAmount === null || one.targetAmount === undefined)\n throw new Refusal('document-payment-amount-required', { currency: document.currency }, 'record what the line paid in the document currency')\n if (!one.notes?.trim()) throw new Refusal('document-payment-basis-required', {}, 'record the exchange rate or settlement basis in notes')\n }\n if (!fits(Number(one.amount), lineCurrency) || !fits(paidOfDocument(one), document.currency))\n throw new Refusal('document-payment-precision', {}, 'an amount has more decimals than its currency')\n }\n if (ofDocument.reduce((sum, one) => sum + paidOfDocument(one), 0) > Math.abs(document.total) + 0.00001)\n throw new Refusal(codes.over, {}, 'the payments are more than the document')\n }\n}\n\nregisterPaymentTarget(INVOICE_TARGET, {\n purpose: PaymentPurpose.EVIDENCE,\n\n async label(tx, domain, ids) {\n const invoices = await tx.getRepository(Invoice).find({ where: { domain: { id: domain.id }, id: In(ids) } as any })\n return new Map(invoices.map(one => [one.id, [one.number, one.direction === 'SALES' ? one.buyerName : one.supplierName].filter(Boolean).join(' ')]))\n },\n\n async validate(tx, domain, { targetIds }) {\n const invoices = await tx.getRepository(Invoice).find({ where: { domain: { id: domain.id }, id: In(targetIds) } as any })\n if (invoices.length !== new Set(targetIds).size) throw new Refusal('invoice-not-found', {}, 'the invoice was not found - reload and try again')\n await checkDocuments(\n tx,\n domain,\n INVOICE_TARGET,\n invoices.map(one => ({ id: one.id, currency: one.currency, total: Number(one.totalAmount), sale: one.direction === 'SALES' })),\n { side: 'invoice-payment-side', over: 'invoice-payment-over' }\n )\n }\n})\n\nregisterPaymentTarget(TAX_INVOICE_TARGET, {\n purpose: PaymentPurpose.EVIDENCE,\n\n async label(tx, domain, ids) {\n const invoices = await tx.getRepository(TaxInvoice).find({ where: { domain: { id: domain.id }, id: In(ids) } as any })\n return new Map(\n invoices.map(one => [one.id, [one.issueDate, one.category === Category.EXPORT ? one.buyerBusinessName : one.supplierBusinessName, one.approvalNo].filter(Boolean).join(' ')])\n )\n },\n\n /* A tax invoice is in won. */\n async validate(tx, domain, { targetIds }) {\n const invoices = await tx.getRepository(TaxInvoice).find({ where: { domain: { id: domain.id }, id: In(targetIds) } as any })\n if (invoices.length !== new Set(targetIds).size) throw new Refusal('tax-invoice-not-found', {}, 'the tax invoice was not found - reload and try again')\n await checkDocuments(\n tx,\n domain,\n TAX_INVOICE_TARGET,\n invoices.map(one => ({ id: one.id, currency: 'KRW', total: Number(one.totalAmount), sale: one.category === Category.EXPORT })),\n { side: 'tax-invoice-payment-side', over: 'tax-invoice-payment-over' }\n )\n }\n})\n"]}
|
|
@@ -144,6 +144,9 @@ async function savePayments(tx, domain, user, patches) {
|
|
|
144
144
|
});
|
|
145
145
|
for (const [targetType, kind] of touched)
|
|
146
146
|
await (0, payment_targets_1.paymentTargetOf)(targetType)?.validate?.(tx, domain, touchedOf(kind));
|
|
147
|
+
const transactionIds = [...new Set([...touched.values()].flatMap(kind => [...kind.transactionIds]))];
|
|
148
|
+
for (const rule of (0, payment_targets_1.paymentRules)())
|
|
149
|
+
await rule(tx, domain, { transactionIds });
|
|
147
150
|
for (const [targetType, kind] of touched)
|
|
148
151
|
await (0, payment_targets_1.paymentTargetOf)(targetType)?.onSaved?.(tx, domain, touchedOf(kind));
|
|
149
152
|
return saved;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payment-service.js","sourceRoot":"","sources":["../../../server/service/payment/payment-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { Refusal } from '@things-factory/shell'\n\nimport { Transaction } from '../transaction/transaction'\nimport { Payment, PaymentMethod, PaymentPurpose } from './payment'\nimport { paymentTargetOf } from './payment-targets'\nimport { PaymentPatch } from './payment-type'\n\n/** Amounts are decimals; a sum within half a cent of the transaction is the transaction. */\nconst CENT = 0.005\n\n/**\n * Within one purpose a transaction pays no more than itself - and for evidence, within each kind of document: the\n * same money is evidenced in full by a tax invoice and by a commercial invoice alike, each telling it its own way.\n * Run after its payments or its amount change.\n */\nexport async function checkPaymentsWithin(\n tx: EntityManager,\n domain: { id: string },\n transactionIds: string[]\n): Promise<void> {\n const ids = [...new Set(transactionIds)]\n if (!ids.length) return\n\n const transactions = await tx\n .getRepository(Transaction)\n .find({ where: { domain: { id: domain.id }, id: In(ids) }, select: ['id', 'amount'] })\n const payments = await tx\n .getRepository(Payment)\n .find({ where: { domain: { id: domain.id }, transaction: { id: In(ids) } }, relations: ['transaction'] })\n const paid = new Map<string, number>()\n for (const payment of payments) {\n const layer = payment.purpose === PaymentPurpose.EVIDENCE ? `${payment.purpose}:${payment.targetType}` : payment.purpose\n const key = `${payment.transaction.id}|${layer}`\n paid.set(key, (paid.get(key) ?? 0) + Number(payment.amount))\n }\n for (const transaction of transactions) {\n for (const [key, sum] of paid) {\n const [id, purpose] = key.split('|')\n if (id !== transaction.id) continue\n const amount = Math.abs(Number(transaction.amount))\n if (sum > amount + CENT) {\n throw new Refusal(\n 'payment-over-transaction',\n { purpose, paid: sum, amount },\n `${purpose}: ${sum} put on a transaction of ${amount}`\n )\n }\n }\n }\n}\n\n/** A patch as the row it writes: the target kind known, the amount more than zero, the purpose the kind's. */\nfunction rowOf(patch: PaymentPatch, isNew: boolean): Partial<Payment> & { transactionId?: string } {\n const row: Record<string, any> = {}\n const transactionId = patch.transaction?.id\n if (isNew && !transactionId)\n throw new Refusal('payment-transaction-required', {}, 'a payment needs the transaction it is a part of')\n if (transactionId) row.transactionId = transactionId\n\n if (patch.targetType !== undefined || isNew) {\n const target = paymentTargetOf(String(patch.targetType ?? ''))\n if (!target)\n throw new Refusal(\n 'payment-target-unknown',\n { targetType: patch.targetType },\n `'${patch.targetType}' is not a kind of target any module registered`\n )\n row.targetType = patch.targetType\n row.purpose = target.purpose\n }\n if (patch.targetId !== undefined || isNew) {\n if (!patch.targetId) throw new Refusal('payment-target-required', {}, 'a payment needs what it paid')\n row.targetId = patch.targetId\n }\n if (patch.amount !== undefined || isNew) {\n const amount = Number(patch.amount)\n if (!(amount > 0)) throw new Refusal('payment-amount-not-positive', {}, 'amount: must be more than zero')\n row.amount = amount\n }\n if (patch.targetAmount !== undefined) {\n const targetAmount = patch.targetAmount === null ? null : Number(patch.targetAmount)\n if (targetAmount !== null && !(targetAmount > 0))\n throw new Refusal('payment-amount-not-positive', {}, 'targetAmount: must be more than zero')\n row.targetAmount = targetAmount\n }\n if (patch.method !== undefined) row.method = patch.method ?? PaymentMethod.PERSON\n if (patch.basis !== undefined) row.basis = patch.basis || null\n if (patch.notes !== undefined) row.notes = patch.notes || null\n return row\n}\n\n/**\n * Apply rows to a domain's payments: `+` (or no id) adds, `M` changes, `-` deletes (softly). Then the general rule\n * (§`checkPaymentsWithin`) and each touched kind's own rules run, in the same database transaction.\n */\nexport async function savePayments(\n tx: EntityManager,\n domain: { id: string },\n user: any,\n patches: PaymentPatch[]\n): Promise<Payment[]> {\n const repo = tx.getRepository(Payment)\n const transactions = tx.getRepository(Transaction)\n const touched = new Map<string, { targetIds: Set<string>; transactionIds: Set<string> }>()\n const touch = (payment: { targetType: string; targetId: string; transactionId?: string }) => {\n const kind = touched.get(payment.targetType) ?? { targetIds: new Set<string>(), transactionIds: new Set<string>() }\n kind.targetIds.add(payment.targetId)\n if (payment.transactionId) kind.transactionIds.add(payment.transactionId)\n touched.set(payment.targetType, kind)\n }\n\n const deletes = patches.filter(one => one.cuFlag === '-' && one.id).map(one => one.id!)\n if (deletes.length) {\n const owned = await repo.find({ where: { domain: { id: domain.id }, id: In(deletes) }, relations: ['transaction'] })\n if (owned.length !== new Set(deletes).size)\n throw new Refusal('payment-not-found', {}, 'a payment was not found - reload and try again')\n for (const one of owned) touch({ ...one, transactionId: one.transaction.id })\n await repo.softDelete({ id: In(deletes) })\n }\n\n const saved: Payment[] = []\n for (const patch of patches.filter(one => one.cuFlag !== '-')) {\n const isNew = !patch.id || patch.cuFlag === '+'\n const { transactionId, ...row } = rowOf(patch, isNew)\n const current = isNew\n ? undefined\n : await repo.findOne({ where: { domain: { id: domain.id }, id: patch.id }, relations: ['transaction'] })\n if (!isNew && !current) throw new Refusal('payment-not-found', {}, 'a payment was not found - reload and try again')\n\n const transactionOf = transactionId ?? current!.transaction.id\n const transaction = await transactions.findOne({ where: { domain: { id: domain.id }, id: transactionOf } })\n if (!transaction)\n throw new Refusal('payment-transaction-not-found', {}, 'the transaction was not found - reload and try again')\n\n const targetType = row.targetType ?? current!.targetType\n const targetId = row.targetId ?? current!.targetId\n const twin = await repo.findOne({\n where: { domain: { id: domain.id }, transaction: { id: transaction.id }, targetType, targetId }\n })\n if (twin && twin.id !== current?.id)\n throw new Refusal(\n 'payment-twice',\n { targetType, targetId },\n 'the transaction already pays this - change that payment instead'\n )\n\n if (current) touch({ ...current, transactionId: current.transaction.id })\n const written = await repo.save({\n ...(current ?? { domain, creator: user }),\n ...row,\n transaction,\n updater: user\n } as Payment)\n touch({ targetType: written.targetType, targetId: written.targetId, transactionId: transaction.id })\n saved.push(written)\n }\n\n await checkPaymentsWithin(\n tx,\n domain,\n [...touched.values()].flatMap(kind => [...kind.transactionIds])\n )\n const touchedOf = (kind: { targetIds: Set<string>; transactionIds: Set<string> }) => ({\n targetIds: [...kind.targetIds],\n transactionIds: [...kind.transactionIds]\n })\n for (const [targetType, kind] of touched) await paymentTargetOf(targetType)?.validate?.(tx, domain, touchedOf(kind))\n for (const [targetType, kind] of touched) await paymentTargetOf(targetType)?.onSaved?.(tx, domain, touchedOf(kind))\n\n return saved\n}\n"]}
|
|
1
|
+
{"version":3,"file":"payment-service.js","sourceRoot":"","sources":["../../../server/service/payment/payment-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { Refusal } from '@things-factory/shell'\n\nimport { Transaction } from '../transaction/transaction'\nimport { Payment, PaymentMethod, PaymentPurpose } from './payment'\nimport { paymentRules, paymentTargetOf } from './payment-targets'\nimport { PaymentPatch } from './payment-type'\n\n/** Amounts are decimals; a sum within half a cent of the transaction is the transaction. */\nconst CENT = 0.005\n\n/**\n * Within one purpose a transaction pays no more than itself - and for evidence, within each kind of document: the\n * same money is evidenced in full by a tax invoice and by a commercial invoice alike, each telling it its own way.\n * Run after its payments or its amount change.\n */\nexport async function checkPaymentsWithin(\n tx: EntityManager,\n domain: { id: string },\n transactionIds: string[]\n): Promise<void> {\n const ids = [...new Set(transactionIds)]\n if (!ids.length) return\n\n const transactions = await tx\n .getRepository(Transaction)\n .find({ where: { domain: { id: domain.id }, id: In(ids) }, select: ['id', 'amount'] })\n const payments = await tx\n .getRepository(Payment)\n .find({ where: { domain: { id: domain.id }, transaction: { id: In(ids) } }, relations: ['transaction'] })\n const paid = new Map<string, number>()\n for (const payment of payments) {\n const layer = payment.purpose === PaymentPurpose.EVIDENCE ? `${payment.purpose}:${payment.targetType}` : payment.purpose\n const key = `${payment.transaction.id}|${layer}`\n paid.set(key, (paid.get(key) ?? 0) + Number(payment.amount))\n }\n for (const transaction of transactions) {\n for (const [key, sum] of paid) {\n const [id, purpose] = key.split('|')\n if (id !== transaction.id) continue\n const amount = Math.abs(Number(transaction.amount))\n if (sum > amount + CENT) {\n throw new Refusal(\n 'payment-over-transaction',\n { purpose, paid: sum, amount },\n `${purpose}: ${sum} put on a transaction of ${amount}`\n )\n }\n }\n }\n}\n\n/** A patch as the row it writes: the target kind known, the amount more than zero, the purpose the kind's. */\nfunction rowOf(patch: PaymentPatch, isNew: boolean): Partial<Payment> & { transactionId?: string } {\n const row: Record<string, any> = {}\n const transactionId = patch.transaction?.id\n if (isNew && !transactionId)\n throw new Refusal('payment-transaction-required', {}, 'a payment needs the transaction it is a part of')\n if (transactionId) row.transactionId = transactionId\n\n if (patch.targetType !== undefined || isNew) {\n const target = paymentTargetOf(String(patch.targetType ?? ''))\n if (!target)\n throw new Refusal(\n 'payment-target-unknown',\n { targetType: patch.targetType },\n `'${patch.targetType}' is not a kind of target any module registered`\n )\n row.targetType = patch.targetType\n row.purpose = target.purpose\n }\n if (patch.targetId !== undefined || isNew) {\n if (!patch.targetId) throw new Refusal('payment-target-required', {}, 'a payment needs what it paid')\n row.targetId = patch.targetId\n }\n if (patch.amount !== undefined || isNew) {\n const amount = Number(patch.amount)\n if (!(amount > 0)) throw new Refusal('payment-amount-not-positive', {}, 'amount: must be more than zero')\n row.amount = amount\n }\n if (patch.targetAmount !== undefined) {\n const targetAmount = patch.targetAmount === null ? null : Number(patch.targetAmount)\n if (targetAmount !== null && !(targetAmount > 0))\n throw new Refusal('payment-amount-not-positive', {}, 'targetAmount: must be more than zero')\n row.targetAmount = targetAmount\n }\n if (patch.method !== undefined) row.method = patch.method ?? PaymentMethod.PERSON\n if (patch.basis !== undefined) row.basis = patch.basis || null\n if (patch.notes !== undefined) row.notes = patch.notes || null\n return row\n}\n\n/**\n * Apply rows to a domain's payments: `+` (or no id) adds, `M` changes, `-` deletes (softly). Then the general rule\n * (§`checkPaymentsWithin`) and each touched kind's own rules run, in the same database transaction.\n */\nexport async function savePayments(\n tx: EntityManager,\n domain: { id: string },\n user: any,\n patches: PaymentPatch[]\n): Promise<Payment[]> {\n const repo = tx.getRepository(Payment)\n const transactions = tx.getRepository(Transaction)\n const touched = new Map<string, { targetIds: Set<string>; transactionIds: Set<string> }>()\n const touch = (payment: { targetType: string; targetId: string; transactionId?: string }) => {\n const kind = touched.get(payment.targetType) ?? { targetIds: new Set<string>(), transactionIds: new Set<string>() }\n kind.targetIds.add(payment.targetId)\n if (payment.transactionId) kind.transactionIds.add(payment.transactionId)\n touched.set(payment.targetType, kind)\n }\n\n const deletes = patches.filter(one => one.cuFlag === '-' && one.id).map(one => one.id!)\n if (deletes.length) {\n const owned = await repo.find({ where: { domain: { id: domain.id }, id: In(deletes) }, relations: ['transaction'] })\n if (owned.length !== new Set(deletes).size)\n throw new Refusal('payment-not-found', {}, 'a payment was not found - reload and try again')\n for (const one of owned) touch({ ...one, transactionId: one.transaction.id })\n await repo.softDelete({ id: In(deletes) })\n }\n\n const saved: Payment[] = []\n for (const patch of patches.filter(one => one.cuFlag !== '-')) {\n const isNew = !patch.id || patch.cuFlag === '+'\n const { transactionId, ...row } = rowOf(patch, isNew)\n const current = isNew\n ? undefined\n : await repo.findOne({ where: { domain: { id: domain.id }, id: patch.id }, relations: ['transaction'] })\n if (!isNew && !current) throw new Refusal('payment-not-found', {}, 'a payment was not found - reload and try again')\n\n const transactionOf = transactionId ?? current!.transaction.id\n const transaction = await transactions.findOne({ where: { domain: { id: domain.id }, id: transactionOf } })\n if (!transaction)\n throw new Refusal('payment-transaction-not-found', {}, 'the transaction was not found - reload and try again')\n\n const targetType = row.targetType ?? current!.targetType\n const targetId = row.targetId ?? current!.targetId\n const twin = await repo.findOne({\n where: { domain: { id: domain.id }, transaction: { id: transaction.id }, targetType, targetId }\n })\n if (twin && twin.id !== current?.id)\n throw new Refusal(\n 'payment-twice',\n { targetType, targetId },\n 'the transaction already pays this - change that payment instead'\n )\n\n if (current) touch({ ...current, transactionId: current.transaction.id })\n const written = await repo.save({\n ...(current ?? { domain, creator: user }),\n ...row,\n transaction,\n updater: user\n } as Payment)\n touch({ targetType: written.targetType, targetId: written.targetId, transactionId: transaction.id })\n saved.push(written)\n }\n\n await checkPaymentsWithin(\n tx,\n domain,\n [...touched.values()].flatMap(kind => [...kind.transactionIds])\n )\n const touchedOf = (kind: { targetIds: Set<string>; transactionIds: Set<string> }) => ({\n targetIds: [...kind.targetIds],\n transactionIds: [...kind.transactionIds]\n })\n for (const [targetType, kind] of touched) await paymentTargetOf(targetType)?.validate?.(tx, domain, touchedOf(kind))\n const transactionIds = [...new Set([...touched.values()].flatMap(kind => [...kind.transactionIds]))]\n for (const rule of paymentRules()) await rule(tx, domain, { transactionIds })\n for (const [targetType, kind] of touched) await paymentTargetOf(targetType)?.onSaved?.(tx, domain, touchedOf(kind))\n\n return saved\n}\n"]}
|
|
@@ -38,4 +38,16 @@ export interface PaymentTarget {
|
|
|
38
38
|
/** Register a kind of target. A kind is registered once; registering it again is a mistake in the app. */
|
|
39
39
|
export declare function registerPaymentTarget(targetType: string, target: PaymentTarget): void;
|
|
40
40
|
export declare function paymentTargetOf(targetType: string): PaymentTarget | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* An application's rule on every payment of a transaction, whatever it paid - run after the target kinds' own, given
|
|
43
|
+
* the transactions a save touched. Throw a `Refusal` to refuse the save. (Office: cash reserved for a refund is not
|
|
44
|
+
* also put on anything else.)
|
|
45
|
+
*/
|
|
46
|
+
export type PaymentRule = (tx: EntityManager, domain: {
|
|
47
|
+
id: string;
|
|
48
|
+
}, touched: {
|
|
49
|
+
transactionIds: string[];
|
|
50
|
+
}) => Promise<void>;
|
|
51
|
+
export declare function registerPaymentRule(rule: PaymentRule): void;
|
|
52
|
+
export declare function paymentRules(): readonly PaymentRule[];
|
|
41
53
|
export declare function registeredPaymentTargets(): string[];
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.registerPaymentTarget = registerPaymentTarget;
|
|
4
4
|
exports.paymentTargetOf = paymentTargetOf;
|
|
5
|
+
exports.registerPaymentRule = registerPaymentRule;
|
|
6
|
+
exports.paymentRules = paymentRules;
|
|
5
7
|
exports.registeredPaymentTargets = registeredPaymentTargets;
|
|
6
8
|
const targets = new Map();
|
|
7
9
|
/** Register a kind of target. A kind is registered once; registering it again is a mistake in the app. */
|
|
@@ -13,6 +15,13 @@ function registerPaymentTarget(targetType, target) {
|
|
|
13
15
|
function paymentTargetOf(targetType) {
|
|
14
16
|
return targets.get(targetType);
|
|
15
17
|
}
|
|
18
|
+
const rules = [];
|
|
19
|
+
function registerPaymentRule(rule) {
|
|
20
|
+
rules.push(rule);
|
|
21
|
+
}
|
|
22
|
+
function paymentRules() {
|
|
23
|
+
return rules;
|
|
24
|
+
}
|
|
16
25
|
function registeredPaymentTargets() {
|
|
17
26
|
return [...targets.keys()];
|
|
18
27
|
}
|