@indodev/toolkit 0.3.3 → 0.4.0
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/README.md +46 -170
- package/dist/currency/index.cjs +158 -25
- package/dist/currency/index.cjs.map +1 -1
- package/dist/currency/index.d.cts +50 -354
- package/dist/currency/index.d.ts +50 -354
- package/dist/currency/index.js +154 -26
- package/dist/currency/index.js.map +1 -1
- package/dist/datetime/index.cjs +442 -0
- package/dist/datetime/index.cjs.map +1 -0
- package/dist/datetime/index.d.cts +412 -0
- package/dist/datetime/index.d.ts +412 -0
- package/dist/datetime/index.js +422 -0
- package/dist/datetime/index.js.map +1 -0
- package/dist/index.cjs +503 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +485 -26
- package/dist/index.js.map +1 -1
- package/dist/utils-OG1yMaAa.d.cts +485 -0
- package/dist/utils-OG1yMaAa.d.ts +485 -0
- package/package.json +19 -2
package/dist/currency/index.d.ts
CHANGED
|
@@ -1,407 +1,103 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* @module currency/types
|
|
5
|
-
* @packageDocumentation
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Options for formatting Rupiah currency.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* Default formatting:
|
|
12
|
-
* ```typescript
|
|
13
|
-
* const options: RupiahOptions = {
|
|
14
|
-
* symbol: true,
|
|
15
|
-
* decimal: false,
|
|
16
|
-
* separator: '.',
|
|
17
|
-
* };
|
|
18
|
-
* formatRupiah(1500000, options); // 'Rp 1.500.000'
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* With decimals:
|
|
23
|
-
* ```typescript
|
|
24
|
-
* const options: RupiahOptions = {
|
|
25
|
-
* symbol: true,
|
|
26
|
-
* decimal: true,
|
|
27
|
-
* precision: 2,
|
|
28
|
-
* };
|
|
29
|
-
* formatRupiah(1500000.50, options); // 'Rp 1.500.000,50'
|
|
30
|
-
* ```
|
|
31
|
-
*
|
|
32
|
-
* @public
|
|
33
|
-
*/
|
|
34
|
-
interface RupiahOptions {
|
|
35
|
-
/**
|
|
36
|
-
* Whether to show 'Rp' symbol.
|
|
37
|
-
*
|
|
38
|
-
* @defaultValue true
|
|
39
|
-
*/
|
|
40
|
-
symbol?: boolean;
|
|
41
|
-
/**
|
|
42
|
-
* Whether to show decimal places.
|
|
43
|
-
*
|
|
44
|
-
* @defaultValue false
|
|
45
|
-
*/
|
|
46
|
-
decimal?: boolean;
|
|
47
|
-
/**
|
|
48
|
-
* Thousands separator character.
|
|
49
|
-
*
|
|
50
|
-
* @defaultValue '.'
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* ```typescript
|
|
54
|
-
* '.' // Indonesian standard
|
|
55
|
-
* ',' // International standard
|
|
56
|
-
* ' ' // Space separator
|
|
57
|
-
* ```
|
|
58
|
-
*/
|
|
59
|
-
separator?: string;
|
|
60
|
-
/**
|
|
61
|
-
* Decimal separator character.
|
|
62
|
-
*
|
|
63
|
-
* @defaultValue ','
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* ```typescript
|
|
67
|
-
* ',' // Indonesian standard
|
|
68
|
-
* '.' // International standard
|
|
69
|
-
* ```
|
|
70
|
-
*/
|
|
71
|
-
decimalSeparator?: string;
|
|
72
|
-
/**
|
|
73
|
-
* Number of decimal places to show.
|
|
74
|
-
*
|
|
75
|
-
* @defaultValue 0
|
|
76
|
-
*/
|
|
77
|
-
precision?: number;
|
|
78
|
-
/**
|
|
79
|
-
* Whether to add space after 'Rp' symbol.
|
|
80
|
-
*
|
|
81
|
-
* @defaultValue true
|
|
82
|
-
*
|
|
83
|
-
* @example
|
|
84
|
-
* ```typescript
|
|
85
|
-
* true // 'Rp 1.500.000'
|
|
86
|
-
* false // 'Rp1.500.000'
|
|
87
|
-
* ```
|
|
88
|
-
*/
|
|
89
|
-
spaceAfterSymbol?: boolean;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Options for converting numbers to Indonesian words (terbilang).
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* Default:
|
|
96
|
-
* ```typescript
|
|
97
|
-
* toWords(1500000); // 'satu juta lima ratus ribu rupiah'
|
|
98
|
-
* ```
|
|
99
|
-
*
|
|
100
|
-
* @example
|
|
101
|
-
* Uppercase:
|
|
102
|
-
* ```typescript
|
|
103
|
-
* toWords(1500000, { uppercase: true });
|
|
104
|
-
* // 'Satu juta lima ratus ribu rupiah'
|
|
105
|
-
* ```
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* Without currency suffix:
|
|
109
|
-
* ```typescript
|
|
110
|
-
* toWords(1500000, { withCurrency: false });
|
|
111
|
-
* // 'satu juta lima ratus ribu'
|
|
112
|
-
* ```
|
|
113
|
-
*
|
|
114
|
-
* @public
|
|
115
|
-
*/
|
|
116
|
-
interface WordOptions {
|
|
117
|
-
/**
|
|
118
|
-
* Whether to capitalize the first letter.
|
|
119
|
-
*
|
|
120
|
-
* @defaultValue false
|
|
121
|
-
*
|
|
122
|
-
* @example
|
|
123
|
-
* ```typescript
|
|
124
|
-
* false // 'satu juta'
|
|
125
|
-
* true // 'Satu juta'
|
|
126
|
-
* ```
|
|
127
|
-
*/
|
|
128
|
-
uppercase?: boolean;
|
|
129
|
-
/**
|
|
130
|
-
* Whether to add 'rupiah' at the end.
|
|
131
|
-
*
|
|
132
|
-
* @defaultValue true
|
|
133
|
-
*
|
|
134
|
-
* @example
|
|
135
|
-
* ```typescript
|
|
136
|
-
* true // 'satu juta rupiah'
|
|
137
|
-
* false // 'satu juta'
|
|
138
|
-
* ```
|
|
139
|
-
*/
|
|
140
|
-
withCurrency?: boolean;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Unit for rounding currency amounts.
|
|
144
|
-
*
|
|
145
|
-
* Common Indonesian currency rounding units:
|
|
146
|
-
* - `'ribu'`: Round to thousands (1.000)
|
|
147
|
-
* - `'ratus-ribu'`: Round to hundred thousands (100.000)
|
|
148
|
-
* - `'juta'`: Round to millions (1.000.000)
|
|
149
|
-
*
|
|
150
|
-
* @example
|
|
151
|
-
* ```typescript
|
|
152
|
-
* roundToClean(1234567, 'ribu'); // 1235000
|
|
153
|
-
* roundToClean(1234567, 'ratus-ribu'); // 1200000
|
|
154
|
-
* roundToClean(1234567, 'juta'); // 1000000
|
|
155
|
-
* ```
|
|
156
|
-
*
|
|
157
|
-
* @public
|
|
158
|
-
*/
|
|
159
|
-
type RoundUnit = 'ribu' | 'ratus-ribu' | 'juta';
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Currency formatting utilities for Indonesian Rupiah.
|
|
163
|
-
*
|
|
164
|
-
* @module currency/format
|
|
165
|
-
* @packageDocumentation
|
|
166
|
-
*/
|
|
1
|
+
import { S as SplitOptions } from '../utils-OG1yMaAa.js';
|
|
2
|
+
export { C as CompactOptions, e as RoundUnit, R as RupiahOptions, W as WordOptions, d as addRupiahSymbol, c as calculateTax, b as formatAccounting, a as formatCompact, f as formatRupiah, p as parseRupiah, r as roundToClean, t as toWords } from '../utils-OG1yMaAa.js';
|
|
167
3
|
|
|
168
4
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* Provides flexible formatting options including symbol display,
|
|
172
|
-
* decimal places, and custom separators.
|
|
173
|
-
*
|
|
174
|
-
* @param amount - The amount to format
|
|
175
|
-
* @param options - Formatting options
|
|
176
|
-
* @returns Formatted Rupiah string
|
|
177
|
-
*
|
|
178
|
-
* @example
|
|
179
|
-
* Basic formatting:
|
|
180
|
-
* ```typescript
|
|
181
|
-
* formatRupiah(1500000); // 'Rp 1.500.000'
|
|
182
|
-
* ```
|
|
183
|
-
*
|
|
184
|
-
* @example
|
|
185
|
-
* With decimals:
|
|
186
|
-
* ```typescript
|
|
187
|
-
* formatRupiah(1500000.50, { decimal: true }); // 'Rp 1.500.000,50'
|
|
188
|
-
* ```
|
|
189
|
-
*
|
|
190
|
-
* @example
|
|
191
|
-
* Without symbol:
|
|
192
|
-
* ```typescript
|
|
193
|
-
* formatRupiah(1500000, { symbol: false }); // '1.500.000'
|
|
194
|
-
* ```
|
|
195
|
-
*
|
|
196
|
-
* @example
|
|
197
|
-
* Custom separators:
|
|
198
|
-
* ```typescript
|
|
199
|
-
* formatRupiah(1500000, { separator: ',' }); // 'Rp 1,500,000'
|
|
200
|
-
* ```
|
|
5
|
+
* Invalid split error thrown when split parameters are invalid.
|
|
201
6
|
*
|
|
202
7
|
* @public
|
|
203
8
|
*/
|
|
204
|
-
declare
|
|
9
|
+
declare class InvalidSplitError extends Error {
|
|
10
|
+
constructor(message: string);
|
|
11
|
+
}
|
|
205
12
|
/**
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
* Uses Indonesian units: ribu, juta, miliar, triliun.
|
|
209
|
-
* Follows Indonesian grammar rules (e.g., "1 juta" not "1,0 juta").
|
|
13
|
+
* Splits an amount into equal or custom-ratio parts.
|
|
210
14
|
*
|
|
211
|
-
* @param amount - The amount to
|
|
212
|
-
* @
|
|
15
|
+
* @param amount - The amount to split
|
|
16
|
+
* @param parts - Number of parts to split into
|
|
17
|
+
* @param options - Split options (ratios, rounding)
|
|
18
|
+
* @returns Array of split amounts
|
|
213
19
|
*
|
|
214
20
|
* @example
|
|
215
|
-
*
|
|
21
|
+
* Equal split:
|
|
216
22
|
* ```typescript
|
|
217
|
-
*
|
|
218
|
-
* formatCompact(1000000); // 'Rp 1 juta'
|
|
23
|
+
* splitAmount(1500000, 3); // [500000, 500000, 500000]
|
|
219
24
|
* ```
|
|
220
25
|
*
|
|
221
26
|
* @example
|
|
222
|
-
*
|
|
27
|
+
* Custom ratios:
|
|
223
28
|
* ```typescript
|
|
224
|
-
*
|
|
29
|
+
* splitAmount(1000000, 2, { ratios: [70, 30] }); // [700000, 300000]
|
|
225
30
|
* ```
|
|
226
31
|
*
|
|
227
32
|
* @example
|
|
228
|
-
*
|
|
33
|
+
* With rounding:
|
|
229
34
|
* ```typescript
|
|
230
|
-
*
|
|
35
|
+
* splitAmount(1234567, 3, { roundTo: 'ribu' }); // [412000, 411000, 411000]
|
|
231
36
|
* ```
|
|
232
37
|
*
|
|
233
38
|
* @public
|
|
234
39
|
*/
|
|
235
|
-
declare function
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Currency parsing utilities for Indonesian Rupiah.
|
|
239
|
-
*
|
|
240
|
-
* @module currency/parse
|
|
241
|
-
* @packageDocumentation
|
|
242
|
-
*/
|
|
40
|
+
declare function splitAmount(amount: number, parts: number, options?: SplitOptions): number[];
|
|
243
41
|
/**
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
* Handles multiple formats:
|
|
247
|
-
* - Standard: "Rp 1.500.000"
|
|
248
|
-
* - No symbol: "1.500.000"
|
|
249
|
-
* - With decimals: "Rp 1.500.000,50"
|
|
250
|
-
* - Compact: "Rp 1,5 juta", "Rp 500 ribu"
|
|
251
|
-
*
|
|
252
|
-
* @param formatted - The formatted Rupiah string to parse
|
|
253
|
-
* @returns Parsed number, or null if invalid
|
|
254
|
-
*
|
|
255
|
-
* @example
|
|
256
|
-
* Standard format:
|
|
257
|
-
* ```typescript
|
|
258
|
-
* parseRupiah('Rp 1.500.000'); // 1500000
|
|
259
|
-
* ```
|
|
260
|
-
*
|
|
261
|
-
* @example
|
|
262
|
-
* With decimals:
|
|
263
|
-
* ```typescript
|
|
264
|
-
* parseRupiah('Rp 1.500.000,50'); // 1500000.50
|
|
265
|
-
* ```
|
|
42
|
+
* Calculates what percentage a part is of a total.
|
|
266
43
|
*
|
|
267
|
-
* @
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
* parseRupiah('Rp 1,5 juta'); // 1500000
|
|
271
|
-
* parseRupiah('Rp 500 ribu'); // 500000
|
|
272
|
-
* ```
|
|
44
|
+
* @param part - The part value
|
|
45
|
+
* @param total - The total value
|
|
46
|
+
* @returns Percentage as number (e.g., 15 for 15%)
|
|
273
47
|
*
|
|
274
48
|
* @example
|
|
275
|
-
* Invalid input:
|
|
276
49
|
* ```typescript
|
|
277
|
-
*
|
|
50
|
+
* percentageOf(150000, 1000000); // 15
|
|
51
|
+
* percentageOf(0, 1000000); // 0
|
|
52
|
+
* percentageOf(100, 0); // 0 (not NaN)
|
|
278
53
|
* ```
|
|
279
54
|
*
|
|
280
55
|
* @public
|
|
281
56
|
*/
|
|
282
|
-
declare function
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Convert numbers to Indonesian words (terbilang).
|
|
286
|
-
*
|
|
287
|
-
* @module currency/words
|
|
288
|
-
* @packageDocumentation
|
|
289
|
-
*/
|
|
290
|
-
|
|
57
|
+
declare function percentageOf(part: number, total: number): number;
|
|
291
58
|
/**
|
|
292
|
-
*
|
|
59
|
+
* Calculates absolute and percentage difference between two amounts.
|
|
293
60
|
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* Special rules:
|
|
298
|
-
* - 1 = "satu" in most cases, but "se-" for 100, 1000
|
|
299
|
-
* - 11 = "sebelas" (not "satu belas")
|
|
300
|
-
* - 100 = "seratus" (not "satu ratus")
|
|
301
|
-
* - 1000 = "seribu" (not "satu ribu")
|
|
302
|
-
*
|
|
303
|
-
* @param amount - The number to convert
|
|
304
|
-
* @param options - Conversion options
|
|
305
|
-
* @returns Indonesian words representation
|
|
61
|
+
* @param amount1 - The new/current amount
|
|
62
|
+
* @param amount2 - The original/reference amount
|
|
63
|
+
* @returns Object with absolute difference, percentage, and direction
|
|
306
64
|
*
|
|
307
65
|
* @example
|
|
308
|
-
* Basic numbers:
|
|
309
66
|
* ```typescript
|
|
310
|
-
*
|
|
311
|
-
*
|
|
67
|
+
* difference(1200000, 1000000);
|
|
68
|
+
* // { absolute: 200000, percentage: 20, direction: 'increase' }
|
|
312
69
|
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* ```typescript
|
|
316
|
-
* toWords(1500000); // 'satu juta lima ratus ribu rupiah'
|
|
317
|
-
* ```
|
|
318
|
-
*
|
|
319
|
-
* @example
|
|
320
|
-
* With options:
|
|
321
|
-
* ```typescript
|
|
322
|
-
* toWords(1500000, { uppercase: true });
|
|
323
|
-
* // 'Satu juta lima ratus ribu rupiah'
|
|
324
|
-
*
|
|
325
|
-
* toWords(1500000, { withCurrency: false });
|
|
326
|
-
* // 'satu juta lima ratus ribu'
|
|
70
|
+
* difference(0, 1000000);
|
|
71
|
+
* // { absolute: -1000000, percentage: null, direction: 'decrease' }
|
|
327
72
|
* ```
|
|
328
73
|
*
|
|
329
74
|
* @public
|
|
330
75
|
*/
|
|
331
|
-
declare function
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
* @module currency/utils
|
|
337
|
-
* @packageDocumentation
|
|
338
|
-
*/
|
|
76
|
+
declare function difference(amount1: number, amount2: number): {
|
|
77
|
+
absolute: number;
|
|
78
|
+
percentage: number | null;
|
|
79
|
+
direction: 'increase' | 'decrease' | 'same';
|
|
80
|
+
};
|
|
339
81
|
|
|
340
82
|
/**
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
* Common use case: displaying approximate prices or budgets
|
|
344
|
-
* in clean, rounded numbers.
|
|
345
|
-
*
|
|
346
|
-
* @param amount - The amount to round
|
|
347
|
-
* @param unit - The unit to round to (default: 'ribu')
|
|
348
|
-
* @returns Rounded amount
|
|
83
|
+
* Validates whether a string is a valid Rupiah format.
|
|
349
84
|
*
|
|
350
|
-
*
|
|
351
|
-
* Round to thousands:
|
|
352
|
-
* ```typescript
|
|
353
|
-
* roundToClean(1234567, 'ribu'); // 1235000
|
|
354
|
-
* ```
|
|
85
|
+
* Accepts standard, compact, and negative formats.
|
|
355
86
|
*
|
|
356
|
-
* @
|
|
357
|
-
*
|
|
358
|
-
* ```typescript
|
|
359
|
-
* roundToClean(1234567, 'ratus-ribu'); // 1200000
|
|
360
|
-
* ```
|
|
87
|
+
* @param formatted - The string to validate
|
|
88
|
+
* @returns `true` if valid Rupiah format, `false` otherwise
|
|
361
89
|
*
|
|
362
90
|
* @example
|
|
363
|
-
* Round to millions:
|
|
364
91
|
* ```typescript
|
|
365
|
-
*
|
|
92
|
+
* validateRupiah('Rp 1.500.000'); // true
|
|
93
|
+
* validateRupiah('1.500.000'); // true
|
|
94
|
+
* validateRupiah('Rp 1,5 juta'); // true
|
|
95
|
+
* validateRupiah('abc'); // false
|
|
96
|
+
* validateRupiah(''); // false
|
|
366
97
|
* ```
|
|
367
98
|
*
|
|
368
99
|
* @public
|
|
369
100
|
*/
|
|
370
|
-
declare function
|
|
371
|
-
/**
|
|
372
|
-
* Formats a number as Indonesian Rupiah in accounting style.
|
|
373
|
-
* Negative numbers are wrapped in parentheses.
|
|
374
|
-
*
|
|
375
|
-
* @param amount - The amount to format
|
|
376
|
-
* @param options - Formatting options
|
|
377
|
-
* @returns Formatted accounting string
|
|
378
|
-
*
|
|
379
|
-
* @example
|
|
380
|
-
* ```typescript
|
|
381
|
-
* formatAccounting(-1500000); // '(Rp 1.500.000)'
|
|
382
|
-
* ```
|
|
383
|
-
*/
|
|
384
|
-
declare function formatAccounting(amount: number, options?: RupiahOptions): string;
|
|
385
|
-
/**
|
|
386
|
-
* Calculates tax (PPN) for a given amount.
|
|
387
|
-
*
|
|
388
|
-
* @param amount - The base amount
|
|
389
|
-
* @param rate - The tax rate (default: 0.11 for 11%)
|
|
390
|
-
* @returns The calculated tax amount
|
|
391
|
-
*
|
|
392
|
-
* @example
|
|
393
|
-
* ```typescript
|
|
394
|
-
* calculateTax(1000000); // 110000
|
|
395
|
-
* ```
|
|
396
|
-
*/
|
|
397
|
-
declare function calculateTax(amount: number, rate?: number): number;
|
|
398
|
-
/**
|
|
399
|
-
* Helper to ensure a string or number has the 'Rp ' prefix.
|
|
400
|
-
* If already prefixed, it returns the input as is.
|
|
401
|
-
*
|
|
402
|
-
* @param amount - The amount or formatted string
|
|
403
|
-
* @returns String with Rupiah prefix
|
|
404
|
-
*/
|
|
405
|
-
declare function addRupiahSymbol(amount: string | number): string;
|
|
101
|
+
declare function validateRupiah(formatted: string): boolean;
|
|
406
102
|
|
|
407
|
-
export {
|
|
103
|
+
export { InvalidSplitError, SplitOptions, difference, percentageOf, splitAmount, validateRupiah };
|
package/dist/currency/index.js
CHANGED
|
@@ -8,7 +8,7 @@ function formatRupiah(amount, options) {
|
|
|
8
8
|
spaceAfterSymbol = true
|
|
9
9
|
} = options || {};
|
|
10
10
|
const precision = options?.precision !== void 0 ? options.precision : decimal ? 2 : 0;
|
|
11
|
-
const isNegative = amount < 0;
|
|
11
|
+
const isNegative = amount < 0 && amount !== 0;
|
|
12
12
|
const absAmount = Math.abs(amount);
|
|
13
13
|
let result;
|
|
14
14
|
if (decimal) {
|
|
@@ -26,17 +26,21 @@ function formatRupiah(amount, options) {
|
|
|
26
26
|
const intAmount = Math.floor(absAmount);
|
|
27
27
|
result = intAmount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, separator);
|
|
28
28
|
}
|
|
29
|
-
if (isNegative) {
|
|
30
|
-
result = `-${result}`;
|
|
31
|
-
}
|
|
32
29
|
if (symbol) {
|
|
33
30
|
const space = spaceAfterSymbol ? " " : "";
|
|
34
|
-
|
|
31
|
+
if (isNegative) {
|
|
32
|
+
result = `-Rp${space}${result}`;
|
|
33
|
+
} else {
|
|
34
|
+
result = `Rp${space}${result}`;
|
|
35
|
+
}
|
|
36
|
+
} else if (isNegative) {
|
|
37
|
+
result = `-${result}`;
|
|
35
38
|
}
|
|
36
39
|
return result;
|
|
37
40
|
}
|
|
38
|
-
function formatCompact(amount) {
|
|
39
|
-
const
|
|
41
|
+
function formatCompact(amount, options) {
|
|
42
|
+
const { symbol = true, spaceAfterSymbol = true } = options || {};
|
|
43
|
+
const isNegative = amount < 0 && amount !== 0;
|
|
40
44
|
const abs = Math.abs(amount);
|
|
41
45
|
let result;
|
|
42
46
|
if (abs >= 1e12) {
|
|
@@ -52,10 +56,17 @@ function formatCompact(amount) {
|
|
|
52
56
|
} else {
|
|
53
57
|
result = abs.toString();
|
|
54
58
|
}
|
|
55
|
-
if (
|
|
59
|
+
if (symbol) {
|
|
60
|
+
const space = spaceAfterSymbol ? " " : "";
|
|
61
|
+
if (isNegative) {
|
|
62
|
+
result = `-Rp${space}${result}`;
|
|
63
|
+
} else {
|
|
64
|
+
result = `Rp${space}${result}`;
|
|
65
|
+
}
|
|
66
|
+
} else if (isNegative) {
|
|
56
67
|
result = `-${result}`;
|
|
57
68
|
}
|
|
58
|
-
return
|
|
69
|
+
return result;
|
|
59
70
|
}
|
|
60
71
|
function formatCompactValue(value, unit) {
|
|
61
72
|
const rounded = Math.round(value * 10) / 10;
|
|
@@ -152,20 +163,42 @@ var TENS = [
|
|
|
152
163
|
"sembilan puluh"
|
|
153
164
|
];
|
|
154
165
|
function toWords(amount, options) {
|
|
155
|
-
const {
|
|
166
|
+
const {
|
|
167
|
+
uppercase = false,
|
|
168
|
+
withCurrency = true,
|
|
169
|
+
withDecimals = false
|
|
170
|
+
} = options || {};
|
|
156
171
|
if (amount === 0) {
|
|
157
172
|
let result = "nol";
|
|
158
173
|
if (withCurrency) result += " rupiah";
|
|
159
174
|
return uppercase ? capitalize(result) : result;
|
|
160
175
|
}
|
|
161
176
|
const isNegative = amount < 0;
|
|
162
|
-
const absAmount = Math.
|
|
177
|
+
const absAmount = Math.abs(amount);
|
|
178
|
+
const intPart = Math.floor(absAmount);
|
|
179
|
+
let words = convertInteger(intPart);
|
|
180
|
+
if (isNegative) {
|
|
181
|
+
words = "minus " + words;
|
|
182
|
+
}
|
|
183
|
+
if (withCurrency) {
|
|
184
|
+
words += " rupiah";
|
|
185
|
+
}
|
|
186
|
+
if (withDecimals) {
|
|
187
|
+
const decimalPart = Math.round((absAmount - intPart) * 100);
|
|
188
|
+
if (decimalPart > 0) {
|
|
189
|
+
words += " koma " + convertDecimal(decimalPart);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return uppercase ? capitalize(words) : words;
|
|
193
|
+
}
|
|
194
|
+
function convertInteger(num) {
|
|
195
|
+
if (num === 0) return "nol";
|
|
163
196
|
let words = "";
|
|
164
|
-
const triliun = Math.floor(
|
|
165
|
-
const miliar = Math.floor(
|
|
166
|
-
const juta = Math.floor(
|
|
167
|
-
const ribu = Math.floor(
|
|
168
|
-
const sisa =
|
|
197
|
+
const triliun = Math.floor(num / 1e12);
|
|
198
|
+
const miliar = Math.floor(num % 1e12 / 1e9);
|
|
199
|
+
const juta = Math.floor(num % 1e9 / 1e6);
|
|
200
|
+
const ribu = Math.floor(num % 1e6 / 1e3);
|
|
201
|
+
const sisa = num % 1e3;
|
|
169
202
|
if (triliun > 0) {
|
|
170
203
|
words += convertGroup(triliun) + " triliun";
|
|
171
204
|
}
|
|
@@ -185,13 +218,19 @@ function toWords(amount, options) {
|
|
|
185
218
|
if (words) words += " ";
|
|
186
219
|
words += convertGroup(sisa);
|
|
187
220
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (
|
|
192
|
-
|
|
221
|
+
return words;
|
|
222
|
+
}
|
|
223
|
+
function convertDecimal(num) {
|
|
224
|
+
if (num === 0) return "";
|
|
225
|
+
if (num < 10) return BASIC_NUMBERS[num];
|
|
226
|
+
if (num < 20) return TEENS[num - 10];
|
|
227
|
+
const tens = Math.floor(num / 10);
|
|
228
|
+
const ones = num % 10;
|
|
229
|
+
let result = TENS[tens];
|
|
230
|
+
if (ones > 0) {
|
|
231
|
+
result += " " + BASIC_NUMBERS[ones];
|
|
193
232
|
}
|
|
194
|
-
return
|
|
233
|
+
return result;
|
|
195
234
|
}
|
|
196
235
|
function convertGroup(num) {
|
|
197
236
|
if (num === 0) return "";
|
|
@@ -241,19 +280,108 @@ function formatAccounting(amount, options) {
|
|
|
241
280
|
}
|
|
242
281
|
return formatted;
|
|
243
282
|
}
|
|
244
|
-
function calculateTax(amount, rate
|
|
283
|
+
function calculateTax(amount, rate) {
|
|
245
284
|
return amount * rate;
|
|
246
285
|
}
|
|
247
286
|
function addRupiahSymbol(amount) {
|
|
248
287
|
if (typeof amount === "number") {
|
|
249
|
-
|
|
288
|
+
const formatted = amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
|
|
289
|
+
return `Rp ${formatted}`;
|
|
250
290
|
}
|
|
251
291
|
if (amount.trim().startsWith("Rp")) {
|
|
252
|
-
return amount;
|
|
292
|
+
return amount.trim();
|
|
253
293
|
}
|
|
254
294
|
return `Rp ${amount.trim()}`;
|
|
255
295
|
}
|
|
256
296
|
|
|
257
|
-
|
|
297
|
+
// src/currency/calc.ts
|
|
298
|
+
var InvalidSplitError = class extends Error {
|
|
299
|
+
constructor(message) {
|
|
300
|
+
super(message);
|
|
301
|
+
this.name = "InvalidSplitError";
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
function splitAmount(amount, parts, options) {
|
|
305
|
+
if (parts < 1) {
|
|
306
|
+
throw new InvalidSplitError("Parts must be at least 1");
|
|
307
|
+
}
|
|
308
|
+
if (parts === 1) {
|
|
309
|
+
return [amount];
|
|
310
|
+
}
|
|
311
|
+
const { ratios, roundTo } = options || {};
|
|
312
|
+
if (ratios) {
|
|
313
|
+
if (ratios.length !== parts) {
|
|
314
|
+
throw new InvalidSplitError(
|
|
315
|
+
`Ratios length (${ratios.length}) must match parts count (${parts})`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
const sum = ratios.reduce((a, b) => a + b, 0);
|
|
319
|
+
if (Math.abs(sum - 100) > 0.01) {
|
|
320
|
+
throw new InvalidSplitError(`Ratios must sum to 100 (got ${sum})`);
|
|
321
|
+
}
|
|
322
|
+
let result2 = ratios.map((r) => amount * (r / 100));
|
|
323
|
+
if (roundTo) {
|
|
324
|
+
result2 = result2.map((v) => roundToClean2(v, roundTo));
|
|
325
|
+
}
|
|
326
|
+
return result2;
|
|
327
|
+
}
|
|
328
|
+
const base = Math.floor(amount / parts);
|
|
329
|
+
const remainder = amount - base * parts;
|
|
330
|
+
const result = [];
|
|
331
|
+
for (let i = 0; i < parts; i++) {
|
|
332
|
+
result.push(base + (i < remainder ? 1 : 0));
|
|
333
|
+
}
|
|
334
|
+
if (roundTo) {
|
|
335
|
+
return result.map((v) => roundToClean2(v, roundTo));
|
|
336
|
+
}
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
function percentageOf(part, total) {
|
|
340
|
+
if (total === 0) return 0;
|
|
341
|
+
return part / total * 100;
|
|
342
|
+
}
|
|
343
|
+
function difference(amount1, amount2) {
|
|
344
|
+
const absolute = amount1 - amount2;
|
|
345
|
+
let percentage;
|
|
346
|
+
if (amount2 === 0) {
|
|
347
|
+
percentage = amount1 === 0 ? 0 : null;
|
|
348
|
+
} else {
|
|
349
|
+
percentage = absolute / amount2 * 100;
|
|
350
|
+
}
|
|
351
|
+
const direction = absolute > 0 ? "increase" : absolute < 0 ? "decrease" : "same";
|
|
352
|
+
return { absolute, percentage, direction };
|
|
353
|
+
}
|
|
354
|
+
function roundToClean2(amount, unit) {
|
|
355
|
+
const divisors = {
|
|
356
|
+
ribu: 1e3,
|
|
357
|
+
"ratus-ribu": 1e5,
|
|
358
|
+
juta: 1e6
|
|
359
|
+
};
|
|
360
|
+
return Math.round(amount / divisors[unit]) * divisors[unit];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/currency/validate.ts
|
|
364
|
+
function validateRupiah(formatted) {
|
|
365
|
+
if (!formatted || typeof formatted !== "string") {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
const trimmed = formatted.trim();
|
|
369
|
+
if (!trimmed) return false;
|
|
370
|
+
const compactUnits = ["triliun", "miliar", "juta", "ribu"];
|
|
371
|
+
for (const unit of compactUnits) {
|
|
372
|
+
if (trimmed.toLowerCase().includes(unit)) {
|
|
373
|
+
return /-?\d+[,.]?\d*\s*(ribu|juta|miliar|triliun)/i.test(trimmed);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
let cleaned = trimmed.replace(/^(-?\s*)?Rp\s*/i, "");
|
|
377
|
+
cleaned = cleaned.replace(/^\s*-/, "");
|
|
378
|
+
cleaned = cleaned.trim();
|
|
379
|
+
if (!cleaned) return false;
|
|
380
|
+
if (!/^[0-9.,]+$/.test(cleaned)) return false;
|
|
381
|
+
if (!/\d/.test(cleaned)) return false;
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export { InvalidSplitError, addRupiahSymbol, calculateTax, difference, formatAccounting, formatCompact, formatRupiah, parseRupiah, percentageOf, roundToClean, splitAmount, toWords, validateRupiah };
|
|
258
386
|
//# sourceMappingURL=index.js.map
|
|
259
387
|
//# sourceMappingURL=index.js.map
|