@things-factory/biz-base 10.1.149 → 10.1.154

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/biz-base",
3
- "version": "10.1.149",
3
+ "version": "10.1.154",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "client/index.js",
6
6
  "exports": {
@@ -41,7 +41,7 @@
41
41
  "test": "npm run build && node --test tests/*.test.mjs tests/*.test.cjs"
42
42
  },
43
43
  "dependencies": {
44
- "@things-factory/auth-base": "^10.1.149"
44
+ "@things-factory/auth-base": "^10.1.154"
45
45
  },
46
- "gitHead": "816f88a9fa6110935a3f71740458db8f7469d2e3"
46
+ "gitHead": "f28579aa7131cbb282eb8813f8b285d9d29bb525"
47
47
  }
@@ -5,7 +5,7 @@ import { createRequire } from 'node:module'
5
5
  /* How names point at counterparties - pure rules, moved from Office with the tests that held them there. */
6
6
 
7
7
  const require = createRequire(import.meta.url)
8
- const { frequentUnmatched, linkFor, looksLikePersonName, matcherFor, nameClash, nameKey, namesToRegister, normalizeBusinessNumber, partiesFromNames, partyNamed } = require('../dist-server/service/counterparty/counterparty-names.js')
8
+ const { frequentUnmatched, linkFor, looksLikePersonName, matcherFor, nameKey, namesToRegister, normalizeBusinessNumber, numberClash, partiesFromNames, partyNamed } = require('../dist-server/service/counterparty/counterparty-names.js')
9
9
 
10
10
  test('a counterparty is found by its name or an alias', () => {
11
11
  const parties = [{ id: 'a', name: '니어솔루션', aliases: '니어, 제이투-니어' }, { id: 'b', name: '한빛' }]
@@ -31,11 +31,32 @@ test('counterparty names match the way people mean them', () => {
31
31
  assert.deepEqual(linkFor({ description: 'x' }, byId, byName), {})
32
32
  assert.throws(() => linkFor({ counterparty: { id: 'nope' } }, byId, byName), /not found/)
33
33
 
34
- assert.deepEqual(nameClash(known, [{ name: '(주)한빛' }]), ['한빛', '(주)한빛'])
35
- assert.equal(nameClash(known, [{ id: 'c1', name: '한빛테크' }, { name: '한빛' }]), undefined)
36
34
  assert.deepEqual(namesToRegister(['한빛', ' 새회사 ', '(주)새회사', '', null], known), ['새회사'])
37
35
  })
38
36
 
37
+ test('a business number is one counterparty - a name may be shared', () => {
38
+ const known = [
39
+ { id: 'k1', name: '(주)경암 성남', businessNumber: '166-85-01719' },
40
+ { id: 'k2', name: '(주)경암 성남', businessNumber: '541-85-00963' },
41
+ { id: 'w', name: '웰스테크' }
42
+ ]
43
+ // the same name under another number, or under none, is not refused
44
+ assert.equal(numberClash(known, [{ name: '(주)경암 성남', businessNumber: '222-22-22222' }]), undefined)
45
+ assert.equal(numberClash(known, [{ name: '웰스테크' }]), undefined)
46
+ // a number another counterparty holds is, by registering or by changing to it
47
+ assert.deepEqual(numberClash(known, [{ name: '경암', businessNumber: '166-85-01719' }]), { number: '166-85-01719', existing: '(주)경암 성남', name: '경암' })
48
+ assert.deepEqual(numberClash(known, [{ id: 'w', businessNumber: '541-85-00963' }]), { number: '541-85-00963', existing: '(주)경암 성남', name: '웰스테크' })
49
+ // changing another field of a holder, or deleting the holder first, is not
50
+ assert.equal(numberClash(known, [{ id: 'k1', name: '경암 성남' }]), undefined)
51
+ assert.equal(numberClash(known, [{ name: '경암', businessNumber: '166-85-01719' }], ['k1']), undefined)
52
+ })
53
+
54
+ test('a name two parties go by points at neither', () => {
55
+ const named = partyNamed([{ id: 'a', name: '(주)경암 성남' }, { id: 'b', name: '경암 성남' }, { id: 'c', name: '한빛', aliases: '빛' }])
56
+ assert.equal(named('경암성남'), undefined)
57
+ assert.equal(named('(주)한빛')?.id, 'c')
58
+ })
59
+
39
60
  test('a business registration number is ten digits, stored with dashes', () => {
40
61
  assert.equal(normalizeBusinessNumber('1234567890'), '123-45-67890')
41
62
  assert.equal(normalizeBusinessNumber('123-45-67890'), '123-45-67890')
@@ -3,8 +3,8 @@ import assert from 'node:assert/strict'
3
3
  import { createRequire } from 'node:module'
4
4
 
5
5
  /*
6
- * A domain's own counterparty list (ADR-0122, the counterparty concept map): one domain's list is its own, names that
7
- * mean the same party are one counterparty, and the module that keeps names links rows once a name is registered.
6
+ * A domain's own counterparty list (ADR-0122, the counterparty concept map): one domain's list is its own, a business
7
+ * number is one counterparty and a name may be shared, and the module that keeps names links rows once a name is registered.
8
8
  * An in-memory SQLite source; no application database is opened.
9
9
  */
10
10
 
@@ -42,20 +42,29 @@ test('each domain keeps its own list - the same name may be registered in two do
42
42
  assert.equal(mine[0].kind, 'COMPANY', 'the words people write for the kind are taken')
43
43
  })
44
44
 
45
- test('names that mean the same party are refused, within one domain', async () => {
46
- await assert.rejects(save([{ name: '한빛' }]), { code: 'counterparty-name-clash' })
47
- await assert.rejects(save([{ name: '주식회사 한빛' }]), { code: 'counterparty-name-clash' })
45
+ test('a business number is one counterparty in the domain; a name may be shared', async () => {
46
+ await assert.rejects(save([{ name: '한빛', businessNumber: '123-45-67890' }]), { code: 'counterparty-number-clash' })
47
+ const [same] = await save([{ name: '주식회사 한빛', businessNumber: '2223344444' }])
48
+ assert.equal(same.name, '주식회사 한빛', 'another business under the same name')
49
+ await save([{ id: same.id, cuFlag: '-' }])
48
50
  await assert.rejects(save([{ name: ' ' }]), { code: 'counterparty-name-required' })
49
51
  await assert.rejects(save([{ name: '새 거래처', businessNumber: '12-34' }]), { code: 'business-number-invalid' })
50
52
  })
51
53
 
52
- test('a deleted counterparty frees its name, and another domain cannot change or delete mine', async () => {
53
- const [mine] = await db.getRepository(biz.Counterparty).find({ where: { domain: { id: domain.id } } })
54
+ test('a deleted counterparty frees its number, and another domain cannot change or delete mine', async () => {
55
+ const [mine] = await db.getRepository(biz.Counterparty).find({ where: { domain: { id: domain.id }, businessNumber: '123-45-67890' } })
54
56
  await assert.rejects(save([{ id: mine.id, cuFlag: 'M', notes: 'x' }], other), { code: 'counterparty-not-found' })
55
57
  await assert.rejects(save([{ id: mine.id, cuFlag: '-' }], other), { code: 'counterparty-not-found' })
56
58
  await save([{ id: mine.id, cuFlag: '-' }])
57
- const [again] = await save([{ name: '한빛' }])
58
- assert.equal(again.name, '한빛')
59
+ const [again] = await save([{ name: '한빛', businessNumber: '1234567890' }])
60
+ assert.equal(again.businessNumber, '123-45-67890')
61
+ })
62
+
63
+ test('the database holds a business number to one live counterparty, whatever writes it', async () => {
64
+ const repo = db.getRepository(biz.Counterparty)
65
+ await assert.rejects(repo.save({ domain, name: '다른 이름', businessNumber: '123-45-67890' }), /UNIQUE/)
66
+ await repo.save({ domain, name: '한빛', businessNumber: null })
67
+ await repo.save({ domain, name: '한빛', businessNumber: null })
59
68
  })
60
69
 
61
70
  test('a module that keeps names hears every save, in the same transaction', async () => {
@@ -11,7 +11,7 @@
11
11
  "field.company": "company",
12
12
  "error.no x found": "no {x} found",
13
13
  "error.counterparty-name-required": "Give the counterparty a name.",
14
- "error.counterparty-name-clash": "'{name}' is the same counterparty as '{existing}'. Edit that one instead.",
14
+ "error.counterparty-number-clash": "Business number {number} already belongs to '{existing}'. Edit that counterparty instead.",
15
15
  "error.counterparty-not-found": "The counterparty was not found. Reload it.",
16
16
  "error.counterparty-kind-invalid": "'{text}' is neither a company nor a person.",
17
17
  "error.business-number-invalid": "'{text}' is not a business registration number. It has 10 digits."
@@ -11,7 +11,7 @@
11
11
  "field.company": "empresa",
12
12
  "error.no x found": "no se encontró {x}",
13
13
  "error.counterparty-name-required": "Asigne un nombre a la contraparte.",
14
- "error.counterparty-name-clash": "'{name}' es la misma contraparte que '{existing}'. Edite esa en su lugar.",
14
+ "error.counterparty-number-clash": "El número de empresa {number} ya pertenece a '{existing}'. Edite esa contraparte en su lugar.",
15
15
  "error.counterparty-not-found": "No se encontró la contraparte. Vuelva a cargarla.",
16
16
  "error.counterparty-kind-invalid": "'{text}' no es ni una empresa ni una persona.",
17
17
  "error.business-number-invalid": "'{text}' no es un número de registro empresarial. Debe tener 10 dígitos."
@@ -9,5 +9,6 @@
9
9
  "field.postal_code": "郵便番号",
10
10
  "field.latlng": "緯度/経度",
11
11
  "field.company": "会社",
12
+ "error.counterparty-number-clash": "事業者番号 {number} はすでに取引先 '{existing}' の番号です。その取引先を編集してください。",
12
13
  "error.no x found": "{x}が見つかりません"
13
14
  }
@@ -11,7 +11,7 @@
11
11
  "field.company": "회사",
12
12
  "error.no x found": "발견된 {x}이(가) 없습니다",
13
13
  "error.counterparty-name-required": "거래처 이름을 넣으세요.",
14
- "error.counterparty-name-clash": "'{name}'은(는) 이미 있는 거래처 '{existing}'와 같습니다. 그 거래처를 고치세요.",
14
+ "error.counterparty-number-clash": "사업자번호 {number}은(는) 이미 거래처 '{existing}'의 번호입니다. 그 거래처를 고치세요.",
15
15
  "error.counterparty-not-found": "거래처를 찾을 수 없습니다. 다시 불러오세요.",
16
16
  "error.counterparty-kind-invalid": "'{text}'은(는) 회사도 개인도 아닙니다.",
17
17
  "error.business-number-invalid": "'{text}'은(는) 사업자등록번호가 아닙니다. 사업자등록번호는 숫자 10자리입니다."
@@ -9,5 +9,6 @@
9
9
  "field.postal_code": "poskod",
10
10
  "field.latlng": "latitud/longitud",
11
11
  "field.company": "syarikat",
12
+ "error.counterparty-number-clash": "Nombor pendaftaran perniagaan {number} sudah milik '{existing}'. Sunting rakan niaga itu sahaja.",
12
13
  "error.no x found": "tiada {x} yang dijumpai"
13
14
  }
@@ -9,5 +9,6 @@
9
9
  "field.postal_code": "邮政编码",
10
10
  "field.latlng": "纬度/经度",
11
11
  "field.company": "公司",
12
+ "error.counterparty-number-clash": "营业登记号 {number} 已属于往来单位 '{existing}'。请改为编辑该往来单位。",
12
13
  "error.no x found": "没有找到{x}"
13
14
  }