@ripwords/myinvois-client 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/package.json +5 -8
  2. package/.prettierrc +0 -8
  3. package/CHANGELOG.md +0 -152
  4. package/bun.lock +0 -460
  5. package/myinvois-cert.conf.template +0 -23
  6. package/scripts/gen-cert.sh +0 -159
  7. package/src/api/platform/platformLogin.ts +0 -34
  8. package/src/index.ts +0 -530
  9. package/src/types/classification-codes.d.ts +0 -115
  10. package/src/types/country-code.d.ts +0 -790
  11. package/src/types/currencies.d.ts +0 -383
  12. package/src/types/documents.d.ts +0 -869
  13. package/src/types/e-invoice.d.ts +0 -41
  14. package/src/types/index.d.ts +0 -24
  15. package/src/types/msic/0X.d.ts +0 -408
  16. package/src/types/msic/1X.d.ts +0 -210
  17. package/src/types/msic/2X.d.ts +0 -266
  18. package/src/types/msic/3X.d.ts +0 -114
  19. package/src/types/msic/4X.d.ts +0 -520
  20. package/src/types/msic/5X.d.ts +0 -144
  21. package/src/types/msic/6X.d.ts +0 -200
  22. package/src/types/msic/7X.d.ts +0 -132
  23. package/src/types/msic/8X.d.ts +0 -210
  24. package/src/types/msic/9X.d.ts +0 -186
  25. package/src/types/msic-codes.d.ts +0 -31
  26. package/src/types/payment-modes.d.ts +0 -41
  27. package/src/types/signatures.d.ts +0 -169
  28. package/src/types/state-codes.d.ts +0 -59
  29. package/src/types/tax-types.d.ts +0 -39
  30. package/src/types/unit/1X.d.ts +0 -16
  31. package/src/types/unit/2X.d.ts +0 -62
  32. package/src/types/unit/3X.d.ts +0 -17
  33. package/src/types/unit/4X.d.ts +0 -44
  34. package/src/types/unit/5X.d.ts +0 -26
  35. package/src/types/unit/6X.d.ts +0 -12
  36. package/src/types/unit/7X.d.ts +0 -12
  37. package/src/types/unit/8X.d.ts +0 -15
  38. package/src/types/unit/9X.d.ts +0 -11
  39. package/src/types/unit/AX.d.ts +0 -202
  40. package/src/types/unit/BX.d.ts +0 -212
  41. package/src/types/unit/CX.d.ts +0 -238
  42. package/src/types/unit/DX.d.ts +0 -212
  43. package/src/types/unit/EX.d.ts +0 -196
  44. package/src/types/unit/FX.d.ts +0 -236
  45. package/src/types/unit/GX.d.ts +0 -254
  46. package/src/types/unit/HX.d.ts +0 -234
  47. package/src/types/unit/IX.d.ts +0 -28
  48. package/src/types/unit/JX.d.ts +0 -190
  49. package/src/types/unit/KX.d.ts +0 -284
  50. package/src/types/unit/LX.d.ts +0 -228
  51. package/src/types/unit/MX.d.ts +0 -288
  52. package/src/types/unit/NX.d.ts +0 -226
  53. package/src/types/unit/OX.d.ts +0 -34
  54. package/src/types/unit/PX.d.ts +0 -224
  55. package/src/types/unit/QX.d.ts +0 -94
  56. package/src/types/unit/RX.d.ts +0 -28
  57. package/src/types/unit/SX.d.ts +0 -56
  58. package/src/types/unit/TX.d.ts +0 -44
  59. package/src/types/unit/UX.d.ts +0 -14
  60. package/src/types/unit/VX.d.ts +0 -13
  61. package/src/types/unit/WX.d.ts +0 -34
  62. package/src/types/unit/XX.d.ts +0 -825
  63. package/src/types/unit/YX.d.ts +0 -17
  64. package/src/types/unit/ZX.d.ts +0 -19
  65. package/src/types/unit-types.d.ts +0 -86
  66. package/src/utils/base64.ts +0 -7
  67. package/src/utils/certificate.ts +0 -60
  68. package/src/utils/document.ts +0 -852
  69. package/src/utils/getBaseUrl.ts +0 -5
  70. package/src/utils/helpers.ts +0 -552
  71. package/src/utils/signature-diagnostics.ts +0 -583
  72. package/src/utils/validation.ts +0 -268
  73. package/test/MyInvoiClientWithRealData.test.ts +0 -40
  74. package/test/MyInvoisClient.test.ts +0 -204
  75. package/test/base64.test.ts +0 -43
  76. package/test/dynamicInvoiceFeatures.test.ts +0 -451
  77. package/test/signAndSubmitInvoice.test.ts +0 -452
  78. package/test/signature-diagnostics.test.ts +0 -130
  79. package/tsconfig.json +0 -39
  80. package/tsdown.config.ts +0 -31
  81. package/vitest.config.ts +0 -8
@@ -1,451 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import type { InvoiceV1_1 } from '../src/types'
3
- import {
4
- PaymentHelpers,
5
- CurrencyHelpers,
6
- TaxHelpers,
7
- AllowanceChargeHelpers,
8
- DeliveryHelpers,
9
- DocumentReferenceHelpers,
10
- IndustryHelpers,
11
- DateTimeHelpers,
12
- InvoiceBuilder,
13
- } from '../src/utils/helpers'
14
- import { validateInvoice } from '../src/utils/validation'
15
- import { generateCleanUBLDocument } from '../src/utils/document'
16
-
17
- /**
18
- * ⚠️ SECURITY NOTICE: This file uses environment variables for sensitive data.
19
- * Never hardcode actual TIN, NRIC, certificates, or API credentials in test files.
20
- * Use .env file for your actual values (already gitignored).
21
- */
22
-
23
- describe('Dynamic Invoice Features', () => {
24
- describe('Helper Functions', () => {
25
- it('should create different payment methods', () => {
26
- const cash = PaymentHelpers.cash()
27
- expect(cash.paymentMeansCode).toBe('01')
28
- expect(cash.paymentTerms).toBe('Cash payment')
29
-
30
- const bankTransfer = PaymentHelpers.bankTransfer('ACC123', '2025-02-01')
31
- expect(bankTransfer.paymentMeansCode).toBe('03')
32
- expect(bankTransfer.payeeFinancialAccountID).toBe('ACC123')
33
- expect(bankTransfer.paymentDueDate).toBe('2025-02-01')
34
-
35
- const net30 = PaymentHelpers.net(30, 'ACC456')
36
- expect(net30.paymentMeansCode).toBe('03')
37
- expect(net30.paymentTerms).toBe('Net 30 days')
38
- expect(net30.payeeFinancialAccountID).toBe('ACC456')
39
- })
40
-
41
- it('should calculate taxes correctly', () => {
42
- const sstResult = TaxHelpers.calculateSST(1000, 6)
43
- expect(sstResult.taxAmount).toBe(60)
44
- expect(sstResult.totalWithTax).toBe(1060)
45
-
46
- const zeroRated = TaxHelpers.zeroRated(1000)
47
- expect(zeroRated.taxAmount).toBe(0)
48
- expect(zeroRated.totalWithTax).toBe(1000)
49
-
50
- const exempt = TaxHelpers.exempt(1000)
51
- expect(exempt.taxAmount).toBe(0)
52
- expect(exempt.totalWithTax).toBe(1000)
53
- })
54
-
55
- it('should create allowances and charges', () => {
56
- const discount = AllowanceChargeHelpers.discount('Early payment', 100, 10)
57
- expect(discount.chargeIndicator).toBe(false)
58
- expect(discount.reason).toBe('Early payment')
59
- expect(discount.amount).toBe(100)
60
- expect(discount.multiplierFactorNumeric).toBe(0.1)
61
-
62
- const shipping = AllowanceChargeHelpers.shipping(50)
63
- expect(shipping.chargeIndicator).toBe(true)
64
- expect(shipping.reason).toBe('Shipping and handling')
65
- expect(shipping.amount).toBe(50)
66
-
67
- const earlyDiscount = AllowanceChargeHelpers.earlyPaymentDiscount(2, 5000)
68
- expect(earlyDiscount.chargeIndicator).toBe(false)
69
- expect(earlyDiscount.amount).toBe(100) // 2% of 5000
70
- expect(earlyDiscount.baseAmount).toBe(5000)
71
- })
72
-
73
- it('should create delivery information', () => {
74
- const standardDelivery = DeliveryHelpers.standard('2025-01-15', {
75
- addressLine0: '123 Main St',
76
- cityName: 'Kuala Lumpur',
77
- postalZone: '50000',
78
- state: '14',
79
- })
80
-
81
- expect(standardDelivery.actualDeliveryDate).toBe('2025-01-15')
82
- expect(standardDelivery.deliveryLocation?.addressLine0).toBe(
83
- '123 Main St',
84
- )
85
- expect(standardDelivery.deliveryLocation?.country).toBe('MYS')
86
-
87
- const pickup = DeliveryHelpers.pickup('2025-01-16')
88
- expect(pickup.actualDeliveryDate).toBe('2025-01-16')
89
- expect(pickup.deliveryLocation?.addressLine0).toBe('Customer pickup')
90
-
91
- const withFreight = DeliveryHelpers.withFreight(
92
- '2025-01-17',
93
- {
94
- addressLine0: '456 Business Ave',
95
- cityName: 'Shah Alam',
96
- state: '10',
97
- },
98
- 75,
99
- 'Express delivery',
100
- )
101
-
102
- expect(withFreight.shipment?.freightAllowanceCharge?.amount).toBe(75)
103
- expect(withFreight.shipment?.freightAllowanceCharge?.reason).toBe(
104
- 'Express delivery',
105
- )
106
- })
107
-
108
- it('should create document references', () => {
109
- const po = DocumentReferenceHelpers.purchaseOrder('PO-2025-001')
110
- expect(po.purchaseOrderReference).toBe('PO-2025-001')
111
-
112
- const contract = DocumentReferenceHelpers.contract('CONTRACT-2025-001')
113
- expect(contract.contractReference).toBe('CONTRACT-2025-001')
114
-
115
- const customs = DocumentReferenceHelpers.customs('CUSTOMS-12345')
116
- expect(customs.id).toBe('CUSTOMS-12345')
117
- expect(customs.documentType).toBe('CustomsImportForm')
118
-
119
- const fta = DocumentReferenceHelpers.freeTradeAgreement(
120
- 'FTA-123',
121
- 'Singapore',
122
- )
123
- expect(fta.id).toBe('FTA-123')
124
- expect(fta.documentType).toBe('FreeTradeAgreement')
125
- expect(fta.documentDescription).toBe(
126
- 'Free trade agreement with Singapore',
127
- )
128
- })
129
-
130
- it('should provide industry classifications', () => {
131
- expect(IndustryHelpers.software.industryClassificationCode).toBe('62012')
132
- expect(IndustryHelpers.software.industryClassificationDescription).toBe(
133
- 'Business and domestic software development',
134
- )
135
-
136
- expect(IndustryHelpers.trading.industryClassificationCode).toBe('47900')
137
- expect(IndustryHelpers.manufacturing.industryClassificationCode).toBe(
138
- '10131',
139
- )
140
- expect(IndustryHelpers.consulting.industryClassificationCode).toBe(
141
- '70200',
142
- )
143
-
144
- const custom = IndustryHelpers.custom('99999', 'Custom business activity')
145
- expect(custom.industryClassificationCode).toBe('99999')
146
- expect(custom.industryClassificationDescription).toBe(
147
- 'Custom business activity',
148
- )
149
- })
150
-
151
- it('should handle currency exchange rates', () => {
152
- const domestic = CurrencyHelpers.domestic()
153
- expect(domestic.sourceCurrencyCode).toBe('MYR')
154
- expect(domestic.targetCurrencyCode).toBe('MYR')
155
- expect(domestic.calculationRate).toBe(1.0)
156
-
157
- const international = CurrencyHelpers.international(
158
- 'USD',
159
- 'MYR',
160
- 4.75,
161
- '2025-01-01',
162
- )
163
- expect(international.sourceCurrencyCode).toBe('USD')
164
- expect(international.targetCurrencyCode).toBe('MYR')
165
- expect(international.calculationRate).toBe(4.75)
166
- expect(international.exchangeRateDate).toBe('2025-01-01')
167
-
168
- // Test common rates
169
- expect(CurrencyHelpers.commonRates.USD_MYR).toBe(4.75)
170
- expect(CurrencyHelpers.commonRates.SGD_MYR).toBe(3.45)
171
- })
172
-
173
- it('should handle date and time formatting', () => {
174
- const currentDate = DateTimeHelpers.currentDate()
175
- expect(currentDate).toMatch(/^\d{4}-\d{2}-\d{2}$/)
176
-
177
- const currentTime = DateTimeHelpers.currentTime()
178
- expect(currentTime).toMatch(/^\d{2}:\d{2}:\d{2}Z$/)
179
-
180
- const testDate = new Date('2025-01-15T10:30:00Z')
181
- expect(DateTimeHelpers.formatDate(testDate)).toBe('2025-01-15')
182
- expect(DateTimeHelpers.formatTime(testDate)).toBe('10:30:00Z')
183
-
184
- const addedDays = DateTimeHelpers.addDays(testDate, 5)
185
- expect(DateTimeHelpers.formatDate(addedDays)).toBe('2025-01-20')
186
-
187
- const monthlyPeriod = DateTimeHelpers.monthlyPeriod(2025, 1)
188
- expect(monthlyPeriod.start).toBe('2025-01-01')
189
- expect(monthlyPeriod.end).toBe('2025-01-31')
190
- })
191
-
192
- it('should create invoice templates', () => {
193
- const cashInvoice = InvoiceBuilder.basicCash('INV-CASH-001')
194
- expect(cashInvoice.eInvoiceCodeOrNumber).toBe('INV-CASH-001')
195
- expect(cashInvoice.eInvoiceTypeCode).toBe('01')
196
- expect(cashInvoice.invoiceCurrencyCode).toBe('MYR')
197
- expect(cashInvoice.paymentMeans?.[0].paymentMeansCode).toBe('01')
198
-
199
- const businessInvoice = InvoiceBuilder.businessInvoice(
200
- 'INV-BIZ-001',
201
- 'ACC123',
202
- 30,
203
- )
204
- expect(businessInvoice.eInvoiceCodeOrNumber).toBe('INV-BIZ-001')
205
- expect(businessInvoice.paymentMeans?.[0].paymentMeansCode).toBe('03')
206
- expect(businessInvoice.paymentMeans?.[0].paymentTerms).toBe('Net 30 days')
207
-
208
- const internationalInvoice = InvoiceBuilder.international(
209
- 'INV-USD-001',
210
- 'USD',
211
- 4.75,
212
- )
213
- expect(internationalInvoice.eInvoiceCodeOrNumber).toBe('INV-USD-001')
214
- expect(internationalInvoice.invoiceCurrencyCode).toBe('USD')
215
- expect(internationalInvoice.currencyExchangeRate).toBe(4.75)
216
- expect(internationalInvoice.taxExchangeRate?.calculationRate).toBe(4.75)
217
- })
218
- })
219
-
220
- describe('Validation Functions', () => {
221
- const createTestInvoice = (): InvoiceV1_1 => ({
222
- eInvoiceVersion: '1.1',
223
- eInvoiceTypeCode: '01',
224
- eInvoiceCodeOrNumber: 'TEST-001',
225
- eInvoiceDate: '2025-01-01',
226
- eInvoiceTime: '10:00:00Z',
227
- invoiceCurrencyCode: 'MYR',
228
-
229
- supplier: {
230
- name: 'Test Supplier',
231
- tin: 'C1234567890',
232
- registrationType: 'BRN',
233
- registrationNumber: '202201234567',
234
- contactNumber: '+60123456789',
235
- address: {
236
- addressLine0: 'Test Address',
237
- cityName: 'Kuala Lumpur',
238
- state: '14',
239
- country: 'MYS',
240
- },
241
- industryClassificationCode: '41001',
242
- industryClassificationDescription: 'Test Industry',
243
- },
244
-
245
- buyer: {
246
- name: 'Test Buyer',
247
- tin: process.env.BUYER_TIN_VALUE || 'IG00000000000', // Replace with your buyer TIN
248
- registrationType: 'NRIC',
249
- registrationNumber: process.env.BUYER_NRIC_VALUE || '000000000000', // Replace with test NRIC
250
- sstRegistrationNumber: 'NA',
251
- contactNumber: '+60987654321',
252
- address: {
253
- addressLine0: 'Buyer Address',
254
- cityName: 'Petaling Jaya',
255
- state: '10',
256
- country: 'MYS',
257
- },
258
- },
259
-
260
- invoiceLineItems: [
261
- {
262
- itemClassificationCode: '001',
263
- itemDescription: 'Test Product',
264
- unitPrice: 100.0,
265
- taxType: '01',
266
- taxRate: 6.0,
267
- taxAmount: 6.0,
268
- totalTaxableAmountPerLine: 100.0,
269
- totalAmountPerLine: 106.0,
270
- },
271
- ],
272
-
273
- legalMonetaryTotal: {
274
- taxExclusiveAmount: 100.0,
275
- taxInclusiveAmount: 106.0,
276
- payableAmount: 106.0,
277
- },
278
-
279
- taxTotal: {
280
- taxAmount: 6.0,
281
- },
282
-
283
- issuerDigitalSignature: {} as any,
284
- })
285
-
286
- it('should validate a correct invoice', () => {
287
- const invoice = createTestInvoice()
288
- const validation = validateInvoice(invoice)
289
-
290
- expect(validation.isValid).toBe(true)
291
- expect(validation.errors).toHaveLength(0)
292
- expect(validation.warnings).toHaveLength(0)
293
- })
294
-
295
- it('should detect TIN format issues', () => {
296
- const invoice = createTestInvoice()
297
- invoice.supplier.tin = 'IG1234567890' // Individual TIN for company
298
-
299
- const validation = validateInvoice(invoice)
300
-
301
- expect(
302
- validation.warnings.some(w => w.code === 'TIN_FORMAT_INVALID'),
303
- ).toBe(true)
304
- expect(
305
- validation.warnings.some(w =>
306
- w.message.includes('Company TIN should start with "C"'),
307
- ),
308
- ).toBe(true)
309
- })
310
-
311
- it('should detect invalid contact numbers', () => {
312
- const invoice = createTestInvoice()
313
- invoice.supplier.contactNumber = 'invalid-phone'
314
-
315
- const validation = validateInvoice(invoice)
316
-
317
- expect(validation.isValid).toBe(false)
318
- expect(
319
- validation.errors.some(e => e.code === 'CONTACT_FORMAT_INVALID'),
320
- ).toBe(true)
321
- })
322
-
323
- it('should detect tax calculation mismatches', () => {
324
- const invoice = createTestInvoice()
325
- invoice.taxTotal.taxAmount = 10.0 // Wrong tax amount
326
-
327
- const validation = validateInvoice(invoice)
328
-
329
- expect(validation.isValid).toBe(false)
330
- expect(
331
- validation.errors.some(e => e.code === 'TAX_AMOUNT_MISMATCH'),
332
- ).toBe(true)
333
- })
334
-
335
- it('should detect negative amounts', () => {
336
- const invoice = createTestInvoice()
337
- invoice.legalMonetaryTotal.payableAmount = -100
338
-
339
- const validation = validateInvoice(invoice)
340
-
341
- expect(validation.isValid).toBe(false)
342
- expect(validation.errors.some(e => e.code === 'AMOUNT_NEGATIVE')).toBe(
343
- true,
344
- )
345
- })
346
- })
347
-
348
- describe('Enhanced Document Generation', () => {
349
- it('should generate document with dynamic industry classification', () => {
350
- const invoice = createBasicInvoiceWithIndustry()
351
-
352
- // Test clean document generation without signing (avoids crypto errors with dummy keys)
353
- const cleanDocument = generateCleanUBLDocument([invoice])
354
-
355
- expect(cleanDocument.Invoice).toHaveLength(1)
356
- expect(cleanDocument.Invoice[0].AccountingSupplierParty).toBeDefined()
357
- })
358
-
359
- it('should generate document with dynamic delivery information', () => {
360
- const invoice = createInvoiceWithDelivery()
361
-
362
- // Test clean document generation without signing (avoids crypto errors with dummy keys)
363
- const cleanDocument = generateCleanUBLDocument([invoice])
364
-
365
- expect(
366
- cleanDocument.Invoice[0].AccountingSupplierParty[0].Party[0]
367
- .PostalAddress[0].CityName[0]._,
368
- ).toBe('Shah Alam')
369
- })
370
- })
371
- })
372
-
373
- // Helper functions for creating test invoices with specific features
374
-
375
- function createBasicInvoiceWithIndustry(): InvoiceV1_1 {
376
- const baseInvoice = InvoiceBuilder.basicCash('TEST-INDUSTRY-001')
377
-
378
- return {
379
- ...baseInvoice,
380
- supplier: {
381
- name: 'Software Co Sdn Bhd',
382
- tin: 'C1234567890',
383
- registrationType: 'BRN',
384
- registrationNumber: '202201234567',
385
- contactNumber: '+60123456789',
386
- ...IndustryHelpers.software, // Dynamic industry classification
387
- address: {
388
- addressLine0: 'Tech Hub',
389
- cityName: 'Kuala Lumpur',
390
- postalZone: '50000',
391
- state: '14',
392
- country: 'MYS',
393
- },
394
- },
395
- buyer: {
396
- name: 'Test Buyer',
397
- tin: 'EI00000000010',
398
- registrationType: 'NRIC',
399
- registrationNumber: process.env.BUYER_NRIC_VALUE || '000000000000', // Replace with test NRIC
400
- sstRegistrationNumber: 'NA',
401
- contactNumber: '+60123456789',
402
- address: {
403
- addressLine0: 'Buyer Address',
404
- cityName: 'KL',
405
- postalZone: '50000',
406
- state: '14',
407
- country: 'MYS',
408
- },
409
- },
410
- invoiceLineItems: [
411
- {
412
- itemClassificationCode: '001',
413
- itemDescription: 'Software Services',
414
- unitPrice: 1000.0,
415
- quantity: 1,
416
- measurement: 'C62',
417
- taxType: '01',
418
- taxRate: 6.0,
419
- taxAmount: 60.0,
420
- totalTaxableAmountPerLine: 1000.0,
421
- totalAmountPerLine: 1060.0,
422
- },
423
- ],
424
- legalMonetaryTotal: {
425
- taxExclusiveAmount: 1000.0,
426
- taxInclusiveAmount: 1060.0,
427
- payableAmount: 1060.0,
428
- },
429
- taxTotal: {
430
- taxAmount: 60.0,
431
- taxSubtotals: [
432
- {
433
- taxableAmount: 1000.0,
434
- taxAmount: 60.0,
435
- taxCategory: {
436
- taxTypeCode: '01',
437
- taxRate: 6.0,
438
- },
439
- },
440
- ],
441
- },
442
- issuerDigitalSignature: {} as any,
443
- } as InvoiceV1_1
444
- }
445
-
446
- function createInvoiceWithDelivery(): InvoiceV1_1 {
447
- const invoice = createBasicInvoiceWithIndustry()
448
- // Update supplier address to Shah Alam as expected by the test
449
- invoice.supplier.address.cityName = 'Shah Alam'
450
- return invoice
451
- }