@things-factory/shell 10.1.122 → 10.1.123
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/index.d.ts +1 -0
- package/dist-server/index.js +1 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/typeorm/money-column.d.ts +34 -0
- package/dist-server/typeorm/money-column.js +43 -0
- package/dist-server/typeorm/money-column.js.map +1 -0
- package/package.json +3 -3
- package/tests/money-column.test.ts +34 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ColumnOptions, ValueTransformer } from 'typeorm';
|
|
2
|
+
/**
|
|
3
|
+
* `decimal` 컬럼을 숫자로 읽고 쓰는 변환기.
|
|
4
|
+
*
|
|
5
|
+
* Postgres·MySQL 은 `decimal` 을 **문자열**로 돌려준다(정밀도를 지키려고). 그대로 두면
|
|
6
|
+
* GraphQL `Float` 필드와 합산 코드가 `'1200.00' + 300` 같은 글자 이어붙이기를 한다.
|
|
7
|
+
* 그래서 읽을 때 숫자로 바꾼다. 비어 있음(`null`·`undefined`)은 그대로 둔다 — 0 으로 만들지 않는다.
|
|
8
|
+
*/
|
|
9
|
+
export declare const decimalTransformer: ValueTransformer;
|
|
10
|
+
/**
|
|
11
|
+
* 금액 컬럼 — `decimal(18, 2)`.
|
|
12
|
+
*
|
|
13
|
+
* ## 왜 `decimal` 인가
|
|
14
|
+
*
|
|
15
|
+
* - 형식을 적지 않은 `number` 컬럼은 32비트 정수가 된다. 원 단위로는 정확하지만 한 칸에
|
|
16
|
+
* 약 21억까지이고 소수(센트)를 못 담는다.
|
|
17
|
+
* - `float` 은 0.1 을 정확히 담지 못한다. 수천 줄을 더하면 은행 잔액과 어긋난다.
|
|
18
|
+
* - `decimal` 은 SQL 표준이라 다섯 드라이버(postgres·mysql/mariadb·sqlite·mssql·oracle)가 모두 받는다.
|
|
19
|
+
* 18자리 중 소수 2자리 — 원화 금액 전부와 외화 센트를 담는다.
|
|
20
|
+
*
|
|
21
|
+
* sqlite 는 자릿수를 강제하지 않는다(NUMERIC 친화형). 정수 원화는 정확하지만 소수 금액은
|
|
22
|
+
* 내부에서 실수로 담길 수 있다 — 소수 금액의 정확성은 서버 DB 를 전제로 한다.
|
|
23
|
+
*
|
|
24
|
+
* ## 쓰는 법
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* @Column({ nullable: true, ...moneyColumn() })
|
|
28
|
+
* @Field(type => Float, { nullable: true })
|
|
29
|
+
* supplyAmount?: number
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* `nullable`·`default` 같은 나머지 옵션은 호출하는 쪽이 정한다. GraphQL 필드는 `Float` 로 둔다.
|
|
33
|
+
*/
|
|
34
|
+
export declare function moneyColumn(options?: ColumnOptions): ColumnOptions;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decimalTransformer = void 0;
|
|
4
|
+
exports.moneyColumn = moneyColumn;
|
|
5
|
+
/**
|
|
6
|
+
* `decimal` 컬럼을 숫자로 읽고 쓰는 변환기.
|
|
7
|
+
*
|
|
8
|
+
* Postgres·MySQL 은 `decimal` 을 **문자열**로 돌려준다(정밀도를 지키려고). 그대로 두면
|
|
9
|
+
* GraphQL `Float` 필드와 합산 코드가 `'1200.00' + 300` 같은 글자 이어붙이기를 한다.
|
|
10
|
+
* 그래서 읽을 때 숫자로 바꾼다. 비어 있음(`null`·`undefined`)은 그대로 둔다 — 0 으로 만들지 않는다.
|
|
11
|
+
*/
|
|
12
|
+
exports.decimalTransformer = {
|
|
13
|
+
to: (value) => (value === undefined ? undefined : value === null || value === '' ? null : Number(value)),
|
|
14
|
+
from: (value) => (value === null || value === undefined ? value : Number(value))
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* 금액 컬럼 — `decimal(18, 2)`.
|
|
18
|
+
*
|
|
19
|
+
* ## 왜 `decimal` 인가
|
|
20
|
+
*
|
|
21
|
+
* - 형식을 적지 않은 `number` 컬럼은 32비트 정수가 된다. 원 단위로는 정확하지만 한 칸에
|
|
22
|
+
* 약 21억까지이고 소수(센트)를 못 담는다.
|
|
23
|
+
* - `float` 은 0.1 을 정확히 담지 못한다. 수천 줄을 더하면 은행 잔액과 어긋난다.
|
|
24
|
+
* - `decimal` 은 SQL 표준이라 다섯 드라이버(postgres·mysql/mariadb·sqlite·mssql·oracle)가 모두 받는다.
|
|
25
|
+
* 18자리 중 소수 2자리 — 원화 금액 전부와 외화 센트를 담는다.
|
|
26
|
+
*
|
|
27
|
+
* sqlite 는 자릿수를 강제하지 않는다(NUMERIC 친화형). 정수 원화는 정확하지만 소수 금액은
|
|
28
|
+
* 내부에서 실수로 담길 수 있다 — 소수 금액의 정확성은 서버 DB 를 전제로 한다.
|
|
29
|
+
*
|
|
30
|
+
* ## 쓰는 법
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* @Column({ nullable: true, ...moneyColumn() })
|
|
34
|
+
* @Field(type => Float, { nullable: true })
|
|
35
|
+
* supplyAmount?: number
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* `nullable`·`default` 같은 나머지 옵션은 호출하는 쪽이 정한다. GraphQL 필드는 `Float` 로 둔다.
|
|
39
|
+
*/
|
|
40
|
+
function moneyColumn(options = {}) {
|
|
41
|
+
return { type: 'decimal', precision: 18, scale: 2, transformer: exports.decimalTransformer, ...options };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=money-column.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money-column.js","sourceRoot":"","sources":["../../server/typeorm/money-column.ts"],"names":[],"mappings":";;;AAsCA,kCAEC;AAtCD;;;;;;GAMG;AACU,QAAA,kBAAkB,GAAqB;IAClD,EAAE,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjH,IAAI,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;CAC1F,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,SAAgB,WAAW,CAAC,UAAyB,EAAE;IACrD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,0BAAkB,EAAE,GAAG,OAAO,EAAE,CAAA;AAClG,CAAC","sourcesContent":["import { ColumnOptions, ValueTransformer } from 'typeorm'\n\n/**\n * `decimal` 컬럼을 숫자로 읽고 쓰는 변환기.\n *\n * Postgres·MySQL 은 `decimal` 을 **문자열**로 돌려준다(정밀도를 지키려고). 그대로 두면\n * GraphQL `Float` 필드와 합산 코드가 `'1200.00' + 300` 같은 글자 이어붙이기를 한다.\n * 그래서 읽을 때 숫자로 바꾼다. 비어 있음(`null`·`undefined`)은 그대로 둔다 — 0 으로 만들지 않는다.\n */\nexport const decimalTransformer: ValueTransformer = {\n to: (value: unknown) => (value === undefined ? undefined : value === null || value === '' ? null : Number(value)),\n from: (value: unknown) => (value === null || value === undefined ? value : Number(value))\n}\n\n/**\n * 금액 컬럼 — `decimal(18, 2)`.\n *\n * ## 왜 `decimal` 인가\n *\n * - 형식을 적지 않은 `number` 컬럼은 32비트 정수가 된다. 원 단위로는 정확하지만 한 칸에\n * 약 21억까지이고 소수(센트)를 못 담는다.\n * - `float` 은 0.1 을 정확히 담지 못한다. 수천 줄을 더하면 은행 잔액과 어긋난다.\n * - `decimal` 은 SQL 표준이라 다섯 드라이버(postgres·mysql/mariadb·sqlite·mssql·oracle)가 모두 받는다.\n * 18자리 중 소수 2자리 — 원화 금액 전부와 외화 센트를 담는다.\n *\n * sqlite 는 자릿수를 강제하지 않는다(NUMERIC 친화형). 정수 원화는 정확하지만 소수 금액은\n * 내부에서 실수로 담길 수 있다 — 소수 금액의 정확성은 서버 DB 를 전제로 한다.\n *\n * ## 쓰는 법\n *\n * ```ts\n * @Column({ nullable: true, ...moneyColumn() })\n * @Field(type => Float, { nullable: true })\n * supplyAmount?: number\n * ```\n *\n * `nullable`·`default` 같은 나머지 옵션은 호출하는 쪽이 정한다. GraphQL 필드는 `Float` 로 둔다.\n */\nexport function moneyColumn(options: ColumnOptions = {}): ColumnOptions {\n return { type: 'decimal', precision: 18, scale: 2, transformer: decimalTransformer, ...options }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/shell",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.123",
|
|
4
4
|
"description": "Core module for framework",
|
|
5
5
|
"bin": {
|
|
6
6
|
"things-factory": "bin/things-factory",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@things-factory/ejs-remote": "^10.1.20",
|
|
57
57
|
"@things-factory/env": "^10.1.20",
|
|
58
58
|
"@things-factory/operato-license-checker": "^4.0.4",
|
|
59
|
-
"@things-factory/styles": "^10.1.
|
|
59
|
+
"@things-factory/styles": "^10.1.123",
|
|
60
60
|
"@things-factory/utils": "^10.1.20",
|
|
61
61
|
"@types/koa": "^3.0.1",
|
|
62
62
|
"@types/koa-bodyparser": "^4.3.13",
|
|
@@ -126,5 +126,5 @@
|
|
|
126
126
|
"pg": "^8.7.3",
|
|
127
127
|
"sqlite3": "^5.0.8"
|
|
128
128
|
},
|
|
129
|
-
"gitHead": "
|
|
129
|
+
"gitHead": "9781f1eb37a7e6a37285878cff16f8683747eb7a"
|
|
130
130
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { decimalTransformer, moneyColumn } from '../server/typeorm/money-column'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Money is a `decimal(18, 2)` column read back as a number.
|
|
5
|
+
*
|
|
6
|
+
* Postgres and MySQL hand `decimal` back as a string. Left alone, a sum over rows becomes string
|
|
7
|
+
* concatenation ('1200.00' + 300 = '1200.00300') and the GraphQL Float field receives text.
|
|
8
|
+
*/
|
|
9
|
+
describe('a money column', () => {
|
|
10
|
+
it('is decimal(18, 2) with the number transformer', () => {
|
|
11
|
+
expect(moneyColumn()).toEqual({ type: 'decimal', precision: 18, scale: 2, transformer: decimalTransformer })
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('keeps the options the caller gives', () => {
|
|
15
|
+
expect(moneyColumn({ nullable: true, default: 0 })).toMatchObject({ type: 'decimal', nullable: true, default: 0 })
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('reads a driver string as a number', () => {
|
|
19
|
+
expect(decimalTransformer.from('1200.00')).toBe(1200)
|
|
20
|
+
expect(decimalTransformer.from('-71200000.50')).toBe(-71200000.5)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('leaves an empty value empty, never 0', () => {
|
|
24
|
+
expect(decimalTransformer.from(null)).toBeNull()
|
|
25
|
+
expect(decimalTransformer.from(undefined)).toBeUndefined()
|
|
26
|
+
expect(decimalTransformer.to(null)).toBeNull()
|
|
27
|
+
expect(decimalTransformer.to('')).toBeNull()
|
|
28
|
+
expect(decimalTransformer.to(undefined)).toBeUndefined()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('writes a number given as text as a number', () => {
|
|
32
|
+
expect(decimalTransformer.to('658630')).toBe(658630)
|
|
33
|
+
})
|
|
34
|
+
})
|