@things-factory/biz-base 10.1.241 → 10.1.244
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/counterparty/counterparty-choice.js +1 -1
- package/dist-server/service/counterparty/counterparty-choice.js.map +1 -1
- package/dist-server/service/counterparty/counterparty-names.d.ts +11 -0
- package/dist-server/service/counterparty/counterparty-names.js +24 -0
- package/dist-server/service/counterparty/counterparty-names.js.map +1 -1
- package/dist-server/service/counterparty/counterparty-service.d.ts +2 -1
- package/dist-server/service/counterparty/counterparty-service.js +57 -16
- package/dist-server/service/counterparty/counterparty-service.js.map +1 -1
- package/dist-server/service/counterparty/counterparty-type.d.ts +2 -0
- package/dist-server/service/counterparty/counterparty-type.js +10 -2
- package/dist-server/service/counterparty/counterparty-type.js.map +1 -1
- package/dist-server/service/counterparty/counterparty.d.ts +10 -1
- package/dist-server/service/counterparty/counterparty.js +15 -2
- package/dist-server/service/counterparty/counterparty.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/tests/counterparty.test.mjs +43 -0
- package/translations/en.json +5 -2
- package/translations/es.json +5 -2
- package/translations/ko.json +5 -2
|
@@ -34,7 +34,7 @@ tslib_1.__decorate([
|
|
|
34
34
|
tslib_1.__metadata("design:type", String)
|
|
35
35
|
], CounterpartyChoice.prototype, "name", void 0);
|
|
36
36
|
tslib_1.__decorate([
|
|
37
|
-
(0, type_graphql_1.Field)(type => counterparty_1.CounterpartyKind, { description: 'A
|
|
37
|
+
(0, type_graphql_1.Field)(type => counterparty_1.CounterpartyKind, { description: 'A business, an institution, a person, or our own company' }),
|
|
38
38
|
tslib_1.__metadata("design:type", String)
|
|
39
39
|
], CounterpartyChoice.prototype, "kind", void 0);
|
|
40
40
|
tslib_1.__decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"counterparty-choice.js","sourceRoot":"","sources":["../../../server/service/counterparty/counterparty-choice.ts"],"names":[],"mappings":";;;AA6CA,sCAGC;AAYD,4CAKC;AAMD,sDAWC;AAGD,oEAGC;;AAxFD,+CAAyD;AAGzD,iDAAgF;AAEhF,iDAA+D;AAG/D;;;;GAIG;AAEH,oEAAoE;AACvD,QAAA,wBAAwB,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,OAAO,CAAC,CAAA;AACxG,+HAA+H;AAClH,QAAA,kBAAkB,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;AAC5D,iFAAiF;AACpE,QAAA,kBAAkB,GAAG,CAAC,GAAG,0BAAkB,EAAE,QAAQ,CAAC,CAAA;AACnE,8DAA8D;AACjD,QAAA,YAAY,GAAG,EAAE,CAAA;AAGvB,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;CAK9B,CAAA;AALY,gDAAkB;AACV;IAAlB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,CAAC;;8CAAW;AACgC;IAA5D,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,mCAAmC,EAAE,CAAC;;gDAAa;
|
|
1
|
+
{"version":3,"file":"counterparty-choice.js","sourceRoot":"","sources":["../../../server/service/counterparty/counterparty-choice.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, ID, Int, ObjectType } from 'type-graphql'\nimport { Repository } from 'typeorm'\n\nimport { getQueryBuilderFromListParams, ListParam } from '@things-factory/shell'\n\nimport { Counterparty, CounterpartyKind } from './counterparty'\nimport { CounterpartyBankAccount } from './counterparty-bank-account'\n\n/*\n * Choosing a counterparty without reading it (ADR-0139, design/published-read-privilege): `counterparty-published`\n * opens what points at a counterparty and nothing else. Its types are its own - no relation to follow - so the closed\n * fields of Counterparty cannot be reached from a choice.\n */\n\n/** What the list door searches - the counterparty keeper's door. */\nexport const COUNTERPARTY_SEARCHABLES = ['name', 'aliases', 'businessNumber', 'representative', 'notes']\n/** What a choice is searched, filtered and sorted by - the fields it shows, so a search tells nothing about a closed field. */\nexport const CHOICE_SEARCHABLES = ['name', 'businessNumber']\n/* `active` is the door's own filter: an inactive counterparty is not offered. */\nexport const CHOICE_FILTERABLES = [...CHOICE_SEARCHABLES, 'active']\n/** A choice door is no way to read the whole list at once. */\nexport const CHOICE_LIMIT = 50\n\n@ObjectType({ description: 'A counterparty as one picks it: which one, and nothing about it' })\nexport class CounterpartyChoice {\n @Field(type => ID) id: string\n @Field({ description: 'The name the business knows it by' }) name: string\n @Field(type => CounterpartyKind, { description: 'A business, an institution, a person, or our own company' }) kind: CounterpartyKind\n @Field({ nullable: true, description: 'Business registration number - tells apart two counterparties of one name (ADR-0132)' }) businessNumber?: string\n}\n\n@ObjectType({ description: 'A page of counterparties to pick from' })\nexport class CounterpartyChoiceList {\n @Field(type => [CounterpartyChoice]) items: CounterpartyChoice[]\n @Field(type => Int) total: number\n}\n\n@ObjectType({ description: \"An account a counterparty is paid into, as one picks it: the bank and the number's last four digits\" })\nexport class CounterpartyAccountChoice {\n @Field(type => ID) id: string\n @Field({ nullable: true, description: 'The bank' }) bankName?: string\n @Field({ nullable: true, description: \"The account number's last four digits\" }) accountTail?: string\n}\n\n/** An account number's last four digits - what a chooser and a summary show of it. */\nexport function accountTailOf(number: string | null | undefined): string | undefined {\n const digits = String(number ?? '').replace(/\\D/g, '')\n return digits ? digits.slice(-4) : undefined\n}\n\n/** What a door lets a list be read by. */\nexport interface ListRules {\n searchables: string[]\n filterables?: string[]\n sortables?: string[]\n defaultLimit?: number\n maxLimit?: number\n}\n\n/** One page of a domain's counterparties - the list door and the choice door read through this one function. */\nexport async function counterpartyPage(repository: Repository<Counterparty>, domain: { id: string }, params: ListParam, rules: ListRules) {\n const queryBuilder = getQueryBuilderFromListParams({ domain: domain as any, params, repository, ...rules, alias: 'counterparty' })\n queryBuilder.addOrderBy('counterparty.name', 'ASC')\n const [items, total] = await queryBuilder.getManyAndCount()\n return { items, total }\n}\n\n/**\n * Counterparties to pick from - asked as any list is (a picker sends filters, a page and sortings), answered only by\n * what a choice shows: active ones, searched, filtered and sorted by the shown fields, a capped page at a time.\n */\nexport async function counterpartyChoicesOf(repository: Repository<Counterparty>, domain: { id: string }, params: ListParam | undefined) {\n const asked = (params ?? {}) as ListParam\n const filters = [...(asked.filters ?? []), { name: 'active', operator: 'eq', value: true }]\n const { items, total } = await counterpartyPage(repository, domain, { ...asked, filters } as ListParam, {\n searchables: CHOICE_SEARCHABLES,\n filterables: CHOICE_FILTERABLES,\n sortables: CHOICE_SEARCHABLES,\n defaultLimit: CHOICE_LIMIT,\n maxLimit: CHOICE_LIMIT\n })\n return { items: items.map(one => ({ id: one.id, name: one.name, kind: one.kind, businessNumber: one.businessNumber })), total }\n}\n\n/** The accounts one counterparty is paid into, as one picks them. */\nexport async function counterpartyAccountChoicesOf(repository: Repository<CounterpartyBankAccount>, domain: { id: string }, counterpartyId: string) {\n const accounts = await repository.find({ where: { domain: { id: domain.id }, counterparty: { id: counterpartyId } } as any, order: { createdAt: 'ASC' } as any, take: CHOICE_LIMIT })\n return accounts.map(one => ({ id: one.id, bankName: one.bankName, accountTail: accountTailOf(one.accountNumber) }))\n}\n"]}
|
|
@@ -37,6 +37,17 @@ export declare function linkFor(patch: {
|
|
|
37
37
|
* not ten digits is refused rather than stored half-right.
|
|
38
38
|
*/
|
|
39
39
|
export declare function normalizeBusinessNumber(value: unknown): string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Where a counterparty is from, as stored (ADR-0132 §0-2): ISO 3166-1 alpha-2 in capitals, empty for one at home.
|
|
42
|
+
* `KR` is home written out, so it is stored empty too - home is written one way.
|
|
43
|
+
*/
|
|
44
|
+
export declare function normalizeCountry(value: unknown): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* A business number as stored for a counterparty of `country`. At home it is the ten-digit 사업자등록번호
|
|
47
|
+
* (§`normalizeBusinessNumber`); abroad it is that country's number as written, its spaces taken out - its form is
|
|
48
|
+
* not checked (ADR-0132 §2-3).
|
|
49
|
+
*/
|
|
50
|
+
export declare function businessNumberOf(value: unknown, country: string | null | undefined): string | null;
|
|
40
51
|
/**
|
|
41
52
|
* Two counterparties with one business registration number — after a batch would apply. Returns the clashing pair of
|
|
42
53
|
* names and the number, or undefined. A number is one business: two counterparties holding it would split its
|
|
@@ -12,6 +12,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.nameKey = nameKey;
|
|
13
13
|
exports.linkFor = linkFor;
|
|
14
14
|
exports.normalizeBusinessNumber = normalizeBusinessNumber;
|
|
15
|
+
exports.normalizeCountry = normalizeCountry;
|
|
16
|
+
exports.businessNumberOf = businessNumberOf;
|
|
15
17
|
exports.numberClash = numberClash;
|
|
16
18
|
exports.keyClash = keyClash;
|
|
17
19
|
exports.accountKey = accountKey;
|
|
@@ -73,6 +75,28 @@ function normalizeBusinessNumber(value) {
|
|
|
73
75
|
throw new shell_1.Refusal('business-number-invalid', { text }, `'${text}' is not a business registration number (10 digits).`);
|
|
74
76
|
return `${digits.slice(0, 3)}-${digits.slice(3, 5)}-${digits.slice(5)}`;
|
|
75
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Where a counterparty is from, as stored (ADR-0132 §0-2): ISO 3166-1 alpha-2 in capitals, empty for one at home.
|
|
80
|
+
* `KR` is home written out, so it is stored empty too - home is written one way.
|
|
81
|
+
*/
|
|
82
|
+
function normalizeCountry(value) {
|
|
83
|
+
const text = String(value ?? '').trim().toUpperCase();
|
|
84
|
+
if (!text || text === 'KR')
|
|
85
|
+
return null;
|
|
86
|
+
if (!/^[A-Z]{2}$/.test(text))
|
|
87
|
+
throw new shell_1.Refusal('counterparty-country-invalid', { text }, `'${text}' is not a country code (two letters, as US or JP)`);
|
|
88
|
+
return text;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A business number as stored for a counterparty of `country`. At home it is the ten-digit 사업자등록번호
|
|
92
|
+
* (§`normalizeBusinessNumber`); abroad it is that country's number as written, its spaces taken out - its form is
|
|
93
|
+
* not checked (ADR-0132 §2-3).
|
|
94
|
+
*/
|
|
95
|
+
function businessNumberOf(value, country) {
|
|
96
|
+
if (!country)
|
|
97
|
+
return normalizeBusinessNumber(value);
|
|
98
|
+
return String(value ?? '').replace(/\s+/g, '') || null;
|
|
99
|
+
}
|
|
76
100
|
/**
|
|
77
101
|
* Two counterparties with one business registration number — after a batch would apply. Returns the clashing pair of
|
|
78
102
|
* names and the number, or undefined. A number is one business: two counterparties holding it would split its
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"counterparty-names.js","sourceRoot":"","sources":["../../../server/service/counterparty/counterparty-names.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * How names point at counterparties — one place for every module that keeps a name and a link.\n *\n * A document (a contract, a bank line, an invoice) records **the name as it was** and, when the domain knows that\n * party, a link to it. The name stays even if the party is renamed later. Names are compared the way people mean\n * them (§`nameKey`), and a counterparty's aliases count as its names.\n *\n * Pure: the caller hands over what it looked up.\n */\n\nimport { Refusal } from '@things-factory/shell'\n\nexport interface KnownCounterparty {\n id: string\n name: string\n}\n\n/**\n * Compare names the way people mean them — case, spacing and `(주)` / `주식회사` do not make another party.\n * NFKC folds the compatibility forms Hometax writes (`(주)` in fullwidth, `㈜`) into the plain ones.\n */\nexport function nameKey(name: string | null | undefined): string {\n return (\n String(name ?? '')\n .normalize('NFKC')\n .replace(/\\(\\s*주\\s*\\)|㈜|주식회사|\\(\\s*유\\s*\\)|유한회사/g, '')\n /* The same company written in Hangul or in Latin — 씨제이올리브네트웍스 is CJ올리브네트웍스. */\n .replace(/씨제이/g, 'cj')\n .replace(/에스케이/g, 'sk')\n .replace(/엘지/g, 'lg')\n .replace(/[\\s\\-_.·,()]+/g, '')\n .toLowerCase()\n )\n}\n\nexport type LinkChange = { counterparty?: { id: string } | null; customerName?: string | null }\n\n/**\n * What to write for the link, given the row's patch. Returns only what changes; an update that\n * touches neither the name nor the counterparty returns `{}`.\n */\nexport function linkFor(\n patch: { counterparty?: { id?: string | null } | null; customerName?: string | null },\n byId: (id: string) => KnownCounterparty | undefined,\n byName: (key: string) => KnownCounterparty | undefined\n): LinkChange {\n if (patch.counterparty !== undefined) {\n const id = patch.counterparty?.id\n if (!id) return { counterparty: null, ...('customerName' in patch ? { customerName: patch.customerName ?? null } : {}) }\n\n const known = byId(id)\n if (!known) throw new Refusal('counterparty-not-found', { id }, 'the counterparty was not found. reload and try again.')\n return { counterparty: { id: known.id }, customerName: known.name }\n }\n\n if (!('customerName' in patch)) return {}\n\n const name = patch.customerName?.trim() || null\n if (!name) return { counterparty: null, customerName: null }\n\n const known = byName(nameKey(name))\n return known ? { counterparty: { id: known.id }, customerName: name } : { counterparty: null, customerName: name }\n}\n\n/**\n * 사업자등록번호 — ten digits, stored as `123-45-67890`. Empty stays empty; anything else that is\n * not ten digits is refused rather than stored half-right.\n */\nexport function normalizeBusinessNumber(value: unknown): string | null {\n const text = String(value ?? '').trim()\n if (!text) return null\n\n const digits = text.replace(/[-\\s]/g, '')\n if (!/^\\d{10}$/.test(digits)) throw new Refusal('business-number-invalid', { text }, `'${text}' is not a business registration number (10 digits).`)\n\n return `${digits.slice(0, 3)}-${digits.slice(3, 5)}-${digits.slice(5)}`\n}\n\n/**\n * Two counterparties with one business registration number — after a batch would apply. Returns the clashing pair of\n * names and the number, or undefined. A number is one business: two counterparties holding it would split its\n * documents in two.\n *\n * Only a clash the batch makes counts: a number it registers or changes to. Names are not compared — two businesses\n * may trade under one name.\n */\nexport function numberClash(\n existing: readonly (KnownCounterparty & { businessNumber?: string | null })[],\n changes: readonly { id?: string; name?: string | null; businessNumber?: string | null }[],\n deleted: readonly string[] = []\n): { number: string; existing: string; name: string } | undefined {\n return keyClash(\n existing.map(one => ({ id: one.id, label: one.name, key: one.businessNumber })),\n changes.map(one => ({ id: one.id, label: one.name, key: one.businessNumber })),\n deleted\n )\n}\n\n/**\n * Two rows holding one key that must be one row's — a business number, an account number — after a batch would apply.\n * Only a clash the batch makes counts: a key it writes. A clash already in the rows does not stop another save.\n */\nexport function keyClash(\n existing: readonly { id: string; label: string; key?: string | null }[],\n changes: readonly { id?: string; label?: string | null; key?: string | null }[],\n deleted: readonly string[] = []\n): { number: string; existing: string; name: string } | undefined {\n const before = new Map(existing.map(one => [one.id, one]))\n const after = new Map<string, { label: string; key: string }>()\n for (const one of existing) if (!deleted.includes(one.id)) after.set(one.id, { label: one.label, key: one.key ?? '' })\n\n const keyed = new Set<string>()\n let fresh = 0\n for (const change of changes) {\n const id = change.id ?? `new:${fresh++}`\n const current = after.get(id) ?? { label: '', key: '' }\n const next = {\n label: change.label ?? current.label,\n key: change.key !== undefined ? change.key ?? '' : current.key\n }\n after.set(id, next)\n if (next.key && next.key !== (before.get(id)?.key ?? '')) keyed.add(id)\n }\n\n const holders = new Map<string, { id: string; label: string }[]>()\n for (const [id, one] of after) if (one.key) holders.set(one.key, [...(holders.get(one.key) ?? []), { id, label: one.label }])\n for (const [key, rows] of holders) {\n if (rows.length < 2) continue\n const made = rows.find(one => keyed.has(one.id))\n if (!made) continue\n const other = rows.find(one => one !== made)!\n return { number: key, existing: other.label, name: made.label }\n }\n\n return undefined\n}\n\n/**\n * An account number as it is compared and kept — its digits. Banks print the same account with the hyphens in\n * different places, so `110-123-456789` and `110123456789` are one account. Empty when there are no digits.\n */\nexport function accountKey(value: unknown): string {\n return String(value ?? '').replace(/\\D/g, '')\n}\n\n/** Names to register: one spelling per party, skipping blanks and parties already known. */\nexport function namesToRegister(names: readonly (string | null | undefined)[], known: readonly KnownCounterparty[]): string[] {\n const taken = new Set(known.map(one => nameKey(one.name)))\n const out: string[] = []\n\n for (const name of names) {\n const trimmed = String(name ?? '').trim()\n const key = nameKey(trimmed)\n if (!key || taken.has(key)) continue\n taken.add(key)\n out.push(trimmed)\n }\n\n return out\n}\n\nexport interface PartyForMatch {\n id: string\n name: string\n aliases?: string | null\n}\n\nexport interface ProposedParty {\n name: string\n variants: string[]\n count: number\n maybePerson: boolean\n}\n\n/** Keys shorter than this match too much by accident. */\nconst MIN_KEY = 2\n\nconst SURNAMES = new Set('김이박최정강조윤장임한오서신권황안송류전홍고문양손배백허유남심노하곽성차주우구민진지엄채원천방공현함변염여추도소석선설마길연위표명기반왕금옥육인맹제모탁국어은편용예경봉사부가복태목형피두감음빈동온호범좌팽승간상갈단견당'.split(''))\n\nexport function looksLikePersonName(name: string): boolean {\n const trimmed = name.trim()\n return /^[가-힣]{2,4}$/.test(trimmed) && trimmed.length === 3 && SURNAMES.has(trimmed[0]) && !/(테크|솔루션|푸드|그룹|시스템|컴|넷|랩|소프트|산업|상사|물산|로지)/.test(trimmed)\n}\n\n/** The parties a list of names (contracts, documents) means, one per `nameKey`, spelled the way the ledger spells it most. */\nexport function partiesFromNames(names: readonly (string | null | undefined)[]): ProposedParty[] {\n const groups = new Map<string, Map<string, number>>()\n for (const raw of names) {\n const name = String(raw ?? '').trim()\n const key = nameKey(name)\n if (key.length < MIN_KEY) continue\n const spellings = groups.get(key) ?? new Map<string, number>()\n spellings.set(name, (spellings.get(name) ?? 0) + 1)\n groups.set(key, spellings)\n }\n\n return [...groups.values()]\n .map(spellings => {\n /* The spelling used most; on a tie the one without a corporate marker, then the shortest. */\n const variants = [...spellings.entries()].sort((a, b) => b[1] - a[1] || marker(a[0]) - marker(b[0]) || a[0].length - b[0].length).map(([one]) => one)\n const count = [...spellings.values()].reduce((sum, one) => sum + one, 0)\n return { name: variants[0], variants, count, maybePerson: looksLikePersonName(variants[0]) }\n })\n .sort((a, b) => a.name.localeCompare(b.name))\n}\n\nconst marker = (name: string) => (/\\(\\s*주\\s*\\)|㈜|주식회사/.test(name) ? 1 : 0)\n\nexport function aliasesOf(party: PartyForMatch): string[] {\n return String(party.aliases ?? '')\n .split(/[\\n,]/)\n .map(one => one.trim())\n .filter(Boolean)\n}\n\n/**\n * A function telling which party a name is — the party's name or one of its aliases, spelled any way\n * `nameKey` folds (§ a contract's 상호). An exact match, not a search.\n */\nexport function partyNamed(parties: readonly PartyForMatch[]): (name: string | null | undefined) => PartyForMatch | undefined {\n /* A name two parties go by answers neither — names are not unique. */\n const byKey = new Map<string, PartyForMatch | null>()\n for (const party of parties)\n for (const name of [party.name, ...aliasesOf(party)]) {\n const key = nameKey(name)\n byKey.set(key, byKey.has(key) && byKey.get(key) !== party ? null : party)\n }\n return name => (nameKey(name) ? byKey.get(nameKey(name)) ?? undefined : undefined)\n}\n\n/**\n * A function telling which party a text names. The longest matching name or alias wins; two\n * different parties matching equally long is ambiguous — no answer.\n */\nexport function matcherFor(parties: readonly PartyForMatch[]): (text: string | null | undefined) => PartyForMatch | undefined {\n const keys: { key: string; party: PartyForMatch }[] = []\n for (const party of parties) {\n for (const name of [party.name, ...aliasesOf(party)]) {\n const key = nameKey(name)\n if (key.length >= MIN_KEY) keys.push({ key, party })\n }\n }\n keys.sort((a, b) => b.key.length - a.key.length)\n\n return text => {\n const hay = nameKey(text)\n if (!hay) return undefined\n const hits = keys.filter(one => hay.includes(one.key))\n if (!hits.length) return undefined\n const longest = hits[0].key.length\n const top = new Set(hits.filter(one => one.key.length === longest).map(one => one.party.id))\n return top.size === 1 ? hits[0].party : undefined\n }\n}\n\n/** The texts left unmatched most often — what a person might add as aliases. */\nexport function frequentUnmatched(texts: readonly (string | null | undefined)[], limit = 30): { text: string; count: number }[] {\n const counts = new Map<string, number>()\n for (const raw of texts) {\n const text = String(raw ?? '').trim()\n if (!text) continue\n counts.set(text, (counts.get(text) ?? 0) + 1)\n }\n return [...counts.entries()]\n .map(([text, count]) => ({ text, count }))\n .sort((a, b) => b.count - a.count || a.text.localeCompare(b.text))\n .slice(0, limit)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"counterparty-names.js","sourceRoot":"","sources":["../../../server/service/counterparty/counterparty-names.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * How names point at counterparties — one place for every module that keeps a name and a link.\n *\n * A document (a contract, a bank line, an invoice) records **the name as it was** and, when the domain knows that\n * party, a link to it. The name stays even if the party is renamed later. Names are compared the way people mean\n * them (§`nameKey`), and a counterparty's aliases count as its names.\n *\n * Pure: the caller hands over what it looked up.\n */\n\nimport { Refusal } from '@things-factory/shell'\n\nexport interface KnownCounterparty {\n id: string\n name: string\n}\n\n/**\n * Compare names the way people mean them — case, spacing and `(주)` / `주식회사` do not make another party.\n * NFKC folds the compatibility forms Hometax writes (`(주)` in fullwidth, `㈜`) into the plain ones.\n */\nexport function nameKey(name: string | null | undefined): string {\n return (\n String(name ?? '')\n .normalize('NFKC')\n .replace(/\\(\\s*주\\s*\\)|㈜|주식회사|\\(\\s*유\\s*\\)|유한회사/g, '')\n /* The same company written in Hangul or in Latin — 씨제이올리브네트웍스 is CJ올리브네트웍스. */\n .replace(/씨제이/g, 'cj')\n .replace(/에스케이/g, 'sk')\n .replace(/엘지/g, 'lg')\n .replace(/[\\s\\-_.·,()]+/g, '')\n .toLowerCase()\n )\n}\n\nexport type LinkChange = { counterparty?: { id: string } | null; customerName?: string | null }\n\n/**\n * What to write for the link, given the row's patch. Returns only what changes; an update that\n * touches neither the name nor the counterparty returns `{}`.\n */\nexport function linkFor(\n patch: { counterparty?: { id?: string | null } | null; customerName?: string | null },\n byId: (id: string) => KnownCounterparty | undefined,\n byName: (key: string) => KnownCounterparty | undefined\n): LinkChange {\n if (patch.counterparty !== undefined) {\n const id = patch.counterparty?.id\n if (!id) return { counterparty: null, ...('customerName' in patch ? { customerName: patch.customerName ?? null } : {}) }\n\n const known = byId(id)\n if (!known) throw new Refusal('counterparty-not-found', { id }, 'the counterparty was not found. reload and try again.')\n return { counterparty: { id: known.id }, customerName: known.name }\n }\n\n if (!('customerName' in patch)) return {}\n\n const name = patch.customerName?.trim() || null\n if (!name) return { counterparty: null, customerName: null }\n\n const known = byName(nameKey(name))\n return known ? { counterparty: { id: known.id }, customerName: name } : { counterparty: null, customerName: name }\n}\n\n/**\n * 사업자등록번호 — ten digits, stored as `123-45-67890`. Empty stays empty; anything else that is\n * not ten digits is refused rather than stored half-right.\n */\nexport function normalizeBusinessNumber(value: unknown): string | null {\n const text = String(value ?? '').trim()\n if (!text) return null\n\n const digits = text.replace(/[-\\s]/g, '')\n if (!/^\\d{10}$/.test(digits)) throw new Refusal('business-number-invalid', { text }, `'${text}' is not a business registration number (10 digits).`)\n\n return `${digits.slice(0, 3)}-${digits.slice(3, 5)}-${digits.slice(5)}`\n}\n\n/**\n * Where a counterparty is from, as stored (ADR-0132 §0-2): ISO 3166-1 alpha-2 in capitals, empty for one at home.\n * `KR` is home written out, so it is stored empty too - home is written one way.\n */\nexport function normalizeCountry(value: unknown): string | null {\n const text = String(value ?? '').trim().toUpperCase()\n if (!text || text === 'KR') return null\n if (!/^[A-Z]{2}$/.test(text)) throw new Refusal('counterparty-country-invalid', { text }, `'${text}' is not a country code (two letters, as US or JP)`)\n return text\n}\n\n/**\n * A business number as stored for a counterparty of `country`. At home it is the ten-digit 사업자등록번호\n * (§`normalizeBusinessNumber`); abroad it is that country's number as written, its spaces taken out - its form is\n * not checked (ADR-0132 §2-3).\n */\nexport function businessNumberOf(value: unknown, country: string | null | undefined): string | null {\n if (!country) return normalizeBusinessNumber(value)\n return String(value ?? '').replace(/\\s+/g, '') || null\n}\n\n/**\n * Two counterparties with one business registration number — after a batch would apply. Returns the clashing pair of\n * names and the number, or undefined. A number is one business: two counterparties holding it would split its\n * documents in two.\n *\n * Only a clash the batch makes counts: a number it registers or changes to. Names are not compared — two businesses\n * may trade under one name.\n */\nexport function numberClash(\n existing: readonly (KnownCounterparty & { businessNumber?: string | null })[],\n changes: readonly { id?: string; name?: string | null; businessNumber?: string | null }[],\n deleted: readonly string[] = []\n): { number: string; existing: string; name: string } | undefined {\n return keyClash(\n existing.map(one => ({ id: one.id, label: one.name, key: one.businessNumber })),\n changes.map(one => ({ id: one.id, label: one.name, key: one.businessNumber })),\n deleted\n )\n}\n\n/**\n * Two rows holding one key that must be one row's — a business number, an account number — after a batch would apply.\n * Only a clash the batch makes counts: a key it writes. A clash already in the rows does not stop another save.\n */\nexport function keyClash(\n existing: readonly { id: string; label: string; key?: string | null }[],\n changes: readonly { id?: string; label?: string | null; key?: string | null }[],\n deleted: readonly string[] = []\n): { number: string; existing: string; name: string } | undefined {\n const before = new Map(existing.map(one => [one.id, one]))\n const after = new Map<string, { label: string; key: string }>()\n for (const one of existing) if (!deleted.includes(one.id)) after.set(one.id, { label: one.label, key: one.key ?? '' })\n\n const keyed = new Set<string>()\n let fresh = 0\n for (const change of changes) {\n const id = change.id ?? `new:${fresh++}`\n const current = after.get(id) ?? { label: '', key: '' }\n const next = {\n label: change.label ?? current.label,\n key: change.key !== undefined ? change.key ?? '' : current.key\n }\n after.set(id, next)\n if (next.key && next.key !== (before.get(id)?.key ?? '')) keyed.add(id)\n }\n\n const holders = new Map<string, { id: string; label: string }[]>()\n for (const [id, one] of after) if (one.key) holders.set(one.key, [...(holders.get(one.key) ?? []), { id, label: one.label }])\n for (const [key, rows] of holders) {\n if (rows.length < 2) continue\n const made = rows.find(one => keyed.has(one.id))\n if (!made) continue\n const other = rows.find(one => one !== made)!\n return { number: key, existing: other.label, name: made.label }\n }\n\n return undefined\n}\n\n/**\n * An account number as it is compared and kept — its digits. Banks print the same account with the hyphens in\n * different places, so `110-123-456789` and `110123456789` are one account. Empty when there are no digits.\n */\nexport function accountKey(value: unknown): string {\n return String(value ?? '').replace(/\\D/g, '')\n}\n\n/** Names to register: one spelling per party, skipping blanks and parties already known. */\nexport function namesToRegister(names: readonly (string | null | undefined)[], known: readonly KnownCounterparty[]): string[] {\n const taken = new Set(known.map(one => nameKey(one.name)))\n const out: string[] = []\n\n for (const name of names) {\n const trimmed = String(name ?? '').trim()\n const key = nameKey(trimmed)\n if (!key || taken.has(key)) continue\n taken.add(key)\n out.push(trimmed)\n }\n\n return out\n}\n\nexport interface PartyForMatch {\n id: string\n name: string\n aliases?: string | null\n}\n\nexport interface ProposedParty {\n name: string\n variants: string[]\n count: number\n maybePerson: boolean\n}\n\n/** Keys shorter than this match too much by accident. */\nconst MIN_KEY = 2\n\nconst SURNAMES = new Set('김이박최정강조윤장임한오서신권황안송류전홍고문양손배백허유남심노하곽성차주우구민진지엄채원천방공현함변염여추도소석선설마길연위표명기반왕금옥육인맹제모탁국어은편용예경봉사부가복태목형피두감음빈동온호범좌팽승간상갈단견당'.split(''))\n\nexport function looksLikePersonName(name: string): boolean {\n const trimmed = name.trim()\n return /^[가-힣]{2,4}$/.test(trimmed) && trimmed.length === 3 && SURNAMES.has(trimmed[0]) && !/(테크|솔루션|푸드|그룹|시스템|컴|넷|랩|소프트|산업|상사|물산|로지)/.test(trimmed)\n}\n\n/** The parties a list of names (contracts, documents) means, one per `nameKey`, spelled the way the ledger spells it most. */\nexport function partiesFromNames(names: readonly (string | null | undefined)[]): ProposedParty[] {\n const groups = new Map<string, Map<string, number>>()\n for (const raw of names) {\n const name = String(raw ?? '').trim()\n const key = nameKey(name)\n if (key.length < MIN_KEY) continue\n const spellings = groups.get(key) ?? new Map<string, number>()\n spellings.set(name, (spellings.get(name) ?? 0) + 1)\n groups.set(key, spellings)\n }\n\n return [...groups.values()]\n .map(spellings => {\n /* The spelling used most; on a tie the one without a corporate marker, then the shortest. */\n const variants = [...spellings.entries()].sort((a, b) => b[1] - a[1] || marker(a[0]) - marker(b[0]) || a[0].length - b[0].length).map(([one]) => one)\n const count = [...spellings.values()].reduce((sum, one) => sum + one, 0)\n return { name: variants[0], variants, count, maybePerson: looksLikePersonName(variants[0]) }\n })\n .sort((a, b) => a.name.localeCompare(b.name))\n}\n\nconst marker = (name: string) => (/\\(\\s*주\\s*\\)|㈜|주식회사/.test(name) ? 1 : 0)\n\nexport function aliasesOf(party: PartyForMatch): string[] {\n return String(party.aliases ?? '')\n .split(/[\\n,]/)\n .map(one => one.trim())\n .filter(Boolean)\n}\n\n/**\n * A function telling which party a name is — the party's name or one of its aliases, spelled any way\n * `nameKey` folds (§ a contract's 상호). An exact match, not a search.\n */\nexport function partyNamed(parties: readonly PartyForMatch[]): (name: string | null | undefined) => PartyForMatch | undefined {\n /* A name two parties go by answers neither — names are not unique. */\n const byKey = new Map<string, PartyForMatch | null>()\n for (const party of parties)\n for (const name of [party.name, ...aliasesOf(party)]) {\n const key = nameKey(name)\n byKey.set(key, byKey.has(key) && byKey.get(key) !== party ? null : party)\n }\n return name => (nameKey(name) ? byKey.get(nameKey(name)) ?? undefined : undefined)\n}\n\n/**\n * A function telling which party a text names. The longest matching name or alias wins; two\n * different parties matching equally long is ambiguous — no answer.\n */\nexport function matcherFor(parties: readonly PartyForMatch[]): (text: string | null | undefined) => PartyForMatch | undefined {\n const keys: { key: string; party: PartyForMatch }[] = []\n for (const party of parties) {\n for (const name of [party.name, ...aliasesOf(party)]) {\n const key = nameKey(name)\n if (key.length >= MIN_KEY) keys.push({ key, party })\n }\n }\n keys.sort((a, b) => b.key.length - a.key.length)\n\n return text => {\n const hay = nameKey(text)\n if (!hay) return undefined\n const hits = keys.filter(one => hay.includes(one.key))\n if (!hits.length) return undefined\n const longest = hits[0].key.length\n const top = new Set(hits.filter(one => one.key.length === longest).map(one => one.party.id))\n return top.size === 1 ? hits[0].party : undefined\n }\n}\n\n/** The texts left unmatched most often — what a person might add as aliases. */\nexport function frequentUnmatched(texts: readonly (string | null | undefined)[], limit = 30): { text: string; count: number }[] {\n const counts = new Map<string, number>()\n for (const raw of texts) {\n const text = String(raw ?? '').trim()\n if (!text) continue\n counts.set(text, (counts.get(text) ?? 0) + 1)\n }\n return [...counts.entries()]\n .map(([text, count]) => ({ text, count }))\n .sort((a, b) => b.count - a.count || a.text.localeCompare(b.text))\n .slice(0, limit)\n}\n"]}
|
|
@@ -15,7 +15,8 @@ export declare function onCounterpartiesSaved(listener: Saved): void;
|
|
|
15
15
|
/**
|
|
16
16
|
* Apply grid rows to a domain's counterparties: `+` (or no id) registers, `M` changes, `-` deletes (softly). A
|
|
17
17
|
* counterparty a contract, a bank line or any other row still names is not deleted (§`refuseTiedRows`). Two
|
|
18
|
-
* counterparties with one business registration number are refused (§`numberClash`); a name may be shared.
|
|
18
|
+
* counterparties with one business registration number are refused (§`numberClash`); a name may be shared. Our own
|
|
19
|
+
* company is one counterparty with none of its facts (§`refuseSelfMisuse`).
|
|
19
20
|
*/
|
|
20
21
|
export declare function saveCounterparties(tx: EntityManager, domain: {
|
|
21
22
|
id: string;
|
|
@@ -14,8 +14,10 @@ function onCounterpartiesSaved(listener) {
|
|
|
14
14
|
}
|
|
15
15
|
/** The words people write for the kind, as well as the values themselves. */
|
|
16
16
|
const KIND_WORDS = {
|
|
17
|
-
[counterparty_1.CounterpartyKind.COMPANY]: ['COMPANY', '
|
|
18
|
-
[counterparty_1.CounterpartyKind.
|
|
17
|
+
[counterparty_1.CounterpartyKind.COMPANY]: ['COMPANY', '사업자', '법인', '개인사업자', '회사', '기업'],
|
|
18
|
+
[counterparty_1.CounterpartyKind.INSTITUTION]: ['INSTITUTION', '기관', '공공기관', '관공서'],
|
|
19
|
+
[counterparty_1.CounterpartyKind.PERSON]: ['PERSON', '개인'],
|
|
20
|
+
[counterparty_1.CounterpartyKind.SELF]: ['SELF', '자사', '우리 회사']
|
|
19
21
|
};
|
|
20
22
|
function kindOf(value) {
|
|
21
23
|
const text = String(value ?? '').trim();
|
|
@@ -23,11 +25,16 @@ function kindOf(value) {
|
|
|
23
25
|
return undefined;
|
|
24
26
|
const found = Object.keys(KIND_WORDS).find(kind => KIND_WORDS[kind].some(word => word.toLowerCase() === text.toLowerCase()));
|
|
25
27
|
if (!found)
|
|
26
|
-
throw new shell_1.Refusal('counterparty-kind-invalid', { text }, `'${text}' is
|
|
28
|
+
throw new shell_1.Refusal('counterparty-kind-invalid', { text }, `'${text}' is not a business, an institution, a person or our own company`);
|
|
27
29
|
return found;
|
|
28
30
|
}
|
|
29
31
|
const TEXT = ['aliases', 'branchNumber', 'representative', 'address', 'billingAddress', 'businessType', 'businessItem', 'notes'];
|
|
30
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* The facts of a company that 「우리 회사」 keeps (ADR-0176 decision 4). A `SELF` counterparty does not keep a copy of
|
|
34
|
+
* them; it is our company's place in the list, by the names the bank writes it under (ADR-0132 §0-2).
|
|
35
|
+
*/
|
|
36
|
+
const COMPANY_FACTS = ['businessNumber', 'branchNumber', 'representative', 'address', 'billingAddress', 'businessType', 'businessItem'];
|
|
37
|
+
/** A patch as the row it writes: names trimmed, the kind and the country in their stored form. The number is `numbered`'s. */
|
|
31
38
|
function rowOf(patch, isNew) {
|
|
32
39
|
const row = {};
|
|
33
40
|
if (patch.name !== undefined || isNew) {
|
|
@@ -43,28 +50,66 @@ function rowOf(patch, isNew) {
|
|
|
43
50
|
row.kind = kindOf(patch.kind) ?? counterparty_1.CounterpartyKind.COMPANY;
|
|
44
51
|
else if (isNew)
|
|
45
52
|
row.kind = counterparty_1.CounterpartyKind.COMPANY;
|
|
46
|
-
if (patch.
|
|
47
|
-
row.
|
|
53
|
+
if (patch.country !== undefined)
|
|
54
|
+
row.country = (0, counterparty_names_1.normalizeCountry)(patch.country);
|
|
48
55
|
if (patch.zeroRated !== undefined)
|
|
49
56
|
row.zeroRated = !!patch.zeroRated;
|
|
57
|
+
/* Sales abroad are zero-rated as a rule; the field stays, so a sale that is not says so (ADR-0132 §0-2). */
|
|
58
|
+
else if (isNew && row.country)
|
|
59
|
+
row.zeroRated = true;
|
|
50
60
|
if (patch.active !== undefined)
|
|
51
61
|
row.active = patch.active !== false;
|
|
52
62
|
return row;
|
|
53
63
|
}
|
|
64
|
+
/** The row with its business number in the stored form of its country - the country it has after the change. */
|
|
65
|
+
function numbered(patch, row, current) {
|
|
66
|
+
if (patch.businessNumber !== undefined)
|
|
67
|
+
return { ...row, businessNumber: (0, counterparty_names_1.businessNumberOf)(patch.businessNumber, 'country' in row ? row.country : current?.country) };
|
|
68
|
+
return row;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A `SELF` counterparty holds none of our company's facts, and a domain has one. `rows` are all the rows after the
|
|
72
|
+
* batch, `written` the ones it writes - only what the batch writes is refused, as with the business number.
|
|
73
|
+
*/
|
|
74
|
+
function refuseSelfMisuse(rows, written) {
|
|
75
|
+
const selves = rows.filter(one => one.kind === counterparty_1.CounterpartyKind.SELF);
|
|
76
|
+
for (const one of written.filter(one => one.kind === counterparty_1.CounterpartyKind.SELF)) {
|
|
77
|
+
const kept = COMPANY_FACTS.filter(field => one[field]);
|
|
78
|
+
if (kept.length)
|
|
79
|
+
throw new shell_1.Refusal('counterparty-self-facts', { name: one.name, fields: kept.join(', ') }, `'${one.name}' is our own company - its ${kept.join(', ')} are kept in 「우리 회사」, not here`);
|
|
80
|
+
}
|
|
81
|
+
if (selves.length > 1 && written.some(one => one.kind === counterparty_1.CounterpartyKind.SELF))
|
|
82
|
+
throw new shell_1.Refusal('counterparty-self-taken', { existing: selves[0].name, name: selves[1].name }, `'${selves[0].name}' is already our own company - a domain has one`);
|
|
83
|
+
}
|
|
54
84
|
/**
|
|
55
85
|
* Apply grid rows to a domain's counterparties: `+` (or no id) registers, `M` changes, `-` deletes (softly). A
|
|
56
86
|
* counterparty a contract, a bank line or any other row still names is not deleted (§`refuseTiedRows`). Two
|
|
57
|
-
* counterparties with one business registration number are refused (§`numberClash`); a name may be shared.
|
|
87
|
+
* counterparties with one business registration number are refused (§`numberClash`); a name may be shared. Our own
|
|
88
|
+
* company is one counterparty with none of its facts (§`refuseSelfMisuse`).
|
|
58
89
|
*/
|
|
59
90
|
async function saveCounterparties(tx, domain, user, patches) {
|
|
60
91
|
const repo = tx.getRepository(counterparty_1.Counterparty);
|
|
61
92
|
const deletes = patches.filter(one => one.cuFlag === '-' && one.id).map(one => one.id);
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
const
|
|
93
|
+
const changed = patches.filter(one => one.id && one.cuFlag !== '-' && one.cuFlag !== '+');
|
|
94
|
+
const currents = new Map((changed.length ? await repo.find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(changed.map(one => one.id)) } }) : []).map(one => [one.id, one]));
|
|
95
|
+
const updates = changed.map(one => {
|
|
96
|
+
const current = currents.get(one.id);
|
|
97
|
+
if (!current)
|
|
98
|
+
throw new shell_1.Refusal('counterparty-not-found', {}, 'a counterparty was not found - reload and try again');
|
|
99
|
+
return { id: one.id, ...numbered(one, rowOf(one, false), current) };
|
|
100
|
+
});
|
|
101
|
+
const creates = patches
|
|
102
|
+
.filter(one => !one.id || one.cuFlag === '+')
|
|
103
|
+
.filter(one => one.cuFlag !== '-')
|
|
104
|
+
.map(one => numbered(one, rowOf(one, true)));
|
|
105
|
+
const existing = await repo.find({ where: { domain: { id: domain.id } } });
|
|
65
106
|
const clash = (0, counterparty_names_1.numberClash)(existing, [...updates, ...creates], deletes);
|
|
66
107
|
if (clash)
|
|
67
108
|
throw new shell_1.Refusal('counterparty-number-clash', clash, `business number ${clash.number} is already '${clash.existing}' - edit that counterparty instead`);
|
|
109
|
+
const after = new Map(existing.filter(one => !deletes.includes(one.id)).map(one => [one.id, one]));
|
|
110
|
+
for (const row of updates)
|
|
111
|
+
after.set(row.id, { ...after.get(row.id), ...row });
|
|
112
|
+
refuseSelfMisuse([...after.values(), ...creates], [...updates.map(one => after.get(one.id)), ...creates]);
|
|
68
113
|
if (deletes.length) {
|
|
69
114
|
const owned = await repo.find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(deletes) }, select: ['id'] });
|
|
70
115
|
if (owned.length !== deletes.length)
|
|
@@ -77,12 +122,8 @@ async function saveCounterparties(tx, domain, user, patches) {
|
|
|
77
122
|
await repo.softDelete({ id: (0, typeorm_1.In)(deletes) });
|
|
78
123
|
}
|
|
79
124
|
const saved = [];
|
|
80
|
-
for (const row of updates)
|
|
81
|
-
|
|
82
|
-
if (!current)
|
|
83
|
-
throw new shell_1.Refusal('counterparty-not-found', {}, 'a counterparty was not found - reload and try again');
|
|
84
|
-
saved.push(await repo.save({ ...current, ...row, updater: user }));
|
|
85
|
-
}
|
|
125
|
+
for (const row of updates)
|
|
126
|
+
saved.push(await repo.save({ ...currents.get(row.id), ...row, updater: user }));
|
|
86
127
|
for (const row of creates)
|
|
87
128
|
saved.push(await repo.save({ ...row, domain, creator: user, updater: user }));
|
|
88
129
|
for (const listener of listeners)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"counterparty-service.js","sourceRoot":"","sources":["../../../server/service/counterparty/counterparty-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { Refusal, refuseTiedRows } from '@things-factory/shell'\n\nimport { Counterparty, CounterpartyKind } from './counterparty'\nimport { CounterpartyBankAccount } from './counterparty-bank-account'\nimport { CounterpartyPatch } from './counterparty-type'\nimport { normalizeBusinessNumber, numberClash } from './counterparty-names'\n\n/**\n * Saving the counterparty list of a domain, and what an app does once it is saved.\n *\n * A module whose documents keep a counterparty's name (a contract, a bank line) links the rows that carry a newly\n * registered name — that is its rule, not this one's — by listening here (§`onCounterpartiesSaved`).\n */\n\ntype Saved = (tx: EntityManager, domain: { id: string }, saved: Counterparty[]) => Promise<void>\nconst listeners: Saved[] = []\n\n/** Run after every save of counterparties, in the same transaction. */\nexport function onCounterpartiesSaved(listener: Saved): void {\n listeners.push(listener)\n}\n\n/** The words people write for the kind, as well as the values themselves. */\nconst KIND_WORDS: Record<CounterpartyKind, string[]> = {\n [CounterpartyKind.COMPANY]: ['COMPANY', '회사', '법인', '사업자', '기업'],\n [CounterpartyKind.PERSON]: ['PERSON', '개인']\n}\n\nfunction kindOf(value: unknown): CounterpartyKind | undefined {\n const text = String(value ?? '').trim()\n if (!text) return undefined\n const found = (Object.keys(KIND_WORDS) as CounterpartyKind[]).find(kind => KIND_WORDS[kind].some(word => word.toLowerCase() === text.toLowerCase()))\n if (!found) throw new Refusal('counterparty-kind-invalid', { text }, `'${text}' is neither a company nor a person`)\n return found\n}\n\nconst TEXT = ['aliases', 'branchNumber', 'representative', 'address', 'billingAddress', 'businessType', 'businessItem', 'notes'] as const\n\n/** A patch as the row it writes: names trimmed, the kind and the business number in their stored form. */\nfunction rowOf(patch: Partial<CounterpartyPatch>, isNew: boolean): Partial<Counterparty> {\n const row: Record<string, any> = {}\n if (patch.name !== undefined || isNew) {\n const name = String(patch.name ?? '').trim()\n if (!name) throw new Refusal('counterparty-name-required', {}, 'a counterparty needs a name')\n row.name = name\n }\n for (const field of TEXT) if (patch[field] !== undefined) row[field] = String(patch[field] ?? '').trim() || null\n if (patch.kind !== undefined) row.kind = kindOf(patch.kind) ?? CounterpartyKind.COMPANY\n else if (isNew) row.kind = CounterpartyKind.COMPANY\n if (patch.businessNumber !== undefined) row.businessNumber = normalizeBusinessNumber(patch.businessNumber)\n if (patch.zeroRated !== undefined) row.zeroRated = !!patch.zeroRated\n if (patch.active !== undefined) row.active = patch.active !== false\n return row\n}\n\n/**\n * Apply grid rows to a domain's counterparties: `+` (or no id) registers, `M` changes, `-` deletes (softly). A\n * counterparty a contract, a bank line or any other row still names is not deleted (§`refuseTiedRows`). Two\n * counterparties with one business registration number are refused (§`numberClash`); a name may be shared.\n */\nexport async function saveCounterparties(tx: EntityManager, domain: { id: string }, user: any, patches: CounterpartyPatch[]): Promise<Counterparty[]> {\n const repo = tx.getRepository(Counterparty)\n const deletes = patches.filter(one => one.cuFlag === '-' && one.id).map(one => one.id!)\n const updates = patches.filter(one => one.id && one.cuFlag !== '-' && one.cuFlag !== '+').map(one => ({ id: one.id!, ...rowOf(one, false) }))\n const creates = patches.filter(one => !one.id || one.cuFlag === '+').filter(one => one.cuFlag !== '-').map(one => rowOf(one, true))\n\n const existing = await repo.find({ where: { domain: { id: domain.id } }, select: ['id', 'name', 'businessNumber'] })\n const clash = numberClash(existing, [...updates, ...creates], deletes)\n if (clash)\n throw new Refusal('counterparty-number-clash', clash, `business number ${clash.number} is already '${clash.existing}' - edit that counterparty instead`)\n\n if (deletes.length) {\n const owned = await repo.find({ where: { domain: { id: domain.id }, id: In(deletes) }, select: ['id'] })\n if (owned.length !== deletes.length) throw new Refusal('counterparty-not-found', {}, 'a counterparty was not found - reload and try again')\n /* Its accounts are its parts and go first, so they are not counted as tied - a number stays free for whoever holds it next. */\n const accounts = (await tx.getRepository(CounterpartyBankAccount).find({ where: { counterparty: { id: In(deletes) } }, select: ['id'] })).map(one => one.id)\n if (accounts.length) await tx.getRepository(CounterpartyBankAccount).softDelete({ id: In(accounts) })\n await refuseTiedRows(tx, Counterparty, deletes)\n await repo.softDelete({ id: In(deletes) })\n }\n\n const saved: Counterparty[] = []\n for (const row of updates) {\n const current = await repo.findOne({ where: { domain: { id: domain.id }, id: row.id } })\n if (!current) throw new Refusal('counterparty-not-found', {}, 'a counterparty was not found - reload and try again')\n saved.push(await repo.save({ ...current, ...row, updater: user }))\n }\n for (const row of creates) saved.push(await repo.save({ ...row, domain, creator: user, updater: user } as Counterparty))\n\n for (const listener of listeners) await listener(tx, domain, saved)\n return saved\n}\n"]}
|
|
1
|
+
{"version":3,"file":"counterparty-service.js","sourceRoot":"","sources":["../../../server/service/counterparty/counterparty-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { Refusal, refuseTiedRows } from '@things-factory/shell'\n\nimport { Counterparty, CounterpartyKind } from './counterparty'\nimport { CounterpartyBankAccount } from './counterparty-bank-account'\nimport { CounterpartyPatch } from './counterparty-type'\nimport { businessNumberOf, normalizeCountry, numberClash } from './counterparty-names'\n\n/**\n * Saving the counterparty list of a domain, and what an app does once it is saved.\n *\n * A module whose documents keep a counterparty's name (a contract, a bank line) links the rows that carry a newly\n * registered name — that is its rule, not this one's — by listening here (§`onCounterpartiesSaved`).\n */\n\ntype Saved = (tx: EntityManager, domain: { id: string }, saved: Counterparty[]) => Promise<void>\nconst listeners: Saved[] = []\n\n/** Run after every save of counterparties, in the same transaction. */\nexport function onCounterpartiesSaved(listener: Saved): void {\n listeners.push(listener)\n}\n\n/** The words people write for the kind, as well as the values themselves. */\nconst KIND_WORDS: Record<CounterpartyKind, string[]> = {\n [CounterpartyKind.COMPANY]: ['COMPANY', '사업자', '법인', '개인사업자', '회사', '기업'],\n [CounterpartyKind.INSTITUTION]: ['INSTITUTION', '기관', '공공기관', '관공서'],\n [CounterpartyKind.PERSON]: ['PERSON', '개인'],\n [CounterpartyKind.SELF]: ['SELF', '자사', '우리 회사']\n}\n\nfunction kindOf(value: unknown): CounterpartyKind | undefined {\n const text = String(value ?? '').trim()\n if (!text) return undefined\n const found = (Object.keys(KIND_WORDS) as CounterpartyKind[]).find(kind => KIND_WORDS[kind].some(word => word.toLowerCase() === text.toLowerCase()))\n if (!found) throw new Refusal('counterparty-kind-invalid', { text }, `'${text}' is not a business, an institution, a person or our own company`)\n return found\n}\n\nconst TEXT = ['aliases', 'branchNumber', 'representative', 'address', 'billingAddress', 'businessType', 'businessItem', 'notes'] as const\n\n/**\n * The facts of a company that 「우리 회사」 keeps (ADR-0176 decision 4). A `SELF` counterparty does not keep a copy of\n * them; it is our company's place in the list, by the names the bank writes it under (ADR-0132 §0-2).\n */\nconst COMPANY_FACTS = ['businessNumber', 'branchNumber', 'representative', 'address', 'billingAddress', 'businessType', 'businessItem'] as const\n\n/** A patch as the row it writes: names trimmed, the kind and the country in their stored form. The number is `numbered`'s. */\nfunction rowOf(patch: Partial<CounterpartyPatch>, isNew: boolean): Partial<Counterparty> {\n const row: Record<string, any> = {}\n if (patch.name !== undefined || isNew) {\n const name = String(patch.name ?? '').trim()\n if (!name) throw new Refusal('counterparty-name-required', {}, 'a counterparty needs a name')\n row.name = name\n }\n for (const field of TEXT) if (patch[field] !== undefined) row[field] = String(patch[field] ?? '').trim() || null\n if (patch.kind !== undefined) row.kind = kindOf(patch.kind) ?? CounterpartyKind.COMPANY\n else if (isNew) row.kind = CounterpartyKind.COMPANY\n if (patch.country !== undefined) row.country = normalizeCountry(patch.country)\n if (patch.zeroRated !== undefined) row.zeroRated = !!patch.zeroRated\n /* Sales abroad are zero-rated as a rule; the field stays, so a sale that is not says so (ADR-0132 §0-2). */\n else if (isNew && row.country) row.zeroRated = true\n if (patch.active !== undefined) row.active = patch.active !== false\n return row\n}\n\n/** The row with its business number in the stored form of its country - the country it has after the change. */\nfunction numbered(patch: Partial<CounterpartyPatch>, row: Partial<Counterparty>, current?: Partial<Counterparty>): Partial<Counterparty> {\n if (patch.businessNumber !== undefined) return { ...row, businessNumber: businessNumberOf(patch.businessNumber, 'country' in row ? row.country : current?.country) }\n return row\n}\n\n/**\n * A `SELF` counterparty holds none of our company's facts, and a domain has one. `rows` are all the rows after the\n * batch, `written` the ones it writes - only what the batch writes is refused, as with the business number.\n */\nfunction refuseSelfMisuse(rows: readonly Partial<Counterparty>[], written: readonly Partial<Counterparty>[]) {\n const selves = rows.filter(one => one.kind === CounterpartyKind.SELF)\n for (const one of written.filter(one => one.kind === CounterpartyKind.SELF)) {\n const kept = COMPANY_FACTS.filter(field => one[field])\n if (kept.length)\n throw new Refusal('counterparty-self-facts', { name: one.name, fields: kept.join(', ') }, `'${one.name}' is our own company - its ${kept.join(', ')} are kept in 「우리 회사」, not here`)\n }\n if (selves.length > 1 && written.some(one => one.kind === CounterpartyKind.SELF))\n throw new Refusal('counterparty-self-taken', { existing: selves[0].name, name: selves[1].name }, `'${selves[0].name}' is already our own company - a domain has one`)\n}\n\n/**\n * Apply grid rows to a domain's counterparties: `+` (or no id) registers, `M` changes, `-` deletes (softly). A\n * counterparty a contract, a bank line or any other row still names is not deleted (§`refuseTiedRows`). Two\n * counterparties with one business registration number are refused (§`numberClash`); a name may be shared. Our own\n * company is one counterparty with none of its facts (§`refuseSelfMisuse`).\n */\nexport async function saveCounterparties(tx: EntityManager, domain: { id: string }, user: any, patches: CounterpartyPatch[]): Promise<Counterparty[]> {\n const repo = tx.getRepository(Counterparty)\n const deletes = patches.filter(one => one.cuFlag === '-' && one.id).map(one => one.id!)\n const changed = patches.filter(one => one.id && one.cuFlag !== '-' && one.cuFlag !== '+')\n const currents = new Map(\n (changed.length ? await repo.find({ where: { domain: { id: domain.id }, id: In(changed.map(one => one.id!)) } }) : []).map(one => [one.id, one])\n )\n const updates = changed.map(one => {\n const current = currents.get(one.id!)\n if (!current) throw new Refusal('counterparty-not-found', {}, 'a counterparty was not found - reload and try again')\n return { id: one.id!, ...numbered(one, rowOf(one, false), current) }\n })\n const creates = patches\n .filter(one => !one.id || one.cuFlag === '+')\n .filter(one => one.cuFlag !== '-')\n .map(one => numbered(one, rowOf(one, true)))\n\n const existing = await repo.find({ where: { domain: { id: domain.id } } })\n const clash = numberClash(existing, [...updates, ...creates], deletes)\n if (clash)\n throw new Refusal('counterparty-number-clash', clash, `business number ${clash.number} is already '${clash.existing}' - edit that counterparty instead`)\n\n const after = new Map<string, Partial<Counterparty>>(existing.filter(one => !deletes.includes(one.id)).map(one => [one.id, one]))\n for (const row of updates) after.set(row.id, { ...after.get(row.id), ...row })\n refuseSelfMisuse([...after.values(), ...creates], [...updates.map(one => after.get(one.id)!), ...creates])\n\n if (deletes.length) {\n const owned = await repo.find({ where: { domain: { id: domain.id }, id: In(deletes) }, select: ['id'] })\n if (owned.length !== deletes.length) throw new Refusal('counterparty-not-found', {}, 'a counterparty was not found - reload and try again')\n /* Its accounts are its parts and go first, so they are not counted as tied - a number stays free for whoever holds it next. */\n const accounts = (await tx.getRepository(CounterpartyBankAccount).find({ where: { counterparty: { id: In(deletes) } }, select: ['id'] })).map(one => one.id)\n if (accounts.length) await tx.getRepository(CounterpartyBankAccount).softDelete({ id: In(accounts) })\n await refuseTiedRows(tx, Counterparty, deletes)\n await repo.softDelete({ id: In(deletes) })\n }\n\n const saved: Counterparty[] = []\n for (const row of updates) saved.push(await repo.save({ ...currents.get(row.id)!, ...row, updater: user }))\n for (const row of creates) saved.push(await repo.save({ ...row, domain, creator: user, updater: user } as Counterparty))\n\n for (const listener of listeners) await listener(tx, domain, saved)\n return saved\n}\n"]}
|
|
@@ -3,6 +3,7 @@ export declare class NewCounterparty {
|
|
|
3
3
|
name: string;
|
|
4
4
|
aliases?: string;
|
|
5
5
|
kind?: CounterpartyKind;
|
|
6
|
+
country?: string;
|
|
6
7
|
businessNumber?: string;
|
|
7
8
|
branchNumber?: string;
|
|
8
9
|
representative?: string;
|
|
@@ -20,6 +21,7 @@ export declare class CounterpartyPatch {
|
|
|
20
21
|
name?: string;
|
|
21
22
|
aliases?: string;
|
|
22
23
|
kind?: string;
|
|
24
|
+
country?: string;
|
|
23
25
|
businessNumber?: string;
|
|
24
26
|
branchNumber?: string;
|
|
25
27
|
representative?: string;
|
|
@@ -16,9 +16,13 @@ tslib_1.__decorate([
|
|
|
16
16
|
tslib_1.__metadata("design:type", String)
|
|
17
17
|
], NewCounterparty.prototype, "aliases", void 0);
|
|
18
18
|
tslib_1.__decorate([
|
|
19
|
-
(0, type_graphql_1.Field)(type => counterparty_1.CounterpartyKind, { nullable: true, description: 'A
|
|
19
|
+
(0, type_graphql_1.Field)(type => counterparty_1.CounterpartyKind, { nullable: true, description: 'A business, an institution, a person, or our own company' }),
|
|
20
20
|
tslib_1.__metadata("design:type", String)
|
|
21
21
|
], NewCounterparty.prototype, "kind", void 0);
|
|
22
|
+
tslib_1.__decorate([
|
|
23
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Country, ISO 3166-1 alpha-2; empty for one at home' }),
|
|
24
|
+
tslib_1.__metadata("design:type", String)
|
|
25
|
+
], NewCounterparty.prototype, "country", void 0);
|
|
22
26
|
tslib_1.__decorate([
|
|
23
27
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Business registration number' }),
|
|
24
28
|
tslib_1.__metadata("design:type", String)
|
|
@@ -82,9 +86,13 @@ tslib_1.__decorate([
|
|
|
82
86
|
tslib_1.__metadata("design:type", String)
|
|
83
87
|
], CounterpartyPatch.prototype, "aliases", void 0);
|
|
84
88
|
tslib_1.__decorate([
|
|
85
|
-
(0, type_graphql_1.Field)({ nullable: true, description: 'COMPANY or
|
|
89
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'COMPANY, INSTITUTION, PERSON or SELF, or the words people write for them' }),
|
|
86
90
|
tslib_1.__metadata("design:type", String)
|
|
87
91
|
], CounterpartyPatch.prototype, "kind", void 0);
|
|
92
|
+
tslib_1.__decorate([
|
|
93
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Country, ISO 3166-1 alpha-2; empty for one at home' }),
|
|
94
|
+
tslib_1.__metadata("design:type", String)
|
|
95
|
+
], CounterpartyPatch.prototype, "country", void 0);
|
|
88
96
|
tslib_1.__decorate([
|
|
89
97
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Business registration number' }),
|
|
90
98
|
tslib_1.__metadata("design:type", String)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"counterparty-type.js","sourceRoot":"","sources":["../../../server/service/counterparty/counterparty-type.ts"],"names":[],"mappings":";;;;AAAA,+CAAoE;AAEpE,iDAA+D;AAGxD,IAAM,eAAe,GAArB,MAAM,eAAe;
|
|
1
|
+
{"version":3,"file":"counterparty-type.js","sourceRoot":"","sources":["../../../server/service/counterparty/counterparty-type.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, ID, InputType, Int, ObjectType } from 'type-graphql'\n\nimport { Counterparty, CounterpartyKind } from './counterparty'\n\n@InputType({ description: 'A counterparty to register' })\nexport class NewCounterparty {\n @Field({ description: 'The name the business knows it by' }) name: string\n @Field({ nullable: true, description: 'Other names it appears under' }) aliases?: string\n @Field(type => CounterpartyKind, { nullable: true, description: 'A business, an institution, a person, or our own company' }) kind?: CounterpartyKind\n @Field({ nullable: true, description: 'Country, ISO 3166-1 alpha-2; empty for one at home' }) country?: string\n @Field({ nullable: true, description: 'Business registration number' }) businessNumber?: string\n @Field({ nullable: true, description: 'Sub-business place number' }) branchNumber?: string\n @Field({ nullable: true, description: 'Representative' }) representative?: string\n @Field({ nullable: true, description: 'Address' }) address?: string\n @Field({ nullable: true, description: 'Billing address' }) billingAddress?: string\n @Field({ nullable: true, description: 'Business type (업태)' }) businessType?: string\n @Field({ nullable: true, description: 'Business item (종목)' }) businessItem?: string\n @Field({ nullable: true, description: 'Whether sales to it are zero-rated' }) zeroRated?: boolean\n @Field({ nullable: true, description: 'Notes' }) notes?: string\n @Field({ nullable: true, description: 'Whether it is still offered for new rows' }) active?: boolean\n}\n\n@InputType({ description: 'A change to a counterparty, or a row of the counterparty grid (cuFlag + for new, M for changed, - for deleted)' })\nexport class CounterpartyPatch {\n @Field(type => ID, { nullable: true, description: 'The counterparty; empty for a new one' }) id?: string\n @Field({ nullable: true, description: 'Grid flag: + new, M changed, - deleted' }) cuFlag?: string\n @Field({ nullable: true, description: 'Name' }) name?: string\n @Field({ nullable: true, description: 'Other names it appears under' }) aliases?: string\n @Field({ nullable: true, description: 'COMPANY, INSTITUTION, PERSON or SELF, or the words people write for them' }) kind?: string\n @Field({ nullable: true, description: 'Country, ISO 3166-1 alpha-2; empty for one at home' }) country?: string\n @Field({ nullable: true, description: 'Business registration number' }) businessNumber?: string\n @Field({ nullable: true, description: 'Sub-business place number' }) branchNumber?: string\n @Field({ nullable: true, description: 'Representative' }) representative?: string\n @Field({ nullable: true, description: 'Address' }) address?: string\n @Field({ nullable: true, description: 'Billing address' }) billingAddress?: string\n @Field({ nullable: true, description: 'Business type (업태)' }) businessType?: string\n @Field({ nullable: true, description: 'Business item (종목)' }) businessItem?: string\n @Field({ nullable: true, description: 'Whether sales to it are zero-rated' }) zeroRated?: boolean\n @Field({ nullable: true, description: 'Notes' }) notes?: string\n @Field({ nullable: true, description: 'Whether it is still offered for new rows' }) active?: boolean\n}\n\n@ObjectType({ description: 'A page of counterparties' })\nexport class CounterpartyList {\n @Field(type => [Counterparty], { description: 'Counterparties on the page' }) items: Counterparty[]\n @Field(type => Int, { description: 'How many match' }) total: number\n}\n\n@InputType({ description: 'A change to a counterparty account, or a row of its grid (cuFlag + for new, M for changed, - for deleted)' })\nexport class CounterpartyBankAccountPatch {\n @Field(type => ID, { nullable: true, description: 'The account; empty for a new one' }) id?: string\n @Field({ nullable: true, description: 'Grid flag: + new, M changed, - deleted' }) cuFlag?: string\n @Field({ nullable: true, description: 'The counterparty the account is of - needed for a new one' }) counterpartyId?: string\n @Field({ nullable: true, description: 'The bank' }) bankName?: string\n @Field({ nullable: true, description: 'The account number, as written - its digits are kept' }) accountNumber?: string\n @Field({ nullable: true, description: 'The account holder (예금주)' }) holderName?: string\n @Field({ nullable: true, description: 'Notes' }) notes?: string\n}\n"]}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { User } from '@things-factory/auth-base';
|
|
2
2
|
import { Domain } from '@things-factory/shell';
|
|
3
|
+
/**
|
|
4
|
+
* Who a counterparty is (ADR-0132 §0-2). `COMPANY` is any business with a registration number - a corporation, a
|
|
5
|
+
* sole proprietor, an association; `INSTITUTION` a state body or public institution (a tax office, a public
|
|
6
|
+
* corporation); `PERSON` someone who is not a business; `SELF` the domain's own company, at most one - the other side
|
|
7
|
+
* of money moved between its own accounts. Where a counterparty is from is `country`, not a kind.
|
|
8
|
+
*/
|
|
3
9
|
export declare enum CounterpartyKind {
|
|
4
10
|
COMPANY = "COMPANY",
|
|
5
|
-
|
|
11
|
+
INSTITUTION = "INSTITUTION",
|
|
12
|
+
PERSON = "PERSON",
|
|
13
|
+
SELF = "SELF"
|
|
6
14
|
}
|
|
7
15
|
/**
|
|
8
16
|
* A company or person a domain signs with, bills, is billed by, or pays — its own list of them.
|
|
@@ -23,6 +31,7 @@ export declare class Counterparty {
|
|
|
23
31
|
name: string;
|
|
24
32
|
aliases?: string;
|
|
25
33
|
kind: CounterpartyKind;
|
|
34
|
+
country?: string;
|
|
26
35
|
businessNumber?: string;
|
|
27
36
|
branchNumber?: string;
|
|
28
37
|
representative?: string;
|
|
@@ -6,14 +6,22 @@ const type_graphql_1 = require("type-graphql");
|
|
|
6
6
|
const typeorm_1 = require("typeorm");
|
|
7
7
|
const auth_base_1 = require("@things-factory/auth-base");
|
|
8
8
|
const shell_1 = require("@things-factory/shell");
|
|
9
|
+
/**
|
|
10
|
+
* Who a counterparty is (ADR-0132 §0-2). `COMPANY` is any business with a registration number - a corporation, a
|
|
11
|
+
* sole proprietor, an association; `INSTITUTION` a state body or public institution (a tax office, a public
|
|
12
|
+
* corporation); `PERSON` someone who is not a business; `SELF` the domain's own company, at most one - the other side
|
|
13
|
+
* of money moved between its own accounts. Where a counterparty is from is `country`, not a kind.
|
|
14
|
+
*/
|
|
9
15
|
var CounterpartyKind;
|
|
10
16
|
(function (CounterpartyKind) {
|
|
11
17
|
CounterpartyKind["COMPANY"] = "COMPANY";
|
|
18
|
+
CounterpartyKind["INSTITUTION"] = "INSTITUTION";
|
|
12
19
|
CounterpartyKind["PERSON"] = "PERSON";
|
|
20
|
+
CounterpartyKind["SELF"] = "SELF";
|
|
13
21
|
})(CounterpartyKind || (exports.CounterpartyKind = CounterpartyKind = {}));
|
|
14
22
|
(0, type_graphql_1.registerEnumType)(CounterpartyKind, {
|
|
15
23
|
name: 'CounterpartyKind',
|
|
16
|
-
description: '
|
|
24
|
+
description: 'A business, a state or public institution, a person, or the domain\'s own company'
|
|
17
25
|
});
|
|
18
26
|
/**
|
|
19
27
|
* A company or person a domain signs with, bills, is billed by, or pays — its own list of them.
|
|
@@ -59,9 +67,14 @@ tslib_1.__decorate([
|
|
|
59
67
|
], Counterparty.prototype, "aliases", void 0);
|
|
60
68
|
tslib_1.__decorate([
|
|
61
69
|
(0, typeorm_1.Column)({ default: CounterpartyKind.COMPANY }),
|
|
62
|
-
(0, type_graphql_1.Field)(type => CounterpartyKind, { description: 'A
|
|
70
|
+
(0, type_graphql_1.Field)(type => CounterpartyKind, { description: 'A business, an institution, a person, or our own company' }),
|
|
63
71
|
tslib_1.__metadata("design:type", String)
|
|
64
72
|
], Counterparty.prototype, "kind", void 0);
|
|
73
|
+
tslib_1.__decorate([
|
|
74
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
75
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Country, ISO 3166-1 alpha-2 (US, JP); empty for one at home' }),
|
|
76
|
+
tslib_1.__metadata("design:type", String)
|
|
77
|
+
], Counterparty.prototype, "country", void 0);
|
|
65
78
|
tslib_1.__decorate([
|
|
66
79
|
(0, typeorm_1.Column)({ nullable: true }),
|
|
67
80
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Business registration number, 123-45-67890 - one counterparty\'s in the domain' }),
|