@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
package/src/index.ts DELETED
@@ -1,530 +0,0 @@
1
- import { EInvoiceTypeCode } from './types'
2
- import { platformLogin } from './api/platform/platformLogin'
3
- import type {
4
- DocumentStatus,
5
- InvoiceV1_1,
6
- RegistrationType,
7
- SigningCredentials,
8
- } from './types/documents'
9
- import { getBaseUrl } from './utils/getBaseUrl'
10
- import { extractCertificateInfo, validateKeyPair } from './utils/certificate'
11
-
12
- import type {
13
- DocumentSummary,
14
- DocumentValidationResult,
15
- DocumentValidationStepResult,
16
- GetSubmissionResponse,
17
- SubmissionResponse,
18
- SubmissionStatus,
19
- } from './types/documents'
20
- import { generateCompleteDocument } from './utils/document'
21
-
22
- export class MyInvoisClient {
23
- private readonly baseUrl: string
24
- private readonly clientId: string
25
- private readonly clientSecret: string
26
- private readonly onBehalfOf?: string
27
- private readonly signingCredentials: SigningCredentials
28
- private readonly debug: boolean
29
- private token = ''
30
- private tokenExpiration: Date | undefined = undefined
31
-
32
- constructor(
33
- clientId: string,
34
- clientSecret: string,
35
- environment: 'sandbox' | 'production',
36
- certificatePem: string,
37
- privateKeyPem: string,
38
- onBehalfOf?: string,
39
- debug?: boolean,
40
- ) {
41
- // Check if basic signing credentials are available
42
- if (!privateKeyPem || !certificatePem) {
43
- throw new Error(
44
- 'Missing required environment variables: PRIVATE_KEY and CERTIFICATE',
45
- )
46
- }
47
-
48
- // Validate that the key pair matches
49
- if (!validateKeyPair(certificatePem, privateKeyPem)) {
50
- throw new Error('Certificate and private key do not match')
51
- }
52
-
53
- this.clientId = clientId
54
- this.clientSecret = clientSecret
55
- this.baseUrl = getBaseUrl(environment)
56
- this.onBehalfOf = onBehalfOf
57
- this.debug = (debug ?? process.env.MYINVOIS_DEBUG === 'true') ? true : false
58
-
59
- // Extract certificate information
60
- const { issuerName, serialNumber } = extractCertificateInfo(certificatePem)
61
-
62
- this.signingCredentials = {
63
- privateKeyPem,
64
- certificatePem,
65
- issuerName,
66
- serialNumber,
67
- }
68
- }
69
-
70
- private async refreshToken() {
71
- const tokenResponse = await platformLogin({
72
- clientId: this.clientId,
73
- clientSecret: this.clientSecret,
74
- baseUrl: this.baseUrl,
75
- onBehalfOf: this.onBehalfOf,
76
- debug: this.debug,
77
- })
78
-
79
- this.token = tokenResponse.token
80
- this.tokenExpiration = tokenResponse.tokenExpiration
81
- }
82
-
83
- private async getToken() {
84
- if (
85
- !this.tokenExpiration ||
86
- this.tokenExpiration < new Date() ||
87
- isNaN(this.tokenExpiration.getTime())
88
- ) {
89
- if (this.debug) {
90
- console.log('Token expired')
91
- console.log('Refreshing token')
92
- }
93
- await this.refreshToken()
94
- }
95
-
96
- return this.token
97
- }
98
-
99
- private async fetch(path: string, options: Parameters<typeof fetch>[1] = {}) {
100
- const token = await this.getToken()
101
-
102
- return fetch(`${this.baseUrl}${path}`, {
103
- ...options,
104
- headers: { ...options.headers, Authorization: `Bearer ${token}` },
105
- })
106
- }
107
-
108
- /**
109
- * Validates a TIN against a NRIC/ARMY/PASSPORT/BRN (Business Registration Number)
110
- *
111
- * @param tin - The TIN to validate
112
- * @param idType - The type of ID to validate against
113
- * @param idValue - The value of the ID to validate against
114
- * @returns true if the TIN is valid, false otherwise
115
- */
116
- async verifyTin(
117
- tin: string,
118
- idType: RegistrationType,
119
- idValue: string,
120
- ): Promise<boolean> {
121
- try {
122
- const response = await this.fetch(
123
- `/api/v1.0/taxpayer/validate/${tin}?idType=${idType}&idValue=${idValue}`,
124
- {
125
- method: 'GET',
126
- },
127
- )
128
-
129
- if (response.status === 200) {
130
- return true
131
- }
132
-
133
- return false
134
- } catch (error) {
135
- if (this.debug) {
136
- console.error(error)
137
- }
138
- return false
139
- }
140
- }
141
-
142
- /**
143
- * Submits one or more e-invoice documents to the MyInvois platform for processing.
144
- *
145
- * This method digitally signs each document using the provided certificate and private key,
146
- * generates document hashes, encodes them for submission, and sends them to the platform.
147
- * The method includes comprehensive validation warnings for document size and count limits.
148
- *
149
- * @param documents - Array of InvoiceV1_1 documents to be submitted
150
- * @returns Promise resolving to submission response containing the submission data and HTTP status
151
- * @throws {Error} If PRIVATE_KEY or CERTIFICATE environment variables are missing
152
- * @throws {Error} If document signing, encoding, or API submission fails
153
- *
154
- * @example
155
- * ```typescript
156
- * // Submit a single invoice
157
- * const result = await client.submitDocument([invoiceData]);
158
- * console.log(result.data.submissionUid); // Track submission with this UID
159
- *
160
- * // Submit multiple invoices
161
- * const result = await client.submitDocument([invoice1, invoice2, invoice3]);
162
- * if (result.status === 202) {
163
- * console.log('Documents submitted successfully');
164
- * }
165
- * ```
166
- *
167
- * @remarks
168
- * - Requires PRIVATE_KEY and CERTIFICATE environment variables for document signing
169
- * - Each document is digitally signed with XML-DSIG before submission
170
- * - Documents are base64-encoded for transmission
171
- * - API limits: Max 100 documents per submission, 5MB total payload, 300KB per document
172
- * - Debug mode provides detailed logging of payload sizes and validation warnings
173
- * - Returns HTTP 202 for successful submissions that require processing
174
- */
175
- async submitDocument(documents: InvoiceV1_1[]): Promise<{
176
- data: SubmissionResponse
177
- status: number
178
- }> {
179
- if (this.debug) {
180
- console.log(`📦 Preparing to submit ${documents.length} document(s)...`)
181
- }
182
-
183
- // Generate the complete signed document structure first
184
- const completeDocument = generateCompleteDocument(
185
- documents,
186
- this.signingCredentials,
187
- )
188
-
189
- if (this.debug) {
190
- console.log('✅ Documents signed successfully')
191
- console.log('📄 Document structure keys:', Object.keys(completeDocument))
192
- console.log('📊 Number of invoices:', completeDocument.Invoice.length)
193
- }
194
-
195
- // Convert the complete document to JSON string
196
- const documentJson = JSON.stringify(completeDocument)
197
-
198
- if (this.debug) {
199
- console.log(`📏 Document JSON size: ${documentJson.length} bytes`)
200
- }
201
-
202
- // Generate SHA256 hash of the JSON document
203
- const crypto = await import('crypto')
204
- const documentHash = crypto
205
- .createHash('sha256')
206
- .update(documentJson, 'utf8')
207
- .digest('hex')
208
-
209
- // Base64 encode the JSON document
210
- const documentBase64 = Buffer.from(documentJson, 'utf8').toString('base64')
211
-
212
- if (this.debug) {
213
- console.log(`🔒 Document hash: ${documentHash.substring(0, 16)}...`)
214
- console.log(`📦 Base64 size: ${documentBase64.length} bytes`)
215
- }
216
-
217
- // Build the submission payload according to MyInvois API format
218
- const submissionPayload = {
219
- documents: documents.map(doc => ({
220
- format: 'JSON', // We're submitting JSON format
221
- document: documentBase64, // Base64 encoded complete document
222
- documentHash: documentHash, // SHA256 hash of the JSON
223
- codeNumber: doc.eInvoiceCodeOrNumber, // Document reference number
224
- })),
225
- }
226
-
227
- if (this.debug) {
228
- console.log('🚀 Submission payload structure:')
229
- console.log('- Format: JSON')
230
- console.log('- Documents count:', submissionPayload.documents.length)
231
- console.log(
232
- '- Total payload size:',
233
- JSON.stringify(submissionPayload).length,
234
- 'bytes',
235
- )
236
-
237
- // Validate submission constraints
238
- const payloadSize = JSON.stringify(submissionPayload).length
239
- if (payloadSize > 5 * 1024 * 1024) {
240
- // 5MB
241
- console.warn('⚠️ WARNING: Payload size exceeds 5MB limit')
242
- }
243
-
244
- if (documents.length > 100) {
245
- console.warn('⚠️ WARNING: Document count exceeds 100 document limit')
246
- }
247
-
248
- if (documentJson.length > 300 * 1024) {
249
- // 300KB per document
250
- console.warn('⚠️ WARNING: Document size exceeds 300KB limit')
251
- }
252
- }
253
-
254
- // Submit to MyInvois API with proper headers
255
- const response = await this.fetch('/api/v1.0/documentsubmissions', {
256
- method: 'POST',
257
- headers: {
258
- 'Content-Type': 'application/json',
259
- },
260
- body: JSON.stringify(submissionPayload),
261
- })
262
-
263
- if (this.debug) {
264
- console.log(`📡 API Response status: ${response.status}`)
265
- }
266
-
267
- const data = (await response.json()) as SubmissionResponse
268
-
269
- if (this.debug) {
270
- if (response.status !== 202) {
271
- console.error('❌ Submission failed with status:', response.status)
272
- console.error('❌ Response data:', data)
273
- } else {
274
- console.log('✅ Submission successful!')
275
- console.log(`📋 Submission UID: ${data.submissionUid}`)
276
- console.log(
277
- `✅ Accepted documents: ${data.acceptedDocuments?.length || 0}`,
278
- )
279
- console.log(
280
- `❌ Rejected documents: ${data.rejectedDocuments?.length || 0}`,
281
- )
282
- }
283
- }
284
-
285
- return {
286
- data,
287
- status: response.status,
288
- }
289
- }
290
-
291
- async getSubmissionStatus(
292
- submissionUid: string,
293
- pollInterval: number = 1000,
294
- maxRetries: number = 10,
295
- ): Promise<{
296
- status: SubmissionStatus
297
- documentSummary?: DocumentSummary[]
298
- error?: {
299
- code: string
300
- message: string | null
301
- target: string
302
- details: {
303
- code: string
304
- message: string
305
- target: string
306
- }[]
307
- }
308
- }> {
309
- try {
310
- const response = await this.fetch(
311
- `/api/v1.0/documentsubmissions/${submissionUid}`,
312
- )
313
-
314
- const data = (await response.json()) as GetSubmissionResponse
315
-
316
- if (this.debug) {
317
- console.log('Submission:', data)
318
- if (data.error) {
319
- console.log('Submission error details:', data.error.details)
320
- }
321
- }
322
-
323
- // If we have a successful response and status is completed, return success
324
- if (data.overallStatus === 'Valid') {
325
- return {
326
- status: data.overallStatus,
327
- documentSummary: data.documentSummary,
328
- }
329
- }
330
- if (data.overallStatus === 'Invalid') {
331
- return {
332
- status: 'Invalid',
333
- documentSummary: data.documentSummary,
334
- }
335
- }
336
-
337
- // If we have retries left, continue polling for any non-completed status or errors
338
- if (maxRetries > 0) {
339
- await new Promise(resolve => setTimeout(resolve, pollInterval))
340
- return await this.getSubmissionStatus(
341
- submissionUid,
342
- pollInterval,
343
- maxRetries - 1,
344
- )
345
- }
346
-
347
- // No retries left - return timeout
348
- return {
349
- status: 'Invalid',
350
- error: {
351
- code: 'Timeout',
352
- message: 'Submission timed out',
353
- target: 'submission',
354
- details: [],
355
- },
356
- }
357
- } catch (error) {
358
- // Handle any request errors by retrying if we have retries left
359
- if (maxRetries > 0) {
360
- if (this.debug) {
361
- console.log('Request error, retrying...', error)
362
- }
363
- await new Promise(resolve => setTimeout(resolve, pollInterval))
364
- return await this.getSubmissionStatus(
365
- submissionUid,
366
- pollInterval,
367
- maxRetries - 1,
368
- )
369
- }
370
-
371
- // No retries left - return timeout
372
- return {
373
- status: 'Invalid',
374
- error: {
375
- code: 'Timeout',
376
- message: 'Submission timed out after request errors',
377
- target: 'submission',
378
- details: [],
379
- },
380
- }
381
- }
382
- }
383
-
384
- /**
385
- * Retrieves a document by its unique identifier with the raw document content.
386
- *
387
- * @param documentUid - The unique identifier of the document to retrieve
388
- * @returns Promise resolving to document summary with raw document content as a string
389
- * @throws {Error} If the document is not found or request fails
390
- *
391
- * @example
392
- * ```typescript
393
- * const document = await client.getDocument('doc-uuid-123');
394
- * console.log(document.document); // Raw XML/JSON content
395
- * console.log(document.uuid); // Document UUID
396
- * ```
397
- */
398
- async getDocument(
399
- documentUid: string,
400
- ): Promise<DocumentSummary & { document: string }> {
401
- const response = await this.fetch(`/api/v1.0/documents/${documentUid}/raw`)
402
-
403
- const data = await response.json()
404
-
405
- return data as DocumentSummary & { document: string }
406
- }
407
-
408
- /**
409
- * Retrieves detailed information about a document including validation results.
410
- *
411
- * @param documentUid - The unique identifier of the document to get details for
412
- * @returns Promise resolving to document summary with detailed validation results
413
- * @throws {Error} If the document is not found or request fails
414
- *
415
- * @example
416
- * ```typescript
417
- * const details = await client.getDocumentDetails('doc-uuid-123');
418
- * console.log(details.validationResults.status); // 'Valid' | 'Invalid' | 'Processing'
419
- * console.log(details.validationResults.validationSteps); // Array of validation step results
420
- * ```
421
- */
422
- async getDocumentDetails(documentUid: string): Promise<
423
- DocumentSummary & {
424
- validationResults: {
425
- status: DocumentValidationResult
426
- validationSteps: DocumentValidationStepResult[]
427
- }
428
- }
429
- > {
430
- const response = await this.fetch(
431
- `/api/v1.0/documents/${documentUid}/details`,
432
- )
433
-
434
- const data = (await response.json()) as DocumentSummary & {
435
- validationResults: {
436
- status: DocumentValidationResult
437
- validationSteps: DocumentValidationStepResult[]
438
- }
439
- }
440
-
441
- return data
442
- }
443
-
444
- /**
445
- * Searches for documents based on various filter criteria.
446
- *
447
- * @param params - Search parameters object
448
- * @param params.uuid - Optional specific document UUID to search for
449
- * @param params.submissionDateFrom - Required start date for submission date range (ISO date string)
450
- * @param params.submissionDateTo - Optional end date for submission date range (ISO date string)
451
- * @param params.pageSize - Optional number of results per page (default handled by API)
452
- * @param params.pageNo - Optional page number for pagination (0-based)
453
- * @param params.issueDateFrom - Optional start date for issue date range (ISO date string)
454
- * @param params.issueDateTo - Optional end date for issue date range (ISO date string)
455
- * @param params.invoiceDirection - Optional filter by invoice direction ('Sent' or 'Received')
456
- * @param params.status - Optional filter by document status
457
- * @param params.documentType - Optional filter by e-invoice type code
458
- * @param params.searchQuery - Optional text search across uuid, buyerTIN, supplierTIN, buyerName, supplierName, internalID, total
459
- * @returns Promise resolving to array of document summaries matching the search criteria
460
- * @throws {Error} If the search request fails
461
- *
462
- * @example
463
- * ```typescript
464
- * // Search for documents submitted in the last 30 days
465
- * const documents = await client.searchDocuments({
466
- * submissionDateFrom: '2024-01-01',
467
- * submissionDateTo: '2024-01-31',
468
- * status: 'Valid',
469
- * pageSize: 10
470
- * });
471
- *
472
- * // Search by supplier name
473
- * const supplierDocs = await client.searchDocuments({
474
- * submissionDateFrom: '2024-01-01',
475
- * searchQuery: 'ACME Corp',
476
- * invoiceDirection: 'Received'
477
- * });
478
- * ```
479
- */
480
- async searchDocuments({
481
- uuid,
482
- submissionDateFrom,
483
- submissionDateTo,
484
- pageSize,
485
- pageNo,
486
- issueDateFrom,
487
- issueDateTo,
488
- invoiceDirection,
489
- status,
490
- documentType,
491
- searchQuery,
492
- }: {
493
- uuid?: string
494
- submissionDateFrom: string
495
- submissionDateTo?: string
496
- pageSize?: number
497
- pageNo?: number
498
- issueDateFrom?: string
499
- issueDateTo?: string
500
- invoiceDirection?: 'Sent' | 'Received'
501
- status?: DocumentStatus
502
- documentType?: EInvoiceTypeCode
503
- searchQuery?: string // Search by uuid, buyerTIN, supplierTIN, buyerName, supplierName, internalID, total
504
- }): Promise<DocumentSummary[]> {
505
- const queryParams = new URLSearchParams()
506
-
507
- if (uuid) queryParams.set('uuid', uuid)
508
- if (submissionDateFrom)
509
- queryParams.set('submissionDateFrom', submissionDateFrom)
510
- if (submissionDateTo) queryParams.set('submissionDateTo', submissionDateTo)
511
- if (pageSize) queryParams.set('pageSize', pageSize.toString())
512
- if (pageNo) queryParams.set('pageNo', pageNo.toString())
513
- if (issueDateFrom) queryParams.set('issueDateFrom', issueDateFrom)
514
- if (issueDateTo) queryParams.set('issueDateTo', issueDateTo)
515
- if (invoiceDirection) queryParams.set('invoiceDirection', invoiceDirection)
516
- if (status) queryParams.set('status', status)
517
- if (documentType) queryParams.set('documentType', documentType)
518
- if (searchQuery) queryParams.set('searchQuery', searchQuery)
519
-
520
- const response = await this.fetch(
521
- `/api/v1.0/documents/search?${queryParams.toString()}`,
522
- )
523
-
524
- const data = (await response.json()) as DocumentSummary[]
525
-
526
- return data
527
- }
528
- }
529
-
530
- export type * from './types/index.d.ts'
@@ -1,115 +0,0 @@
1
- /**
2
- * Represents the allowed classification codes for e-Invoice items.
3
- * Based on the documentation: https://sdk.myinvois.hasil.gov.my/codes/classification-codes/
4
- */
5
- export type ClassificationCode =
6
- | '001' // Breastfeeding equipment
7
- | '002' // Child care centres and kindergartens fees
8
- | '003' // Computer, smartphone or tablet
9
- | '004' // Consolidated e-Invoice
10
- | '005' // Construction materials (as specified under Fourth Schedule of the Lembaga Pembangunan Industri Pembinaan Malaysia Act 1994)
11
- | '006' // Disbursement
12
- | '007' // Donation
13
- | '008' // e-Commerce - e-Invoice to buyer / purchaser
14
- | '009' // e-Commerce - Self-billed e-Invoice to seller, logistics, etc.
15
- | '010' // Education fees
16
- | '011' // Goods on consignment (Consignor)
17
- | '012' // Goods on consignment (Consignee)
18
- | '013' // Gym membership
19
- | '014' // Insurance - Education and medical benefits
20
- | '015' // Insurance - Takaful or life insurance
21
- | '016' // Interest and financing expenses
22
- | '017' // Internet subscription
23
- | '018' // Land and building
24
- | '019' // Medical examination for learning disabilities and early intervention or rehabilitation treatments of learning disabilities
25
- | '020' // Medical examination or vaccination expenses
26
- | '021' // Medical expenses for serious diseases
27
- | '022' // Others
28
- | '023' // Petroleum operations (as defined in Petroleum (Income Tax) Act 1967)
29
- | '024' // Private retirement scheme or deferred annuity scheme
30
- | '025' // Motor vehicle
31
- | '026' // Subscription of books / journals / magazines / newspapers / other similar publications
32
- | '027' // Reimbursement
33
- | '028' // Rental of motor vehicle
34
- | '029' // EV charging facilities (Installation, rental, sale / purchase or subscription fees)
35
- | '030' // Repair and maintenance
36
- | '031' // Research and development
37
- | '032' // Foreign income
38
- | '033' // Self-billed - Betting and gaming
39
- | '034' // Self-billed - Importation of goods
40
- | '035' // Self-billed - Importation of services
41
- | '036' // Self-billed - Others
42
- | '037' // Self-billed - Monetary payment to agents, dealers or distributors
43
- | '038' // Sports equipment, rental / entry fees for sports facilities, registration in sports competition or sports training fees imposed by associations / sports clubs / companies registered with the Sports Commissioner or Companies Commission of Malaysia and carrying out sports activities as listed under the Sports Development Act 1997
44
- | '039' // Supporting equipment for disabled person
45
- | '040' // Voluntary contribution to approved provident fund
46
- | '041' // Dental examination or treatment
47
- | '042' // Fertility treatment
48
- | '043' // Treatment and home care nursing, daycare centres and residential care centers
49
- | '044' // Vouchers, gift cards, loyalty points, etc
50
- | '045' // Self-billed - Non-monetary payment to agents, dealers or distributors
51
-
52
- /**
53
- * Enum representing the allowed classification codes with descriptive names.
54
- * Provides a more readable way to reference classification codes.
55
- *
56
- * @example
57
- * const code = ClassificationCodeEnum.ComputerSmartphoneOrTablet;
58
- * console.log(code); // Output: "003"
59
- */
60
- export enum ClassificationCodeEnum {
61
- BreastfeedingEquipment = '001',
62
- ChildCareCentresAndKindergartensFees = '002',
63
- ComputerSmartphoneOrTablet = '003',
64
- ConsolidatedEInvoice = '004',
65
- ConstructionMaterials = '005',
66
- Disbursement = '006',
67
- Donation = '007',
68
- ECommerceEInvoiceToBuyer = '008',
69
- ECommerceSelfBilledToSellerLogistics = '009',
70
- EducationFees = '010',
71
- GoodsOnConsignmentConsignor = '011',
72
- GoodsOnConsignmentConsignee = '012',
73
- GymMembership = '013',
74
- InsuranceEducationMedicalBenefits = '014',
75
- InsuranceTakafulLife = '015',
76
- InterestFinancingExpenses = '016',
77
- InternetSubscription = '017',
78
- LandAndBuilding = '018',
79
- MedicalExamLearningDisabilities = '019',
80
- MedicalExamVaccination = '020',
81
- MedicalExpensesSeriousDiseases = '021',
82
- Others = '022',
83
- PetroleumOperations = '023',
84
- PrivateRetirementSchemeDeferredAnnuity = '024',
85
- MotorVehicle = '025',
86
- SubscriptionBooksJournalsEtc = '026',
87
- Reimbursement = '027',
88
- RentalOfMotorVehicle = '028',
89
- EVChargingFacilities = '029',
90
- RepairAndMaintenance = '030',
91
- ResearchAndDevelopment = '031',
92
- ForeignIncome = '032',
93
- SelfBilledBettingGaming = '033',
94
- SelfBilledImportationGoods = '034',
95
- SelfBilledImportationServices = '035',
96
- SelfBilledOthers = '036',
97
- SelfBilledMonetaryPaymentToAgents = '037',
98
- SportsEquipmentRentalFeesEtc = '038',
99
- SupportingEquipmentDisabledPerson = '039',
100
- VoluntaryContributionProvidentFund = '040',
101
- DentalExamTreatment = '041',
102
- FertilityTreatment = '042',
103
- TreatmentHomeCareNursingEtc = '043',
104
- VouchersGiftCardsLoyaltyPoints = '044',
105
- SelfBilledNonMonetaryPaymentToAgents = '045',
106
- }
107
-
108
- /**
109
- * Interface representing a classification code entry.
110
- * Contains the code and its corresponding description.
111
- */
112
- export interface Classification {
113
- code: ClassificationCode
114
- description: string
115
- }