@things-factory/accounting 10.1.238 → 10.1.241

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.
Files changed (43) hide show
  1. package/dist-server/service/index.d.ts +4 -0
  2. package/dist-server/service/index.js +4 -0
  3. package/dist-server/service/index.js.map +1 -1
  4. package/dist-server/service/recurring-cash-item/index.d.ts +3 -1
  5. package/dist-server/service/recurring-cash-item/index.js +3 -1
  6. package/dist-server/service/recurring-cash-item/index.js.map +1 -1
  7. package/dist-server/service/recurring-cash-item/recurring-candidate-service.d.ts +29 -0
  8. package/dist-server/service/recurring-cash-item/recurring-candidate-service.js +117 -0
  9. package/dist-server/service/recurring-cash-item/recurring-candidate-service.js.map +1 -0
  10. package/dist-server/service/recurring-cash-item/recurring-candidates.d.ts +42 -0
  11. package/dist-server/service/recurring-cash-item/recurring-candidates.js +57 -0
  12. package/dist-server/service/recurring-cash-item/recurring-candidates.js.map +1 -0
  13. package/dist-server/service/recurring-cash-item/recurring-cash-item-mutation.d.ts +2 -0
  14. package/dist-server/service/recurring-cash-item/recurring-cash-item-mutation.js +38 -0
  15. package/dist-server/service/recurring-cash-item/recurring-cash-item-mutation.js.map +1 -1
  16. package/dist-server/service/recurring-cash-item/recurring-cash-item-query.d.ts +2 -1
  17. package/dist-server/service/recurring-cash-item/recurring-cash-item-query.js +14 -0
  18. package/dist-server/service/recurring-cash-item/recurring-cash-item-query.js.map +1 -1
  19. package/dist-server/service/recurring-cash-item/recurring-cash-item-service.js +8 -3
  20. package/dist-server/service/recurring-cash-item/recurring-cash-item-service.js.map +1 -1
  21. package/dist-server/service/recurring-cash-item/recurring-cash-item-type.d.ts +15 -0
  22. package/dist-server/service/recurring-cash-item/recurring-cash-item-type.js +60 -2
  23. package/dist-server/service/recurring-cash-item/recurring-cash-item-type.js.map +1 -1
  24. package/dist-server/service/recurring-cash-item/recurring-cash-item.d.ts +2 -1
  25. package/dist-server/service/recurring-cash-item/recurring-cash-item.js +7 -2
  26. package/dist-server/service/recurring-cash-item/recurring-cash-item.js.map +1 -1
  27. package/dist-server/service/recurring-cash-item/recurring-hidden-vendor.d.ts +24 -0
  28. package/dist-server/service/recurring-cash-item/recurring-hidden-vendor.js +84 -0
  29. package/dist-server/service/recurring-cash-item/recurring-hidden-vendor.js.map +1 -0
  30. package/dist-server/service/recurring-cash-item/recurring-schedule.d.ts +12 -1
  31. package/dist-server/service/recurring-cash-item/recurring-schedule.js +28 -0
  32. package/dist-server/service/recurring-cash-item/recurring-schedule.js.map +1 -1
  33. package/dist-server/service/recurring-cash-item/recurring-target.d.ts +2 -0
  34. package/dist-server/service/recurring-cash-item/recurring-target.js +43 -0
  35. package/dist-server/service/recurring-cash-item/recurring-target.js.map +1 -0
  36. package/dist-server/tsconfig.tsbuildinfo +1 -1
  37. package/package.json +10 -10
  38. package/translations/en.json +7 -2
  39. package/translations/es.json +7 -2
  40. package/translations/ja.json +7 -1
  41. package/translations/ko.json +7 -2
  42. package/translations/ms.json +7 -1
  43. package/translations/zh.json +7 -1
@@ -7,10 +7,13 @@ const typeorm_1 = require("typeorm");
7
7
  const fact_change_1 = require("@things-factory/fact-change");
8
8
  const shell_1 = require("@things-factory/shell");
9
9
  const recurring_cash_item_1 = require("./recurring-cash-item");
10
+ const recurring_schedule_1 = require("./recurring-schedule");
10
11
  const MONTH = /^\d{4}-(0[1-9]|1[0-2])$/;
12
+ /** An item's words, as its bank lines are read against them (`recurring-schedule` `wordsOf`). */
13
+ const matchWordsOf = (text) => [...new Set((0, recurring_schedule_1.wordsOf)(text))];
11
14
  /* What moved on a recurring item, field by field - kept by the fact-change module for its 360 (ADR-0177 decision 11). */
12
15
  exports.RECURRING_CASH_ITEM_FACT = 'recurring-cash-item';
13
- const TRACKED = ['name', 'accountId', 'type', 'amount', 'currency', 'dayOfMonth', 'lastDayOfMonth', 'startMonth', 'endMonth', 'counterpartyId', 'active'];
16
+ const TRACKED = ['name', 'accountId', 'type', 'amount', 'matchWords', 'currency', 'dayOfMonth', 'lastDayOfMonth', 'startMonth', 'endMonth', 'counterpartyId', 'active'];
14
17
  const asIds = (row) => ({
15
18
  ...row,
16
19
  ...('account' in row ? { accountId: row.account?.id ?? null } : {}),
@@ -34,8 +37,10 @@ function rowOf(input, current) {
34
37
  !(merged.dayOfMonth >= 1 && merged.dayOfMonth <= 31)) {
35
38
  throw new shell_1.Refusal('day-of-month-out-of-range', {}, 'dayOfMonth: must be 1 to 31');
36
39
  }
37
- if (!current && (row.amount === undefined || row.amount === null))
38
- throw new shell_1.Refusal('recurring-amount-required', {}, 'amount: every month');
40
+ if (row.amount !== undefined && row.amount !== null && !(Number(row.amount) > 0))
41
+ throw new shell_1.Refusal('recurring-amount-invalid', {}, 'amount: more than zero, or empty when it varies');
42
+ if ('matchWords' in row)
43
+ row.matchWords = matchWordsOf(row.matchWords).join(', ') || null;
39
44
  return row;
40
45
  }
41
46
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"recurring-cash-item-service.js","sourceRoot":"","sources":["../../../server/service/recurring-cash-item/recurring-cash-item-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { fieldChanges, readFactChanges, recordFactChanges } from '@things-factory/fact-change'\nimport { Refusal, refuseTiedRows } from '@things-factory/shell'\n\nimport { RecurringCashItem } from './recurring-cash-item'\n\nconst MONTH = /^\\d{4}-(0[1-9]|1[0-2])$/\n\n/* What moved on a recurring item, field by field - kept by the fact-change module for its 360 (ADR-0177 decision 11). */\nexport const RECURRING_CASH_ITEM_FACT = 'recurring-cash-item'\nconst TRACKED = ['name', 'accountId', 'type', 'amount', 'currency', 'dayOfMonth', 'lastDayOfMonth', 'startMonth', 'endMonth', 'counterpartyId', 'active'] as const\nconst asIds = (row: Record<string, any>) => ({\n ...row,\n ...('account' in row ? { accountId: row.account?.id ?? null } : {}),\n ...('counterparty' in row ? { counterpartyId: row.counterparty?.id ?? null } : {})\n})\n\nexport type RecurringCashItemInput = Partial<Omit<RecurringCashItem, 'account' | 'counterparty'>> & {\n id?: string\n cuFlag?: string\n account?: { id: string } | null\n counterparty?: { id: string } | null\n}\n\nfunction rowOf(input: RecurringCashItemInput, current: RecurringCashItem | undefined): Record<string, any> {\n const { id, cuFlag, ...row } = input as Record<string, any>\n const merged = { ...current, ...row }\n if (!String(merged.name ?? '').trim()) throw new Refusal('recurring-name-required', {}, 'name: what it is')\n if (!merged.type) throw new Refusal('transaction-type-required', {}, 'type: money in or money out')\n if (!MONTH.test(String(merged.startMonth ?? '')))\n throw new Refusal('recurring-start-required', {}, 'startMonth: YYYY-MM')\n if (merged.endMonth && !MONTH.test(merged.endMonth))\n throw new Refusal('recurring-end-not-a-month', {}, 'endMonth: YYYY-MM')\n if (merged.endMonth && merged.endMonth < merged.startMonth)\n throw new Refusal('recurring-end-before-start', {}, 'endMonth: before startMonth')\n if (\n merged.dayOfMonth !== null &&\n merged.dayOfMonth !== undefined &&\n !(merged.dayOfMonth >= 1 && merged.dayOfMonth <= 31)\n ) {\n throw new Refusal('day-of-month-out-of-range', {}, 'dayOfMonth: must be 1 to 31')\n }\n if (!current && (row.amount === undefined || row.amount === null))\n throw new Refusal('recurring-amount-required', {}, 'amount: every month')\n return row\n}\n\n/**\n * Apply rows to a domain's recurring items: `+` (or no id) adds, `M` changes, `-` deletes (softly). An item another row\n * still names is not deleted (§`refuseTiedRows`).\n */\nexport async function saveRecurringCashItems(\n tx: EntityManager,\n domain: { id: string },\n user: any,\n inputs: RecurringCashItemInput[]\n): Promise<RecurringCashItem[]> {\n const repo = tx.getRepository(RecurringCashItem)\n const deletes = [...new Set(inputs.filter(one => one.cuFlag === '-' && one.id).map(one => one.id!))]\n if (deletes.length) {\n const found = await repo.find({ where: { domain: { id: domain.id }, id: In(deletes) } })\n if (found.length !== deletes.length)\n throw new Refusal('recurring-not-found', {}, 'a recurring item was not found - reload and try again')\n await refuseTiedRows(tx, RecurringCashItem, deletes)\n await repo.softRemove(found)\n }\n\n const saved: RecurringCashItem[] = []\n for (const input of inputs.filter(one => one.cuFlag !== '-')) {\n const isNew = !input.id || input.cuFlag === '+'\n const current = isNew ? undefined : await repo.findOne({ where: { domain: { id: domain.id }, id: input.id } })\n if (!isNew && !current)\n throw new Refusal('recurring-not-found', {}, 'a recurring item was not found - reload and try again')\n const row = rowOf(input, current)\n if (current)\n await recordFactChanges(tx, domain as any, { factKind: RECURRING_CASH_ITEM_FACT, factName: current.id, changes: fieldChanges(current as any, { ...current, ...asIds(row) }, TRACKED as any) }, { user, source: 'accounting' })\n saved.push(\n await repo.save({\n ...(current ?? { domain, creator: user }),\n ...row,\n updater: user\n } as RecurringCashItem)\n )\n }\n return saved\n}\n\n/** What moved on a recurring item, newest first. */\nexport async function recurringCashItemChanges(tx: EntityManager, domain: { id: string }, id: string) {\n const item = await tx.getRepository(RecurringCashItem).findOne({ where: { domain: { id: domain.id }, id } as any, withDeleted: true })\n if (!item) throw new Refusal('recurring-not-found', {}, 'a recurring item was not found - reload and try again')\n return readFactChanges(tx, domain as any, RECURRING_CASH_ITEM_FACT, item.id)\n}\n"]}
1
+ {"version":3,"file":"recurring-cash-item-service.js","sourceRoot":"","sources":["../../../server/service/recurring-cash-item/recurring-cash-item-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { fieldChanges, readFactChanges, recordFactChanges } from '@things-factory/fact-change'\nimport { Refusal, refuseTiedRows } from '@things-factory/shell'\n\nimport { RecurringCashItem } from './recurring-cash-item'\nimport { wordsOf } from './recurring-schedule'\n\nconst MONTH = /^\\d{4}-(0[1-9]|1[0-2])$/\n\n/** An item's words, as its bank lines are read against them (`recurring-schedule` `wordsOf`). */\nconst matchWordsOf = (text: unknown) => [...new Set(wordsOf(text as string))]\n\n/* What moved on a recurring item, field by field - kept by the fact-change module for its 360 (ADR-0177 decision 11). */\nexport const RECURRING_CASH_ITEM_FACT = 'recurring-cash-item'\nconst TRACKED = ['name', 'accountId', 'type', 'amount', 'matchWords', 'currency', 'dayOfMonth', 'lastDayOfMonth', 'startMonth', 'endMonth', 'counterpartyId', 'active'] as const\nconst asIds = (row: Record<string, any>) => ({\n ...row,\n ...('account' in row ? { accountId: row.account?.id ?? null } : {}),\n ...('counterparty' in row ? { counterpartyId: row.counterparty?.id ?? null } : {})\n})\n\nexport type RecurringCashItemInput = Partial<Omit<RecurringCashItem, 'account' | 'counterparty'>> & {\n id?: string\n cuFlag?: string\n account?: { id: string } | null\n counterparty?: { id: string } | null\n}\n\nfunction rowOf(input: RecurringCashItemInput, current: RecurringCashItem | undefined): Record<string, any> {\n const { id, cuFlag, ...row } = input as Record<string, any>\n const merged = { ...current, ...row }\n if (!String(merged.name ?? '').trim()) throw new Refusal('recurring-name-required', {}, 'name: what it is')\n if (!merged.type) throw new Refusal('transaction-type-required', {}, 'type: money in or money out')\n if (!MONTH.test(String(merged.startMonth ?? '')))\n throw new Refusal('recurring-start-required', {}, 'startMonth: YYYY-MM')\n if (merged.endMonth && !MONTH.test(merged.endMonth))\n throw new Refusal('recurring-end-not-a-month', {}, 'endMonth: YYYY-MM')\n if (merged.endMonth && merged.endMonth < merged.startMonth)\n throw new Refusal('recurring-end-before-start', {}, 'endMonth: before startMonth')\n if (\n merged.dayOfMonth !== null &&\n merged.dayOfMonth !== undefined &&\n !(merged.dayOfMonth >= 1 && merged.dayOfMonth <= 31)\n ) {\n throw new Refusal('day-of-month-out-of-range', {}, 'dayOfMonth: must be 1 to 31')\n }\n if (row.amount !== undefined && row.amount !== null && !(Number(row.amount) > 0))\n throw new Refusal('recurring-amount-invalid', {}, 'amount: more than zero, or empty when it varies')\n if ('matchWords' in row) row.matchWords = matchWordsOf(row.matchWords).join(', ') || null\n return row\n}\n\n/**\n * Apply rows to a domain's recurring items: `+` (or no id) adds, `M` changes, `-` deletes (softly). An item another row\n * still names is not deleted (§`refuseTiedRows`).\n */\nexport async function saveRecurringCashItems(\n tx: EntityManager,\n domain: { id: string },\n user: any,\n inputs: RecurringCashItemInput[]\n): Promise<RecurringCashItem[]> {\n const repo = tx.getRepository(RecurringCashItem)\n const deletes = [...new Set(inputs.filter(one => one.cuFlag === '-' && one.id).map(one => one.id!))]\n if (deletes.length) {\n const found = await repo.find({ where: { domain: { id: domain.id }, id: In(deletes) } })\n if (found.length !== deletes.length)\n throw new Refusal('recurring-not-found', {}, 'a recurring item was not found - reload and try again')\n await refuseTiedRows(tx, RecurringCashItem, deletes)\n await repo.softRemove(found)\n }\n\n const saved: RecurringCashItem[] = []\n for (const input of inputs.filter(one => one.cuFlag !== '-')) {\n const isNew = !input.id || input.cuFlag === '+'\n const current = isNew ? undefined : await repo.findOne({ where: { domain: { id: domain.id }, id: input.id } })\n if (!isNew && !current)\n throw new Refusal('recurring-not-found', {}, 'a recurring item was not found - reload and try again')\n const row = rowOf(input, current)\n if (current)\n await recordFactChanges(tx, domain as any, { factKind: RECURRING_CASH_ITEM_FACT, factName: current.id, changes: fieldChanges(current as any, { ...current, ...asIds(row) }, TRACKED as any) }, { user, source: 'accounting' })\n saved.push(\n await repo.save({\n ...(current ?? { domain, creator: user }),\n ...row,\n updater: user\n } as RecurringCashItem)\n )\n }\n return saved\n}\n\n/** What moved on a recurring item, newest first. */\nexport async function recurringCashItemChanges(tx: EntityManager, domain: { id: string }, id: string) {\n const item = await tx.getRepository(RecurringCashItem).findOne({ where: { domain: { id: domain.id }, id } as any, withDeleted: true })\n if (!item) throw new Refusal('recurring-not-found', {}, 'a recurring item was not found - reload and try again')\n return readFactChanges(tx, domain as any, RECURRING_CASH_ITEM_FACT, item.id)\n}\n"]}
@@ -7,6 +7,7 @@ export declare class RecurringCashItemPatch {
7
7
  account?: ObjectRef;
8
8
  type?: CashFlowType;
9
9
  amount?: number;
10
+ matchWords?: string;
10
11
  currency?: string;
11
12
  dayOfMonth?: number;
12
13
  lastDayOfMonth?: boolean;
@@ -21,3 +22,17 @@ export declare class RecurringCashItemList {
21
22
  items: RecurringCashItem[];
22
23
  total: number;
23
24
  }
25
+ export declare class RecurringCandidate {
26
+ key: string;
27
+ counterpartyId?: string;
28
+ counterpartyName: string;
29
+ months: number;
30
+ average: number;
31
+ firstDate: string;
32
+ lastDate: string;
33
+ lastAmount: number;
34
+ currency: string;
35
+ subjectId?: string;
36
+ dayOfMonth?: number;
37
+ hidden: boolean;
38
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RecurringCashItemList = exports.RecurringCashItemPatch = void 0;
3
+ exports.RecurringCandidate = exports.RecurringCashItemList = exports.RecurringCashItemPatch = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
6
  const shell_1 = require("@things-factory/shell");
@@ -26,9 +26,13 @@ tslib_1.__decorate([
26
26
  tslib_1.__metadata("design:type", String)
27
27
  ], RecurringCashItemPatch.prototype, "type", void 0);
28
28
  tslib_1.__decorate([
29
- (0, type_graphql_1.Field)(type => type_graphql_1.Float, { nullable: true, description: 'Amount every month' }),
29
+ (0, type_graphql_1.Field)(type => type_graphql_1.Float, { nullable: true, description: 'Amount every month; empty when it varies' }),
30
30
  tslib_1.__metadata("design:type", Number)
31
31
  ], RecurringCashItemPatch.prototype, "amount", void 0);
32
+ tslib_1.__decorate([
33
+ (0, type_graphql_1.Field)({ nullable: true, description: "Words its bank lines' descriptions hold, comma separated" }),
34
+ tslib_1.__metadata("design:type", String)
35
+ ], RecurringCashItemPatch.prototype, "matchWords", void 0);
32
36
  tslib_1.__decorate([
33
37
  (0, type_graphql_1.Field)({ nullable: true, description: 'ISO 4217 code' }),
34
38
  tslib_1.__metadata("design:type", String)
@@ -82,4 +86,58 @@ tslib_1.__decorate([
82
86
  exports.RecurringCashItemList = RecurringCashItemList = tslib_1.__decorate([
83
87
  (0, type_graphql_1.ObjectType)({ description: 'A page of recurring items' })
84
88
  ], RecurringCashItemList);
89
+ let RecurringCandidate = class RecurringCandidate {
90
+ };
91
+ exports.RecurringCandidate = RecurringCandidate;
92
+ tslib_1.__decorate([
93
+ (0, type_graphql_1.Field)({ description: 'Whom it is - the counterparty id, or name: and the written name for lines no counterparty is linked to' }),
94
+ tslib_1.__metadata("design:type", String)
95
+ ], RecurringCandidate.prototype, "key", void 0);
96
+ tslib_1.__decorate([
97
+ (0, type_graphql_1.Field)({ nullable: true, description: 'The vendor - a counterparty id; empty when the lines only name it' }),
98
+ tslib_1.__metadata("design:type", String)
99
+ ], RecurringCandidate.prototype, "counterpartyId", void 0);
100
+ tslib_1.__decorate([
101
+ (0, type_graphql_1.Field)({ description: "The vendor's name - the counterparty's, or the one written on the lines" }),
102
+ tslib_1.__metadata("design:type", String)
103
+ ], RecurringCandidate.prototype, "counterpartyName", void 0);
104
+ tslib_1.__decorate([
105
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'The months of the last twelve money went out to it in' }),
106
+ tslib_1.__metadata("design:type", Number)
107
+ ], RecurringCandidate.prototype, "months", void 0);
108
+ tslib_1.__decorate([
109
+ (0, type_graphql_1.Field)(type => type_graphql_1.Float, { description: 'What went out a month, on average over those months' }),
110
+ tslib_1.__metadata("design:type", Number)
111
+ ], RecurringCandidate.prototype, "average", void 0);
112
+ tslib_1.__decorate([
113
+ (0, type_graphql_1.Field)({ description: 'The first day money went out to it in the window - YYYY-MM-DD' }),
114
+ tslib_1.__metadata("design:type", String)
115
+ ], RecurringCandidate.prototype, "firstDate", void 0);
116
+ tslib_1.__decorate([
117
+ (0, type_graphql_1.Field)({ description: 'The last day money went out to it - YYYY-MM-DD' }),
118
+ tslib_1.__metadata("design:type", String)
119
+ ], RecurringCandidate.prototype, "lastDate", void 0);
120
+ tslib_1.__decorate([
121
+ (0, type_graphql_1.Field)(type => type_graphql_1.Float, { description: 'How much went out that day' }),
122
+ tslib_1.__metadata("design:type", Number)
123
+ ], RecurringCandidate.prototype, "lastAmount", void 0);
124
+ tslib_1.__decorate([
125
+ (0, type_graphql_1.Field)({ description: 'The currency' }),
126
+ tslib_1.__metadata("design:type", String)
127
+ ], RecurringCandidate.prototype, "currency", void 0);
128
+ tslib_1.__decorate([
129
+ (0, type_graphql_1.Field)({ nullable: true, description: 'The account subject it is booked to most - an id' }),
130
+ tslib_1.__metadata("design:type", String)
131
+ ], RecurringCandidate.prototype, "subjectId", void 0);
132
+ tslib_1.__decorate([
133
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'The day of the month it went out on most; 31 for the last day' }),
134
+ tslib_1.__metadata("design:type", Number)
135
+ ], RecurringCandidate.prototype, "dayOfMonth", void 0);
136
+ tslib_1.__decorate([
137
+ (0, type_graphql_1.Field)({ description: 'Whether a keeper hid it from the candidates' }),
138
+ tslib_1.__metadata("design:type", Boolean)
139
+ ], RecurringCandidate.prototype, "hidden", void 0);
140
+ exports.RecurringCandidate = RecurringCandidate = tslib_1.__decorate([
141
+ (0, type_graphql_1.ObjectType)({ description: 'A vendor the books show paid month after month that is no recurring item yet (ADR-0177 amendment 7-5)' })
142
+ ], RecurringCandidate);
85
143
  //# sourceMappingURL=recurring-cash-item-type.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"recurring-cash-item-type.js","sourceRoot":"","sources":["../../../server/service/recurring-cash-item/recurring-cash-item-type.ts"],"names":[],"mappings":";;;;AAAA,+CAA2E;AAE3E,iDAAiD;AAEjD,sDAAgD;AAChD,+DAAyD;AAGlD,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;CA0ClC,CAAA;AA1CY,wDAAsB;AAEjC;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;;kDACxE;AAGX;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;;oDACxC;AAGb;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC;sCACvF,iBAAS;uDAAA;AAGnB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,6BAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;;oDACnE;AAGnB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAC;;sDAC7D;AAGf;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;;wDACvC;AAGjB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;;0DACzC;AAGnB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;;8DACvD;AAGxB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;;0DAC7C;AAGnB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sBAAsB,EAAE,CAAC;;wDAC9C;AAGjB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;sCACjF,iBAAS;4DAAA;AAGxB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;;sDAC/D;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;;qDAClC;AAGd;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;;sDACxD;iCAzCJ,sBAAsB;IADlC,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,sEAAsE,EAAE,CAAC;GACtF,sBAAsB,CA0ClC;AAGM,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;CAMjC,CAAA;AANY,sDAAqB;AAEhC;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,uCAAiB,CAAC,EAAE,EAAE,WAAW,EAAE,wBAAwB,EAAE,CAAC;;oDACpD;AAG1B;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,2BAA2B,EAAE,CAAC;;oDACpD;gCALF,qBAAqB;IADjC,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,2BAA2B,EAAE,CAAC;GAC5C,qBAAqB,CAMjC","sourcesContent":["import { ObjectType, Field, Float, InputType, Int, ID } from 'type-graphql'\n\nimport { ObjectRef } from '@things-factory/shell'\n\nimport { CashFlowType } from '../cash-flow-type'\nimport { RecurringCashItem } from './recurring-cash-item'\n\n@InputType({ description: 'A recurring item, or a grid row: cuFlag + adds, M changes, - deletes' })\nexport class RecurringCashItemPatch {\n @Field(type => ID, { nullable: true, description: 'The item to change or delete' })\n id?: string\n\n @Field({ nullable: true, description: 'What it is' })\n name?: string\n\n @Field(type => ObjectRef, { nullable: true, description: 'The account subject it is booked to' })\n account?: ObjectRef\n\n @Field(type => CashFlowType, { nullable: true, description: 'Money in or money out' })\n type?: CashFlowType\n\n @Field(type => Float, { nullable: true, description: 'Amount every month' })\n amount?: number\n\n @Field({ nullable: true, description: 'ISO 4217 code' })\n currency?: string\n\n @Field(type => Int, { nullable: true, description: '1-31' })\n dayOfMonth?: number\n\n @Field({ nullable: true, description: 'Falls on the last day of every month' })\n lastDayOfMonth?: boolean\n\n @Field({ nullable: true, description: 'First month - YYYY-MM' })\n startMonth?: string\n\n @Field({ nullable: true, description: 'Last month - YYYY-MM' })\n endMonth?: string\n\n @Field(type => ObjectRef, { nullable: true, description: 'Who it is paid to or received from' })\n counterparty?: ObjectRef\n\n @Field({ nullable: true, description: 'Whether it still falls in its months' })\n active?: boolean\n\n @Field({ nullable: true, description: 'Notes' })\n notes?: string\n\n @Field({ nullable: true, description: '+ adds, M changes, - deletes' })\n cuFlag?: string\n}\n\n@ObjectType({ description: 'A page of recurring items' })\nexport class RecurringCashItemList {\n @Field(type => [RecurringCashItem], { description: 'The items on this page' })\n items: RecurringCashItem[]\n\n @Field(type => Int, { description: 'How many there are in all' })\n total: number\n}\n"]}
1
+ {"version":3,"file":"recurring-cash-item-type.js","sourceRoot":"","sources":["../../../server/service/recurring-cash-item/recurring-cash-item-type.ts"],"names":[],"mappings":"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","sourcesContent":["import { ObjectType, Field, Float, InputType, Int, ID } from 'type-graphql'\n\nimport { ObjectRef } from '@things-factory/shell'\n\nimport { CashFlowType } from '../cash-flow-type'\nimport { RecurringCashItem } from './recurring-cash-item'\n\n@InputType({ description: 'A recurring item, or a grid row: cuFlag + adds, M changes, - deletes' })\nexport class RecurringCashItemPatch {\n @Field(type => ID, { nullable: true, description: 'The item to change or delete' })\n id?: string\n\n @Field({ nullable: true, description: 'What it is' })\n name?: string\n\n @Field(type => ObjectRef, { nullable: true, description: 'The account subject it is booked to' })\n account?: ObjectRef\n\n @Field(type => CashFlowType, { nullable: true, description: 'Money in or money out' })\n type?: CashFlowType\n\n @Field(type => Float, { nullable: true, description: 'Amount every month; empty when it varies' })\n amount?: number\n\n @Field({ nullable: true, description: \"Words its bank lines' descriptions hold, comma separated\" })\n matchWords?: string\n\n @Field({ nullable: true, description: 'ISO 4217 code' })\n currency?: string\n\n @Field(type => Int, { nullable: true, description: '1-31' })\n dayOfMonth?: number\n\n @Field({ nullable: true, description: 'Falls on the last day of every month' })\n lastDayOfMonth?: boolean\n\n @Field({ nullable: true, description: 'First month - YYYY-MM' })\n startMonth?: string\n\n @Field({ nullable: true, description: 'Last month - YYYY-MM' })\n endMonth?: string\n\n @Field(type => ObjectRef, { nullable: true, description: 'Who it is paid to or received from' })\n counterparty?: ObjectRef\n\n @Field({ nullable: true, description: 'Whether it still falls in its months' })\n active?: boolean\n\n @Field({ nullable: true, description: 'Notes' })\n notes?: string\n\n @Field({ nullable: true, description: '+ adds, M changes, - deletes' })\n cuFlag?: string\n}\n\n@ObjectType({ description: 'A page of recurring items' })\nexport class RecurringCashItemList {\n @Field(type => [RecurringCashItem], { description: 'The items on this page' })\n items: RecurringCashItem[]\n\n @Field(type => Int, { description: 'How many there are in all' })\n total: number\n}\n\n@ObjectType({ description: 'A vendor the books show paid month after month that is no recurring item yet (ADR-0177 amendment 7-5)' })\nexport class RecurringCandidate {\n @Field({ description: 'Whom it is - the counterparty id, or name: and the written name for lines no counterparty is linked to' }) key: string\n @Field({ nullable: true, description: 'The vendor - a counterparty id; empty when the lines only name it' }) counterpartyId?: string\n @Field({ description: \"The vendor's name - the counterparty's, or the one written on the lines\" }) counterpartyName: string\n @Field(type => Int, { description: 'The months of the last twelve money went out to it in' }) months: number\n @Field(type => Float, { description: 'What went out a month, on average over those months' }) average: number\n @Field({ description: 'The first day money went out to it in the window - YYYY-MM-DD' }) firstDate: string\n @Field({ description: 'The last day money went out to it - YYYY-MM-DD' }) lastDate: string\n @Field(type => Float, { description: 'How much went out that day' }) lastAmount: number\n @Field({ description: 'The currency' }) currency: string\n @Field({ nullable: true, description: 'The account subject it is booked to most - an id' }) subjectId?: string\n @Field(type => Int, { nullable: true, description: 'The day of the month it went out on most; 31 for the last day' }) dayOfMonth?: number\n @Field({ description: 'Whether a keeper hid it from the candidates' }) hidden: boolean\n}\n"]}
@@ -15,7 +15,8 @@ export declare class RecurringCashItem {
15
15
  account?: Account;
16
16
  accountId?: string;
17
17
  type: CashFlowType;
18
- amount: number;
18
+ amount?: number | null;
19
+ matchWords?: string;
19
20
  currency: string;
20
21
  dayOfMonth?: number;
21
22
  lastDayOfMonth: boolean;
@@ -50,10 +50,15 @@ tslib_1.__decorate([
50
50
  tslib_1.__metadata("design:type", String)
51
51
  ], RecurringCashItem.prototype, "type", void 0);
52
52
  tslib_1.__decorate([
53
- (0, typeorm_1.Column)((0, shell_1.moneyColumn)()),
54
- (0, type_graphql_1.Field)(type => type_graphql_1.Float, { description: 'Amount every month' }),
53
+ (0, typeorm_1.Column)((0, shell_1.moneyColumn)({ nullable: true })),
54
+ (0, type_graphql_1.Field)(type => type_graphql_1.Float, { nullable: true, description: 'Amount every month; empty when it varies - then expected at the average of its latest months' }),
55
55
  tslib_1.__metadata("design:type", Number)
56
56
  ], RecurringCashItem.prototype, "amount", void 0);
57
+ tslib_1.__decorate([
58
+ (0, typeorm_1.Column)({ type: 'text', nullable: true }),
59
+ (0, type_graphql_1.Field)({ nullable: true, description: "Words its bank lines' descriptions hold, comma separated - for items of one counterparty" }),
60
+ tslib_1.__metadata("design:type", String)
61
+ ], RecurringCashItem.prototype, "matchWords", void 0);
57
62
  tslib_1.__decorate([
58
63
  (0, typeorm_1.Column)({ default: 'KRW' }),
59
64
  (0, type_graphql_1.Field)({ description: 'ISO 4217 code' }),
@@ -1 +1 @@
1
- {"version":3,"file":"recurring-cash-item.js","sourceRoot":"","sources":["../../../server/service/recurring-cash-item/recurring-cash-item.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n DeleteDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Field, Float, ID, Int, ObjectType } from 'type-graphql'\n\nimport { Domain, moneyColumn } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\n\nimport { Account } from '../account/account'\nimport { CashFlowType } from '../cash-flow-type'\n\n/**\n * Money that comes in or goes out every month on a known day - rent, a subscription, a loan's interest. An app reads\n * the months it falls in as money still to come (§`recurring-schedule`); nothing is written for them.\n */\n@Entity()\n@Index('ix_recurring_cash_item_0', (item: RecurringCashItem) => [item.domain, item.active])\n@ObjectType({ description: 'Money that comes in or goes out every month on a known day' })\nexport class RecurringCashItem {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain whose item it is' })\n domain?: Domain\n\n @RelationId((item: RecurringCashItem) => item.domain)\n domainId?: string\n\n @Column()\n @Field({ description: 'What it is' })\n name: string\n\n @ManyToOne(type => Account, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => Account, { nullable: true, description: 'The account subject it is booked to' })\n account?: Account\n\n @RelationId((item: RecurringCashItem) => item.account)\n accountId?: string\n\n @Column({ default: CashFlowType.EXPENSE })\n @Field(type => CashFlowType, { description: 'Money in or money out' })\n type: CashFlowType\n\n @Column(moneyColumn())\n @Field(type => Float, { description: 'Amount every month' })\n amount: number\n\n @Column({ default: 'KRW' })\n @Field({ description: 'ISO 4217 code' })\n currency: string\n\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, {\n nullable: true,\n description: \"1-31; a day past the month's end is its last day. Empty with lastDayOfMonth\"\n })\n dayOfMonth?: number\n\n @Column({ default: false })\n @Field({ description: 'Falls on the last day of every month' })\n lastDayOfMonth: boolean\n\n @Column()\n @Field({ description: 'First month - YYYY-MM' })\n startMonth: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Last month; open-ended when empty - YYYY-MM' })\n endMonth?: string\n\n @ManyToOne(type => Counterparty, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => Counterparty, { nullable: true, description: 'Who it is paid to or received from' })\n counterparty?: Counterparty\n\n @RelationId((item: RecurringCashItem) => item.counterparty)\n counterpartyId?: string\n\n @Column({ default: true })\n @Field({ description: 'An inactive item falls in no more months' })\n active: boolean\n\n @Column({ type: 'text', nullable: true })\n @Field({ nullable: true, description: 'Notes' })\n notes?: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When it was created' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When it last changed' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'When it was deleted' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who created it' })\n creator?: User\n\n @RelationId((item: RecurringCashItem) => item.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who last changed it' })\n updater?: User\n\n @RelationId((item: RecurringCashItem) => item.updater)\n updaterId?: string\n}\n"]}
1
+ {"version":3,"file":"recurring-cash-item.js","sourceRoot":"","sources":["../../../server/service/recurring-cash-item/recurring-cash-item.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n DeleteDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Field, Float, ID, Int, ObjectType } from 'type-graphql'\n\nimport { Domain, moneyColumn } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\n\nimport { Account } from '../account/account'\nimport { CashFlowType } from '../cash-flow-type'\n\n/**\n * Money that comes in or goes out every month on a known day - rent, a subscription, a loan's interest. An app reads\n * the months it falls in as money still to come (§`recurring-schedule`); nothing is written for them.\n */\n@Entity()\n@Index('ix_recurring_cash_item_0', (item: RecurringCashItem) => [item.domain, item.active])\n@ObjectType({ description: 'Money that comes in or goes out every month on a known day' })\nexport class RecurringCashItem {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain whose item it is' })\n domain?: Domain\n\n @RelationId((item: RecurringCashItem) => item.domain)\n domainId?: string\n\n @Column()\n @Field({ description: 'What it is' })\n name: string\n\n @ManyToOne(type => Account, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => Account, { nullable: true, description: 'The account subject it is booked to' })\n account?: Account\n\n @RelationId((item: RecurringCashItem) => item.account)\n accountId?: string\n\n @Column({ default: CashFlowType.EXPENSE })\n @Field(type => CashFlowType, { description: 'Money in or money out' })\n type: CashFlowType\n\n /* Empty for an amount that varies - its months are what the bank lines paid, and what is expected their recent average. */\n @Column(moneyColumn({ nullable: true }))\n @Field(type => Float, { nullable: true, description: 'Amount every month; empty when it varies - then expected at the average of its latest months' })\n amount?: number | null\n\n /* What tells its bank lines from the other items of the same counterparty (Azure and Microsoft 365 paid through one reseller). */\n @Column({ type: 'text', nullable: true })\n @Field({ nullable: true, description: \"Words its bank lines' descriptions hold, comma separated - for items of one counterparty\" })\n matchWords?: string\n\n @Column({ default: 'KRW' })\n @Field({ description: 'ISO 4217 code' })\n currency: string\n\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, {\n nullable: true,\n description: \"1-31; a day past the month's end is its last day. Empty with lastDayOfMonth\"\n })\n dayOfMonth?: number\n\n @Column({ default: false })\n @Field({ description: 'Falls on the last day of every month' })\n lastDayOfMonth: boolean\n\n @Column()\n @Field({ description: 'First month - YYYY-MM' })\n startMonth: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Last month; open-ended when empty - YYYY-MM' })\n endMonth?: string\n\n @ManyToOne(type => Counterparty, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => Counterparty, { nullable: true, description: 'Who it is paid to or received from' })\n counterparty?: Counterparty\n\n @RelationId((item: RecurringCashItem) => item.counterparty)\n counterpartyId?: string\n\n @Column({ default: true })\n @Field({ description: 'An inactive item falls in no more months' })\n active: boolean\n\n @Column({ type: 'text', nullable: true })\n @Field({ nullable: true, description: 'Notes' })\n notes?: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When it was created' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When it last changed' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'When it was deleted' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who created it' })\n creator?: User\n\n @RelationId((item: RecurringCashItem) => item.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who last changed it' })\n updater?: User\n\n @RelationId((item: RecurringCashItem) => item.updater)\n updaterId?: string\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import { Domain } from '@things-factory/shell';
2
+ import { User } from '@things-factory/auth-base';
3
+ import { Counterparty } from '@things-factory/biz-base';
4
+ /**
5
+ * Whom a keeper said is no recurring item - paid month after month but by chance - so the books stop offering it
6
+ * (ADR-0177 개정 1, 7-5): a counterparty, or the name written on lines no counterparty is linked to. Shown again, the
7
+ * row goes.
8
+ */
9
+ export declare class RecurringHiddenVendor {
10
+ readonly id: string;
11
+ domain?: Domain;
12
+ domainId?: string;
13
+ counterparty?: Counterparty;
14
+ counterpartyId?: string;
15
+ /** The written name's key (biz-base `nameKey`), for lines no counterparty is linked to. */
16
+ nameKey?: string;
17
+ createdAt?: Date;
18
+ updatedAt?: Date;
19
+ deletedAt?: Date;
20
+ creator?: User;
21
+ creatorId?: string;
22
+ updater?: User;
23
+ updaterId?: string;
24
+ }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RecurringHiddenVendor = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const typeorm_1 = require("typeorm");
6
+ const type_graphql_1 = require("type-graphql");
7
+ const shell_1 = require("@things-factory/shell");
8
+ const auth_base_1 = require("@things-factory/auth-base");
9
+ const biz_base_1 = require("@things-factory/biz-base");
10
+ /**
11
+ * Whom a keeper said is no recurring item - paid month after month but by chance - so the books stop offering it
12
+ * (ADR-0177 개정 1, 7-5): a counterparty, or the name written on lines no counterparty is linked to. Shown again, the
13
+ * row goes.
14
+ */
15
+ let RecurringHiddenVendor = class RecurringHiddenVendor {
16
+ };
17
+ exports.RecurringHiddenVendor = RecurringHiddenVendor;
18
+ tslib_1.__decorate([
19
+ (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
20
+ (0, type_graphql_1.Field)(type => type_graphql_1.ID, { description: 'Unique identifier' }),
21
+ tslib_1.__metadata("design:type", String)
22
+ ], RecurringHiddenVendor.prototype, "id", void 0);
23
+ tslib_1.__decorate([
24
+ (0, typeorm_1.ManyToOne)(type => shell_1.Domain),
25
+ (0, type_graphql_1.Field)(type => shell_1.Domain, { nullable: true, description: 'The domain whose row it is' }),
26
+ tslib_1.__metadata("design:type", shell_1.Domain)
27
+ ], RecurringHiddenVendor.prototype, "domain", void 0);
28
+ tslib_1.__decorate([
29
+ (0, typeorm_1.RelationId)((row) => row.domain),
30
+ tslib_1.__metadata("design:type", String)
31
+ ], RecurringHiddenVendor.prototype, "domainId", void 0);
32
+ tslib_1.__decorate([
33
+ (0, typeorm_1.ManyToOne)(type => biz_base_1.Counterparty, { nullable: true, onDelete: 'CASCADE' }),
34
+ (0, type_graphql_1.Field)(type => biz_base_1.Counterparty, { nullable: true, description: 'The vendor hidden' }),
35
+ tslib_1.__metadata("design:type", biz_base_1.Counterparty)
36
+ ], RecurringHiddenVendor.prototype, "counterparty", void 0);
37
+ tslib_1.__decorate([
38
+ (0, typeorm_1.RelationId)((row) => row.counterparty),
39
+ tslib_1.__metadata("design:type", String)
40
+ ], RecurringHiddenVendor.prototype, "counterpartyId", void 0);
41
+ tslib_1.__decorate([
42
+ (0, typeorm_1.Column)({ nullable: true }),
43
+ (0, type_graphql_1.Field)({ nullable: true, description: "The written name's key, for lines no counterparty is linked to" }),
44
+ tslib_1.__metadata("design:type", String)
45
+ ], RecurringHiddenVendor.prototype, "nameKey", void 0);
46
+ tslib_1.__decorate([
47
+ (0, typeorm_1.CreateDateColumn)(),
48
+ (0, type_graphql_1.Field)({ nullable: true, description: 'When it was hidden' }),
49
+ tslib_1.__metadata("design:type", Date)
50
+ ], RecurringHiddenVendor.prototype, "createdAt", void 0);
51
+ tslib_1.__decorate([
52
+ (0, typeorm_1.UpdateDateColumn)(),
53
+ (0, type_graphql_1.Field)({ nullable: true, description: 'When it last changed' }),
54
+ tslib_1.__metadata("design:type", Date)
55
+ ], RecurringHiddenVendor.prototype, "updatedAt", void 0);
56
+ tslib_1.__decorate([
57
+ (0, typeorm_1.DeleteDateColumn)(),
58
+ (0, type_graphql_1.Field)({ nullable: true, description: 'When it was shown again' }),
59
+ tslib_1.__metadata("design:type", Date)
60
+ ], RecurringHiddenVendor.prototype, "deletedAt", void 0);
61
+ tslib_1.__decorate([
62
+ (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
63
+ (0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true, description: 'Who hid it' }),
64
+ tslib_1.__metadata("design:type", auth_base_1.User)
65
+ ], RecurringHiddenVendor.prototype, "creator", void 0);
66
+ tslib_1.__decorate([
67
+ (0, typeorm_1.RelationId)((row) => row.creator),
68
+ tslib_1.__metadata("design:type", String)
69
+ ], RecurringHiddenVendor.prototype, "creatorId", void 0);
70
+ tslib_1.__decorate([
71
+ (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
72
+ (0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true, description: 'Who last changed it' }),
73
+ tslib_1.__metadata("design:type", auth_base_1.User)
74
+ ], RecurringHiddenVendor.prototype, "updater", void 0);
75
+ tslib_1.__decorate([
76
+ (0, typeorm_1.RelationId)((row) => row.updater),
77
+ tslib_1.__metadata("design:type", String)
78
+ ], RecurringHiddenVendor.prototype, "updaterId", void 0);
79
+ exports.RecurringHiddenVendor = RecurringHiddenVendor = tslib_1.__decorate([
80
+ (0, typeorm_1.Entity)(),
81
+ (0, typeorm_1.Index)('ix_recurring_hidden_vendor_0', (row) => [row.domain, row.counterparty, row.nameKey]),
82
+ (0, type_graphql_1.ObjectType)({ description: 'A vendor a keeper hid from the recurring candidates' })
83
+ ], RecurringHiddenVendor);
84
+ //# sourceMappingURL=recurring-hidden-vendor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recurring-hidden-vendor.js","sourceRoot":"","sources":["../../../server/service/recurring-cash-item/recurring-hidden-vendor.ts"],"names":[],"mappings":"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","sourcesContent":["import { CreateDateColumn, Column, DeleteDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\nimport { Field, ID, ObjectType } from 'type-graphql'\n\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\n\n/**\n * Whom a keeper said is no recurring item - paid month after month but by chance - so the books stop offering it\n * (ADR-0177 개정 1, 7-5): a counterparty, or the name written on lines no counterparty is linked to. Shown again, the\n * row goes.\n */\n@Entity()\n@Index('ix_recurring_hidden_vendor_0', (row: RecurringHiddenVendor) => [row.domain, row.counterparty, row.nameKey])\n@ObjectType({ description: 'A vendor a keeper hid from the recurring candidates' })\nexport class RecurringHiddenVendor {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain whose row it is' })\n domain?: Domain\n\n @RelationId((row: RecurringHiddenVendor) => row.domain)\n domainId?: string\n\n @ManyToOne(type => Counterparty, { nullable: true, onDelete: 'CASCADE' })\n @Field(type => Counterparty, { nullable: true, description: 'The vendor hidden' })\n counterparty?: Counterparty\n\n @RelationId((row: RecurringHiddenVendor) => row.counterparty)\n counterpartyId?: string\n\n /** The written name's key (biz-base `nameKey`), for lines no counterparty is linked to. */\n @Column({ nullable: true })\n @Field({ nullable: true, description: \"The written name's key, for lines no counterparty is linked to\" })\n nameKey?: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When it was hidden' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When it last changed' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'When it was shown again' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who hid it' })\n creator?: User\n\n @RelationId((row: RecurringHiddenVendor) => row.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who last changed it' })\n updater?: User\n\n @RelationId((row: RecurringHiddenVendor) => row.updater)\n updaterId?: string\n}\n"]}
@@ -13,7 +13,8 @@ export interface RecurringItem {
13
13
  id: string;
14
14
  name: string;
15
15
  type: string;
16
- amount: number;
16
+ /** Empty when it varies. */
17
+ amount?: number | null;
17
18
  currency?: string | null;
18
19
  dayOfMonth?: number | null;
19
20
  lastDayOfMonth?: boolean | null;
@@ -26,3 +27,13 @@ export interface RecurringItem {
26
27
  export declare function fallsIn(item: RecurringItem, year: number, month: number): boolean;
27
28
  /** The `YYYY-MM-DD` the item falls on in a month, or null when it names no day. */
28
29
  export declare function recurringDay(item: Pick<RecurringItem, 'dayOfMonth' | 'lastDayOfMonth'>, year: number, month: number): string | null;
30
+ /** A text as words are looked for in it - width and case folded (「고우-에져」 holds 「에져」), spaces gone. */
31
+ export declare const foldText: (text: string | null | undefined) => string;
32
+ /** The words written in an item's `matchWords` - comma or line separated, each folded. */
33
+ export declare function wordsOf(text: string | null | undefined): string[];
34
+ /**
35
+ * Whether a bank line's texts make it an item's, among the items of one counterparty: it holds one of the item's words
36
+ * and none of the others' - so a line of both (「오피스+에져」) is neither's, and an item with no words takes the lines no
37
+ * other item's words claim.
38
+ */
39
+ export declare function textFits(texts: readonly (string | null | undefined)[], words: readonly string[], rivals: readonly string[]): boolean;
@@ -11,8 +11,11 @@
11
11
  * Pure.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.foldText = void 0;
14
15
  exports.fallsIn = fallsIn;
15
16
  exports.recurringDay = recurringDay;
17
+ exports.wordsOf = wordsOf;
18
+ exports.textFits = textFits;
16
19
  const pad = (n) => String(n).padStart(2, '0');
17
20
  const monthKey = (year, month) => `${year}-${pad(month)}`;
18
21
  const lastDay = (year, month) => new Date(Date.UTC(year, month, 0)).getUTCDate();
@@ -33,4 +36,29 @@ function recurringDay(item, year, month) {
33
36
  return `${monthKey(year, month)}-${pad(last)}`;
34
37
  return item.dayOfMonth && item.dayOfMonth >= 1 ? `${monthKey(year, month)}-${pad(Math.min(item.dayOfMonth, last))}` : null;
35
38
  }
39
+ /* ----------------------------------------------------------------------------------------------------------------- */
40
+ /* Which bank lines are an item's, by the words of their descriptions (ADR-0177 개정 8) */
41
+ /** A text as words are looked for in it - width and case folded (「고우-에져」 holds 「에져」), spaces gone. */
42
+ const foldText = (text) => String(text ?? '').normalize('NFKC').toLowerCase().replace(/\s+/g, '');
43
+ exports.foldText = foldText;
44
+ /** The words written in an item's `matchWords` - comma or line separated, each folded. */
45
+ function wordsOf(text) {
46
+ return String(text ?? '')
47
+ .split(/[,\n]/)
48
+ .map(one => one.trim())
49
+ .filter(Boolean)
50
+ .map(exports.foldText)
51
+ .filter(Boolean);
52
+ }
53
+ /**
54
+ * Whether a bank line's texts make it an item's, among the items of one counterparty: it holds one of the item's words
55
+ * and none of the others' - so a line of both (「오피스+에져」) is neither's, and an item with no words takes the lines no
56
+ * other item's words claim.
57
+ */
58
+ function textFits(texts, words, rivals) {
59
+ const text = texts.map(exports.foldText).join(' ');
60
+ if (rivals.some(word => text.includes(word)))
61
+ return false;
62
+ return !words.length || words.some(word => text.includes(word));
63
+ }
36
64
  //# sourceMappingURL=recurring-schedule.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"recurring-schedule.js","sourceRoot":"","sources":["../../../server/service/recurring-cash-item/recurring-schedule.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AAqBH,0BAOC;AAGD,oCAIC;AAlBD,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AACrD,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAA;AACzE,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAA;AAEhG,SAAgB,OAAO,CAAC,IAAmB,EAAE,IAAY,EAAE,KAAa;IACtE,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACjC,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,IAAI,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAA;IAC1G,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAA;IACvD,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAC3B,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACtE,OAAO,CAAC,CAAC,IAAI,GAAG,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,GAAG,UAAU,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC,CAAA;AACvE,CAAC;AAED,mFAAmF;AACnF,SAAgB,YAAY,CAAC,IAA0D,EAAE,IAAY,EAAE,KAAa;IAClH,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACjC,IAAI,IAAI,CAAC,cAAc;QAAE,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,CAAA;IACvE,OAAO,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAC5H,CAAC","sourcesContent":["/**\n * Which recurring items fall in a month, and on which day of it.\n *\n * An item falls in a month when it is active, the month lies within [startMonth, endMonth] and, for an item paid every\n * few months (`everyMonths` - 12 for a yearly one), the month is a whole number of those after startMonth (ADR-0177\n * decision 4: a subscription paid yearly shares this rule). Its day is the day of\n * the month it names - one past the month's end is the last day, as a ledger means it - or the last day for 말일. An\n * item with neither has no day: the calendar shows its month undated, as it does any money nobody gave a day.\n *\n * Pure.\n */\n\nexport interface RecurringItem {\n id: string\n name: string\n type: string\n amount: number\n currency?: string | null\n dayOfMonth?: number | null\n lastDayOfMonth?: boolean | null\n startMonth: string\n endMonth?: string | null\n active?: boolean | null\n /** Paid every so many months from startMonth; every month when empty */\n everyMonths?: number | null\n}\n\nconst pad = (n: number) => String(n).padStart(2, '0')\nconst monthKey = (year: number, month: number) => `${year}-${pad(month)}`\nconst lastDay = (year: number, month: number) => new Date(Date.UTC(year, month, 0)).getUTCDate()\n\nexport function fallsIn(item: RecurringItem, year: number, month: number): boolean {\n const key = monthKey(year, month)\n if (item.active === false || key < item.startMonth || (item.endMonth && key > item.endMonth)) return false\n const every = Math.floor(Number(item.everyMonths) || 1)\n if (every <= 1) return true\n const [startYear, startMonth] = item.startMonth.split('-').map(Number)\n return ((year - startYear) * 12 + (month - startMonth)) % every === 0\n}\n\n/** The `YYYY-MM-DD` the item falls on in a month, or null when it names no day. */\nexport function recurringDay(item: Pick<RecurringItem, 'dayOfMonth' | 'lastDayOfMonth'>, year: number, month: number): string | null {\n const last = lastDay(year, month)\n if (item.lastDayOfMonth) return `${monthKey(year, month)}-${pad(last)}`\n return item.dayOfMonth && item.dayOfMonth >= 1 ? `${monthKey(year, month)}-${pad(Math.min(item.dayOfMonth, last))}` : null\n}\n"]}
1
+ {"version":3,"file":"recurring-schedule.js","sourceRoot":"","sources":["../../../server/service/recurring-cash-item/recurring-schedule.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which recurring items fall in a month, and on which day of it.\n *\n * An item falls in a month when it is active, the month lies within [startMonth, endMonth] and, for an item paid every\n * few months (`everyMonths` - 12 for a yearly one), the month is a whole number of those after startMonth (ADR-0177\n * decision 4: a subscription paid yearly shares this rule). Its day is the day of\n * the month it names - one past the month's end is the last day, as a ledger means it - or the last day for 말일. An\n * item with neither has no day: the calendar shows its month undated, as it does any money nobody gave a day.\n *\n * Pure.\n */\n\nexport interface RecurringItem {\n id: string\n name: string\n type: string\n /** Empty when it varies. */\n amount?: number | null\n currency?: string | null\n dayOfMonth?: number | null\n lastDayOfMonth?: boolean | null\n startMonth: string\n endMonth?: string | null\n active?: boolean | null\n /** Paid every so many months from startMonth; every month when empty */\n everyMonths?: number | null\n}\n\nconst pad = (n: number) => String(n).padStart(2, '0')\nconst monthKey = (year: number, month: number) => `${year}-${pad(month)}`\nconst lastDay = (year: number, month: number) => new Date(Date.UTC(year, month, 0)).getUTCDate()\n\nexport function fallsIn(item: RecurringItem, year: number, month: number): boolean {\n const key = monthKey(year, month)\n if (item.active === false || key < item.startMonth || (item.endMonth && key > item.endMonth)) return false\n const every = Math.floor(Number(item.everyMonths) || 1)\n if (every <= 1) return true\n const [startYear, startMonth] = item.startMonth.split('-').map(Number)\n return ((year - startYear) * 12 + (month - startMonth)) % every === 0\n}\n\n/** The `YYYY-MM-DD` the item falls on in a month, or null when it names no day. */\nexport function recurringDay(item: Pick<RecurringItem, 'dayOfMonth' | 'lastDayOfMonth'>, year: number, month: number): string | null {\n const last = lastDay(year, month)\n if (item.lastDayOfMonth) return `${monthKey(year, month)}-${pad(last)}`\n return item.dayOfMonth && item.dayOfMonth >= 1 ? `${monthKey(year, month)}-${pad(Math.min(item.dayOfMonth, last))}` : null\n}\n\n/* ----------------------------------------------------------------------------------------------------------------- */\n/* Which bank lines are an item's, by the words of their descriptions (ADR-0177 개정 8) */\n\n/** A text as words are looked for in it - width and case folded (「고우-에져」 holds 「에져」), spaces gone. */\nexport const foldText = (text: string | null | undefined) => String(text ?? '').normalize('NFKC').toLowerCase().replace(/\\s+/g, '')\n\n/** The words written in an item's `matchWords` - comma or line separated, each folded. */\nexport function wordsOf(text: string | null | undefined): string[] {\n return String(text ?? '')\n .split(/[,\\n]/)\n .map(one => one.trim())\n .filter(Boolean)\n .map(foldText)\n .filter(Boolean)\n}\n\n/**\n * Whether a bank line's texts make it an item's, among the items of one counterparty: it holds one of the item's words\n * and none of the others' - so a line of both (「오피스+에져」) is neither's, and an item with no words takes the lines no\n * other item's words claim.\n */\nexport function textFits(texts: readonly (string | null | undefined)[], words: readonly string[], rivals: readonly string[]): boolean {\n const text = texts.map(foldText).join(' ')\n if (rivals.some(word => text.includes(word))) return false\n return !words.length || words.some(word => text.includes(word))\n}\n"]}
@@ -0,0 +1,2 @@
1
+ /** A recurring item as what a bank line paid. */
2
+ export declare const RECURRING_TARGET = "RecurringCashItem";
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RECURRING_TARGET = void 0;
4
+ const typeorm_1 = require("typeorm");
5
+ const shell_1 = require("@things-factory/shell");
6
+ const payment_1 = require("../payment/payment");
7
+ const payment_targets_1 = require("../payment/payment-targets");
8
+ const transaction_1 = require("../transaction/transaction");
9
+ const recurring_cash_item_1 = require("./recurring-cash-item");
10
+ /** A recurring item as what a bank line paid. */
11
+ exports.RECURRING_TARGET = 'RecurringCashItem';
12
+ /*
13
+ * A bank line a person put on a recurring item (ADR-0177 개정 8) - the rule reads the items' bank lines by counterparty,
14
+ * subject and words, and may be wrong; a line a person put on an item is that item's, before the rule. The month it
15
+ * pays is its nearest of the item's. A line of the item's direction, in its currency (§`checkPaymentCurrencies`).
16
+ */
17
+ (0, payment_targets_1.registerPaymentTarget)(exports.RECURRING_TARGET, {
18
+ purpose: payment_1.PaymentPurpose.SETTLEMENT,
19
+ async currencyOf(tx, domain, targetIds) {
20
+ const items = await tx.getRepository(recurring_cash_item_1.RecurringCashItem).find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(targetIds) }, withDeleted: true });
21
+ return new Map(items.map(one => [one.id, one.currency || 'KRW']));
22
+ },
23
+ async label(tx, domain, targetIds) {
24
+ const items = await tx.getRepository(recurring_cash_item_1.RecurringCashItem).find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(targetIds) }, withDeleted: true });
25
+ return new Map(items.map(one => [one.id, one.name]));
26
+ },
27
+ async validate(tx, domain, { targetIds, transactionIds }) {
28
+ const items = await tx.getRepository(recurring_cash_item_1.RecurringCashItem).find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(targetIds) } });
29
+ if (items.length !== new Set(targetIds).size)
30
+ throw new shell_1.Refusal('recurring-not-found', {}, 'a recurring item was not found - reload and try again');
31
+ const lines = await tx.getRepository(transaction_1.Transaction).find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(transactionIds) }, relations: ['payments'] });
32
+ for (const line of lines) {
33
+ if (line.category !== transaction_1.TransactionCategory.CONFIRMED)
34
+ throw new shell_1.Refusal('recurring-payment-not-bank-line', {}, 'a recurring item is paid by bank lines');
35
+ for (const one of (line.payments ?? []).filter(payment => payment.targetType === exports.RECURRING_TARGET)) {
36
+ const item = items.find(each => each.id === one.targetId);
37
+ if (item && item.type !== line.type)
38
+ throw new shell_1.Refusal('recurring-payment-wrong-direction', { name: item.name }, 'money in pays an income item, money out an expense one');
39
+ }
40
+ }
41
+ }
42
+ });
43
+ //# sourceMappingURL=recurring-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recurring-target.js","sourceRoot":"","sources":["../../../server/service/recurring-cash-item/recurring-target.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\n\nimport { Refusal } from '@things-factory/shell'\n\nimport { PaymentPurpose } from '../payment/payment'\nimport { registerPaymentTarget } from '../payment/payment-targets'\nimport { Transaction, TransactionCategory } from '../transaction/transaction'\nimport { RecurringCashItem } from './recurring-cash-item'\n\n/** A recurring item as what a bank line paid. */\nexport const RECURRING_TARGET = 'RecurringCashItem'\n\n/*\n * A bank line a person put on a recurring item (ADR-0177 개정 8) - the rule reads the items' bank lines by counterparty,\n * subject and words, and may be wrong; a line a person put on an item is that item's, before the rule. The month it\n * pays is its nearest of the item's. A line of the item's direction, in its currency (§`checkPaymentCurrencies`).\n */\nregisterPaymentTarget(RECURRING_TARGET, {\n purpose: PaymentPurpose.SETTLEMENT,\n\n async currencyOf(tx, domain, targetIds) {\n const items = await tx.getRepository(RecurringCashItem).find({ where: { domain: { id: domain.id }, id: In(targetIds) } as any, withDeleted: true })\n return new Map(items.map(one => [one.id, one.currency || 'KRW']))\n },\n\n async label(tx, domain, targetIds) {\n const items = await tx.getRepository(RecurringCashItem).find({ where: { domain: { id: domain.id }, id: In(targetIds) } as any, withDeleted: true })\n return new Map(items.map(one => [one.id, one.name]))\n },\n\n async validate(tx, domain, { targetIds, transactionIds }) {\n const items = await tx.getRepository(RecurringCashItem).find({ where: { domain: { id: domain.id }, id: In(targetIds) } as any })\n if (items.length !== new Set(targetIds).size) throw new Refusal('recurring-not-found', {}, 'a recurring item was not found - reload and try again')\n const lines = await tx.getRepository(Transaction).find({ where: { domain: { id: domain.id }, id: In(transactionIds) } as any, relations: ['payments'] })\n for (const line of lines) {\n if (line.category !== TransactionCategory.CONFIRMED) throw new Refusal('recurring-payment-not-bank-line', {}, 'a recurring item is paid by bank lines')\n for (const one of (line.payments ?? []).filter(payment => payment.targetType === RECURRING_TARGET)) {\n const item = items.find(each => each.id === one.targetId)\n if (item && item.type !== line.type) throw new Refusal('recurring-payment-wrong-direction', { name: item.name }, 'money in pays an income item, money out an expense one')\n }\n }\n }\n})\n"]}