@ekwo-ai/myinvois 0.0.0-stage → 0.15.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/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+ - First version: the MyInvois e-Invoice of a posted document — invoice,
11
+ credit note, debit note and refund note, the supplier's (`01`–`04`) or
12
+ self-billed (`11`–`14`), versions 1.0 and 1.1 — as UBL 2.1 in JSON or XML,
13
+ with LHDNM's code tables, and every rule of the SDK the document breaks by
14
+ LHDNM's error code where it publishes one. Unsigned, and submitted by the
15
+ host.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EKWO OÜ, trading as Ekwo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,361 @@
1
- # Temporary Holding Version
1
+ # `@ekwo-ai/myinvois`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Malaysia's **MyInvois e-Invoice** — the UBL 2.1 document the Inland Revenue
4
+ Board of Malaysia (Lembaga Hasil Dalam Negeri Malaysia, LHDNM) validates
5
+ before an invoice is the taxpayer's — in TypeScript, with no dependencies.
6
+ It writes the eight document types of the MyInvois SDK: the invoice, the
7
+ credit note, the debit note and the refund note, each as the supplier issues
8
+ it or **self-billed** by the buyer, in the JSON syntax or the XML one.
9
+
10
+ Give it a posted document as the books hold it, with what MyInvois asks and
11
+ the books do not hold — the LHDNM identifiers of both parties, the supplier's
12
+ MSIC code, the moment of issuance — and it gives you the document, a name for
13
+ it, and every rule of the SDK the document breaks, named by LHDNM's own error
14
+ code where it publishes one.
15
+
16
+ ```ts
17
+ import { MYINVOIS } from '@ekwo-ai/myinvois';
18
+
19
+ const input = {
20
+ header, lines, taxes, // the three views of a posted document
21
+ version: '1.1', // the default: to be signed by the host; 1.0 unsigned
22
+ issuedAt: '2026-01-15T10:30:00+08:00', // written in UTC: 2026-01-15, 02:30:00Z
23
+ supplier: { tin, sst: 'NA', tourismTax: 'NA', activity: { msic: '46510', description } },
24
+ buyer: { tin, phone: '+60123456789' },
25
+ };
26
+
27
+ const { content, filename, typeCode, violations } = MYINVOIS.write(input); // JSON
28
+ const xml = MYINVOIS.write(input, 'xml').content; // XML
29
+ ```
30
+
31
+ `header`, `lines` and `taxes` are one row of `document_header`, the rows of
32
+ `document_line_items` and the rows of `document_tax_summary` in
33
+ [Ekwo OS](https://github.com/Ekwo-ai/ekwo-os) — the same three reads
34
+ `@ekwo-ai/peppol-ubl` and `@ekwo-ai/verifactu` take. They are declared in
35
+ this package's own types, so any invoicing system that can produce the same
36
+ rows can use it.
37
+
38
+ Writing the document is all it does. **It signs nothing**: version 1.1 of
39
+ every document type is the same structure as 1.0 with the signature
40
+ validated, and the signature — an XAdES enveloped signature in
41
+ `UBLExtensions` and `cac:Signature`, with a certificate issued by a
42
+ certification authority licensed by the MCMC — is added by the host after
43
+ this package and before the submission. **It submits nothing**: the login,
44
+ the `documentHash` and the Base64 encoding of the submission, the API call,
45
+ LHDNM's validation, the Unique Identifier Number and the QR code it returns,
46
+ the 72-hour window in which a document may be rejected or cancelled — all of
47
+ it belongs to a transport. In Ekwo, the signature and the transport are for
48
+ Ekwo Cloud. **It reads no clock and no network**: the moment of issuance is
49
+ given, and every code list is in the package.
50
+
51
+ ## The interface
52
+
53
+ ```ts
54
+ interface MyInvoisInput {
55
+ header: Row; lines: Row[]; taxes: Row[];
56
+ version?: '1.0' | '1.1'; // 1.1 when left out
57
+ kind?: 'invoice' | 'credit-note' | 'debit-note' | 'refund-note';
58
+ issuedAt: string; // ISO 8601 with its offset
59
+ supplier: PartyIdentifiers & { activity?: { msic: string; description: string } };
60
+ buyer: PartyIdentifiers;
61
+ adjusts?: { number: string; uuid: string }[]; // what a note adjusts
62
+ exchangeRate?: Numeric; // ringgit per unit, where the currency is not MYR
63
+ paymentMode?: string; // a code of LHDNM's payment modes
64
+ }
65
+
66
+ interface PartyIdentifiers {
67
+ tin?: string; // the TIN LHDNM assigned
68
+ registration?: { scheme: 'BRN' | 'NRIC' | 'PASSPORT' | 'ARMY'; id: string };
69
+ sst?: string; // one or two numbers, or 'NA'
70
+ tourismTax?: string; // the supplier's, or 'NA'
71
+ phone?: string; // E.164
72
+ state?: string; // a state code, where the header's region does not give it
73
+ }
74
+
75
+ MYINVOIS.write(input, syntax = 'json'): {
76
+ content; filename; mediaType; syntax;
77
+ typeCode; // 01–04, 11–14
78
+ version; requiresSignature; // true for 1.1
79
+ violations; // { code, message, line? }
80
+ }
81
+ ```
82
+
83
+ - **Which document.** A `sale_invoice` is an invoice (`01`) and a
84
+ `sale_credit_note` a credit note (`02`) without being told. The books keep
85
+ no separate type for a debit note or a refund note — a debit note is booked
86
+ as an invoice that adds to an earlier one, a refund note as a credit note
87
+ whose money went back — so `kind: 'debit-note'` (`03`) and
88
+ `kind: 'refund-note'` (`04`) are asked for by name, and refused on a
89
+ document of the other side.
90
+ - **Self-billed.** A `purchase_invoice` or `purchase_credit_note` is a
91
+ document the company issues as the buyer: `11` to `14`. In the views
92
+ `seller_*` is always the company and `buyer_*` its contact, so on a
93
+ purchase the two change places — the contact is written as
94
+ `AccountingSupplierParty`, the company as `AccountingCustomerParty` — and
95
+ `supplier` and `buyer` in the input always mean the parties of the
96
+ document, as the SDK names them.
97
+ - **The version is 1.1 unless 1.0 is asked for.** LHDNM recommends 1.1
98
+ (FAQ) and accepts version 1.0 without a signature "until such time as
99
+ \[it\] issues an official notice concerning the retirement of version 1.0".
100
+ A 1.1 document written here is complete once signed, and
101
+ `requiresSignature` says so.
102
+ - **The moment of issuance** is written in UTC, which is what the SDK asks
103
+ for `IssueDate` and `IssueTime`. It is not the date of the document in the
104
+ books: a document issued at 07:00 in Kuala Lumpur is issued the day before
105
+ in UTC.
106
+
107
+ `write` throws a `MyInvoisError` for what makes a document absurd: no
108
+ document, a quote or an order, a document never numbered, no currency, no
109
+ line, an amount that is not a number, a moment that is not ISO 8601 with an
110
+ offset, a kind the document cannot be. Everything else comes back in
111
+ `violations`, beside a document that says what it was given.
112
+
113
+ ## The figures are the books' figures
114
+
115
+ Nothing is recomputed. Totals, bases and taxes are written as they were
116
+ posted, and where they do not add up the mathematical mapping of the SDK they
117
+ break is named rather than the figure corrected. No figure passes through a
118
+ `number`: amounts arrive as the text Postgres prints a `numeric` as, are
119
+ compared as exact decimals on `bigint` — `src/decimal.ts` is the file of
120
+ `@ekwo-ai/peppol-ubl`, byte for byte, and a test compares the two — and are
121
+ written in the JSON as the same digits, unquoted: a JSON number is a decimal
122
+ in text.
123
+
124
+ Three figures are derived, each without rounding:
125
+
126
+ - **The subtotal of a line** (`ItemPriceExtension`) is its price times its
127
+ quantity, exactly, and **its discount** the subtotal less the net amount
128
+ the books posted, with the rate (`MultiplierFactorNumeric`) the keyed
129
+ percentage over a hundred.
130
+ - **The tax of a line.** The books round the tax of all the lines under one
131
+ tax once, so the tax of a line is a figure they hold only where the line is
132
+ alone under its tax, or where the row carries it (`tax_amount`). Otherwise
133
+ it is base times rate, written only where that is exact to the cent for
134
+ every line of the tax and adds up to what was posted; anywhere else it is
135
+ reported, `input:line_tax_amount`.
136
+ - **The amount payable** is the total including tax. A payment received
137
+ after the document was issued is a payment, not the prepayment
138
+ `PrepaidPayment` records, and the books do not say when it was received.
139
+
140
+ A credit note's figures are positive, as the books hold them and as LHDNM's
141
+ samples write them: its type code says what it is.
142
+
143
+ ## Violations
144
+
145
+ Each violation is `{ code, message, line? }`, the shape of
146
+ `EinvoiceViolation` in the core:
147
+
148
+ | Code | What it is |
149
+ |---|---|
150
+ | `CF…`, `CV…` | **LHDNM's own error code**, where the SDK publishes one for the rule, with LHDNM's own sentence |
151
+ | `required:<element>` | A field the SDK's data structure marks mandatory, and the document does not hold |
152
+ | `length:<element>` | A value longer than the SDK's "Number of Chars" allows |
153
+ | `format:<element>` | A value not shaped as the SDK describes it, where it publishes no code for it |
154
+ | `math:<element>` | A total that is not what the SDK's mathematical mappings make of the figures beside it |
155
+ | `input:<what>` | What the books do not hold and the caller gives: `input:supplier_tin`, `input:tax_type`, `input:classification`… |
156
+
157
+ **Empty `violations` is the only state to submit in.** A document that breaks
158
+ a rule leaves out what it does not know, and the schema may refuse it too.
159
+
160
+ | Code | What is checked |
161
+ |---|---|
162
+ | CF345, CF346 | An address line of the supplier, of the buyer, longer than 150 characters |
163
+ | CF404 | A city longer than 50 characters |
164
+ | CF405 | A postcode of more than five digits in Malaysia, or longer than 50 characters elsewhere |
165
+ | CF406 | An SST registration number: letters, digits and dashes, at most two separated by `;`, at most 35 characters, or `NA` |
166
+ | CF407, CF412 | A tourism tax registration number: letters, digits and dashes, at most 17 characters, or `NA` |
167
+ | CF408, CF409, CF410 | A telephone: digits and a leading `+`, between 8 and 20 characters |
168
+ | CF400, CF401, CF411 | An e-mail address: its shape, and at most 320 characters |
169
+ | CV302 | A code outside its list: currency, payment mode, MSIC, classification, unit, tax type, state, country |
170
+ | CV316 | A state code 01–16 on an address outside Malaysia |
171
+ | CV317 | State code 17 on an address in Malaysia (it is for a foreign address, and for a consolidated e-Invoice, which this package does not write) |
172
+ | `required:` | The names of both parties, their registration or identification number, address line, city, state, country and telephone; the supplier's business activity description; each line's description, unit price and subtotal; the reason of a tax exemption (`E`); the number and UUID of an adjusted document |
173
+ | `length:` | The document number (50), the names (300), the TIN (14), a BRN (20), an NRIC, passport or army number (12), the MSIC code (five digits), the activity description (300), the bank account (150), the payment terms (300), the bill reference (150), a line's description (300), a reason of exemption (300), a UUID (26) |
174
+ | `format:` | A TIN: `IG` and digits for an individual; for a non-individual one of the prefixes `C`, `CS`, `D`, `F`, `FA`, `PT`, `TA`, `TC`, `TN`, `TR`, `TP`, `J`, `LE`, then digits that start with no zero and end with one (FAQ); a general TIN, `EI…`, is not checked |
175
+ | `math:` | The total including tax against the total excluding tax plus the tax; the total tax against its tax types; the net total against the lines; per tax type, the tax against the taxes of its lines and the base against their net amounts (except for an exemption, `E`); per line, the net against the subtotal less the discount |
176
+ | `input:` | The TIN of each party; the supplier's SST and tourism tax numbers (the supplier alone knows whether it is registered); its MSIC code; the buyer's telephone, which the views do not publish; the tax type of each tax; the classification of each line; the tax of a line the books did not keep; the net price of a line keyed with its tax; the exchange rate of a foreign currency; the payment mode of an account to pay into; the adjusted document of a note; a country with no three-letter code in LHDNM's list |
177
+
178
+ What only LHDNM can check is not checked here: that a TIN exists and goes
179
+ with the identification beside it (the taxpayer validator, `ERR…`), that a
180
+ referenced UUID is a valid document of the same supplier and buyer (`DR…`),
181
+ that a document is not a duplicate, and the signature.
182
+
183
+ ## From the rows to the document
184
+
185
+ ### `document_header`
186
+
187
+ | Column | Becomes |
188
+ |---|---|
189
+ | `number` | `cbc:ID`, the e-Invoice code or number |
190
+ | `doc_type` | `cbc:InvoiceTypeCode`, with `kind`: see above |
191
+ | `currency_code` | `cbc:DocumentCurrencyCode`, and `cac:TaxExchangeRate` from it to MYR where it is not MYR |
192
+ | `amount_untaxed` | `LineExtensionAmount` and `TaxExclusiveAmount` |
193
+ | `amount_tax` | `cac:TaxTotal/cbc:TaxAmount` |
194
+ | `amount_total` | `TaxInclusiveAmount` and `PayableAmount` |
195
+ | `payment_terms` | `cac:PaymentTerms/cbc:Note` |
196
+ | `payment_reference` | `cac:BillingReference/cac:AdditionalDocumentReference/cbc:ID`, the bill reference number |
197
+ | `payee_iban` | `cac:PayeeFinancialAccount/cbc:ID`, written with a `paymentMode` |
198
+ | `{side}_legal_name`, else `{side}_name` | `cac:PartyLegalEntity/cbc:RegistrationName` |
199
+ | `{side}_registration_number` | The BRN, where no `registration` is given: the SSM number, ISO 6523 scheme `0230` in the Malaysian pack |
200
+ | `{side}_address_line1`, `_line2` | `cac:AddressLine/cbc:Line` |
201
+ | `{side}_city`, `_postal_code` | `cbc:CityName`, `cbc:PostalZone` |
202
+ | `{side}_region` | `cbc:CountrySubentityCode`: a state code as it is, the name of a state of LHDNM's list as its code, a foreign region as it is, `17` for a foreign address with none |
203
+ | `{side}_country` | `cac:Country/cbc:IdentificationCode`, ISO 3166-1 alpha-3 |
204
+ | `seller_phone`, `{side}_email` | `cac:Contact` |
205
+
206
+ ### `document_line_items`: one `cac:InvoiceLine` per `product` line
207
+
208
+ | Column | Becomes |
209
+ |---|---|
210
+ | `sequence` | `cbc:ID` |
211
+ | `quantity`, `unit_code` | `cbc:InvoicedQuantity` and its `@unitCode` |
212
+ | `amount_untaxed` | `cbc:LineExtensionAmount`, and the base of its tax |
213
+ | `unit_price` (or `net_unit_price` where the price holds its tax) | `cac:Price/cbc:PriceAmount` |
214
+ | `discount_percent` | `cac:AllowanceCharge`, with `ChargeIndicator` false |
215
+ | `item_name`, else `item_description` | `cac:Item/cbc:Description` |
216
+ | `vat_rate` | `cac:TaxSubtotal/cbc:Percent` |
217
+ | `tax_code`, else `vat_category` and `vat_rate` | Which tax of the breakdown the line was booked under |
218
+
219
+ ### `document_tax_summary`: the taxes, one `cac:TaxSubtotal` per tax type
220
+
221
+ `base_amount` and `tax_charged` of every row of one LHDNM tax type are added
222
+ into one `TaxableAmount` and one `TaxAmount`; `exemption_reason` is the
223
+ `TaxExemptionReason` of an exemption (`E`). `cac:TaxScheme/cbc:ID` is `OTH`
224
+ in the scheme `UN/ECE 5153`, agency `6`, as every sample of the SDK writes
225
+ it: Malaysia's taxes are not a value added tax.
226
+
227
+ ### What the rows must carry for the document to be complete
228
+
229
+ None of it is in the three views alone. In Ekwo OS the core gives it, from
230
+ the country pack and from the books (`EINVOICE_FORMATS` in `@ekwo-ai/core`,
231
+ profile `myinvois`): the pack's fiscalisation regime gives each tax its
232
+ `myinvois_tax_type` and every line its `myinvois_classification`, and
233
+ `companies.identifiers` and `contacts.identifiers` give each party its TIN,
234
+ its SST and tourism tax registrations by scheme. Any other caller gives it:
235
+
236
+ - **`myinvois_tax_type`** on each row of `document_tax_summary` (or on a
237
+ line): `01` Sales Tax, `02` Service Tax, `03` Tourism Tax, `04` High-Value
238
+ Goods Tax, `05` Sales Tax on Low Value Goods, `06` Not Applicable, `E` Tax
239
+ exemption. Nothing in the books decides it: a category `S` at 10 % is the
240
+ Sales Tax in the Malaysian pack and could be any tax in another, and
241
+ whether an export or a sale outside the scope of the tax is `06` or `E` is
242
+ a reading of the law this package does not make.
243
+ - **`myinvois_classification`** on each line: one code of LHDNM's 45, or
244
+ several. It describes what was sold for LHDNM's purposes — a computer, a
245
+ donation, a disbursement, `022` Others — which the books do not record.
246
+ - **`tax_amount`** on a line that shares its tax with others, where base
247
+ times rate is not exact to the cent.
248
+ - **`tax_code`** on a line, where two taxes of the document share a category
249
+ and a rate.
250
+
251
+ ## Named gaps
252
+
253
+ What this package does not write, because it could not be sourced, because
254
+ the books do not hold it, or because it is not a document's to do:
255
+
256
+ - **The signature, the submission, the UIN and the QR code**, as said above.
257
+ - **The consolidated e-Invoice** (classification `004`, the general TIN
258
+ `EI00000000010`, state `17`) and every **general TIN** for a buyer or a
259
+ supplier that has none — foreign buyers, foreign suppliers of a self-billed
260
+ invoice, individuals. The taxpayer validator's codes name them (`010`,
261
+ `020`, `030`, `040`), but the SDK pages read for this package publish
262
+ neither the full numbers nor the rules of their use beyond the consolidated
263
+ sample, and none is invented here: a TIN is always given.
264
+ - **Fixed-rate taxes**: `PerUnitAmount` and `BaseUnitMeasure`, the way the
265
+ tourism tax is charged per room per night. The books hold a tax as a rate.
266
+ - **Invoice-level allowances and charges**, `PrepaidPayment`,
267
+ `PayableRoundingAmount`, `InvoicePeriod` (frequency and period of billing),
268
+ the shipping recipient (`cac:Delivery`), the customs forms No. 1, 2 and 9,
269
+ the Incoterms, the free trade agreement, the certified exporter's
270
+ authorisation, the product tariff code (`PTC`) and the country of origin:
271
+ the views carry none of them.
272
+ - **The tax in ringgit of a document in another currency.** LHDNM's foreign
273
+ currency sample writes `TaxCurrencyCode` `MYR` and its tax amounts in
274
+ ringgit; its currency validator also says that every amount is in the
275
+ document's currency (DC502), and that tax amounts are in `TaxCurrencyCode`
276
+ where one is given (DC501). The books hold the tax of a document in that
277
+ document's currency, so this package writes every amount in it, gives the
278
+ exchange rate, and leaves `TaxCurrencyCode`, which is optional, out. That
279
+ reading has not been checked against the sandbox.
280
+ - **The special characters of a reason of exemption.** The SDK limits the
281
+ text to letters, digits and `. - , ( )`, and its own example, "Reference
282
+ No: ABC.TAX(S)012-3/4/56(7)", holds a colon and slashes. The rule is not
283
+ checked until the two agree.
284
+ - **Five countries of LHDNM's list** have no pair in the UN table this
285
+ package maps two-letter codes from — `ANT`, `CRM`, `SCT`, `TWN`, `XKX` — and
286
+ a document to or from them is reported (`input:<role>_country`).
287
+ - **The shape of a BRN, an NRIC, a passport or an army number**: the SDK
288
+ publishes their lengths and not their form, and only the lengths are
289
+ checked. A TIN is checked against the shape the FAQ describes, and whether
290
+ it exists is LHDNM's to say.
291
+ - **SVDP 1.2 and 1.3**, the document versions of the Special Voluntary
292
+ Disclosure Programme, which allow a backdated moment of issuance during
293
+ the programme only.
294
+
295
+ ## Sources
296
+
297
+ Read on 19 October 2026 on the MyInvois SDK of LHDNM,
298
+ <https://sdk.myinvois.hasil.gov.my/>, last updated 25 September 2026
299
+ (release notes, SDK 1.0):
300
+
301
+ - **The document types**, each with its data structure — field, UBL path,
302
+ mandatory or not, number of characters, cardinality: Invoice
303
+ (<https://sdk.myinvois.hasil.gov.my/documents/invoice-v1-1/>, and
304
+ `invoice-v1-0`), Credit Note (`credit-v1-1`), Debit Note (`debit-v1-1`),
305
+ Refund Note (`refund-v1-1`), and the self-billed four
306
+ (`self-billed-invoice-v1-1`, `self-billed-credit-v1-1`,
307
+ `self-billed-debit-v1-1`, `self-billed-refund-v1-1`), under
308
+ `https://sdk.myinvois.hasil.gov.my/documents/`.
309
+ - **The samples** of each document type, in JSON and XML,
310
+ `https://sdk.myinvois.hasil.gov.my/files/sdksamples/`; the JSON ones are
311
+ kept under `test/fixtures/sdk/`, with their checksums.
312
+ - **The mathematical mappings**,
313
+ <https://sdk.myinvois.hasil.gov.my/mathematical-mappings/>.
314
+ - **The validators and their error codes**,
315
+ <https://sdk.myinvois.hasil.gov.my/document-validation-rules/>, and the
316
+ pages of the core fields (`CF…`), code (`CV…`), currency (`DC…`),
317
+ referenced documents (`DR…`) and taxpayer (`ERR…`) validators under
318
+ `https://sdk.myinvois.hasil.gov.my/documentvalidationrules/`.
319
+ - **The FAQ**, <https://sdk.myinvois.hasil.gov.my/faq/> — the shape of a
320
+ TIN, versions 1.0 and 1.1, the signature, the size of a submission.
321
+ - **The signature**, <https://sdk.myinvois.hasil.gov.my/signature/>, which
322
+ this package leaves to the host.
323
+
324
+ **The code tables**, each from its page and the JSON file LHDNM publishes
325
+ beside it, kept unmodified under `test/codelist/` and compiled into
326
+ `src/codelists.ts` by `scripts/build-codelists.mjs`:
327
+
328
+ | Table | Page | File | Exported as |
329
+ |---|---|---|---|
330
+ | e-Invoice types (8) | <https://sdk.myinvois.hasil.gov.my/codes/e-invoice-types/> | `EInvoiceTypes.json` | `EINVOICE_TYPES` |
331
+ | Tax types (7) | <https://sdk.myinvois.hasil.gov.my/codes/tax-types/> | `TaxTypes.json` | `TAX_TYPES` |
332
+ | Classification codes (45) | <https://sdk.myinvois.hasil.gov.my/codes/classification-codes/> | `ClassificationCodes.json` | `CLASSIFICATION_CODES` |
333
+ | MSIC codes (1,175 rows, 1,174 codes: `16211` is listed twice) | <https://sdk.myinvois.hasil.gov.my/codes/msic-codes/> | `MSICSubCategoryCodes.json` | `MSIC_CODES` |
334
+ | State codes (17) | <https://sdk.myinvois.hasil.gov.my/codes/state-codes/> | `StateCodes.json` | `STATE_CODES` |
335
+ | Country codes (253, ISO 3166-1 alpha-3) | <https://sdk.myinvois.hasil.gov.my/codes/countries/> | `CountryCodes.json` | `COUNTRY_CODES` |
336
+ | Currency codes (180, ISO 4217) | <https://sdk.myinvois.hasil.gov.my/codes/currencies/> | `CurrencyCodes.json` | `CURRENCY_CODES` |
337
+ | Unit types (2,162, UN/ECE Recommendation 20) | <https://sdk.myinvois.hasil.gov.my/codes/unit-types/> | `UnitTypes.json` | `UNIT_CODES` |
338
+ | Payment modes (8) | <https://sdk.myinvois.hasil.gov.my/codes/payment-methods/> | `PaymentMethods.json` | `PAYMENT_MODES` |
339
+
340
+ **ISO 3166-1, two letters to three.** The books hold a country as its
341
+ two-letter code and MyInvois wants the three-letter one; the pairs are the
342
+ columns "ISO-alpha2 Code" and "ISO-alpha3 Code" of the M49 table of the
343
+ United Nations Statistics Division, *Standard country or area codes for
344
+ statistical use (M49)*, <https://unstats.un.org/unsd/methodology/m49/overview/>,
345
+ read on 19 October 2026 and kept as `test/codelist/m49-iso-alpha.csv`. A
346
+ pair is kept where its three-letter code is in LHDNM's list (`ALPHA3`, 248
347
+ pairs).
348
+
349
+ **The schema.** OASIS UBL 2.1, the schemas `@ekwo-ai/peppol-ubl` keeps under
350
+ its `test/xsd/`: `test/schema.test.ts` validates every XML document against
351
+ them from there.
352
+
353
+ **The law.** Income Tax Act 1967, section 82C, and the Income Tax (Issuance
354
+ of Electronic Invoice) Rules 2024 \[P.U. (A) 265/2024\], as the Malaysian
355
+ pack of Ekwo OS cites them (`packs/my/pack.json`).
356
+
357
+ ## Licence
358
+
359
+ MIT. The files under `test/codelist/` and `test/fixtures/sdk/` are LHDNM's
360
+ and the United Nations', kept as published, are fixtures of the tests, and
361
+ are not part of the published package.
@@ -0,0 +1,32 @@
1
+ /**
2
+ * GENERATED by `scripts/build-codelists.mjs` — do not edit.
3
+ *
4
+ * Read from the JSON files LHDNM publishes with the MyInvois SDK,
5
+ * <https://sdk.myinvois.hasil.gov.my/codes/>, kept under `test/codelist/`.
6
+ * `test/codelists.test.ts` compares every list here with those files.
7
+ */
8
+ /** e-Invoice types (`cbc:InvoiceTypeCode`): 8 codes. From `EInvoiceTypes.json`, <https://sdk.myinvois.hasil.gov.my/codes/e-invoice-types/>. */
9
+ export declare const EINVOICE_TYPES: Readonly<Record<string, string>>;
10
+ /** Tax types (`cac:TaxCategory/cbc:ID`): 7 codes. From `TaxTypes.json`, <https://sdk.myinvois.hasil.gov.my/codes/tax-types/>. */
11
+ export declare const TAX_TYPES: Readonly<Record<string, string>>;
12
+ /** Classification codes (`cbc:ItemClassificationCode[@listID="CLASS"]`): 45 codes. From `ClassificationCodes.json`, <https://sdk.myinvois.hasil.gov.my/codes/classification-codes/>. */
13
+ export declare const CLASSIFICATION_CODES: Readonly<Record<string, string>>;
14
+ /** State codes (`cbc:CountrySubentityCode`): 17 codes. From `StateCodes.json`, <https://sdk.myinvois.hasil.gov.my/codes/state-codes/>. */
15
+ export declare const STATE_CODES: Readonly<Record<string, string>>;
16
+ /** Payment modes (`cbc:PaymentMeansCode`): 8 codes. From `PaymentMethods.json`, <https://sdk.myinvois.hasil.gov.my/codes/payment-methods/>. */
17
+ export declare const PAYMENT_MODES: Readonly<Record<string, string>>;
18
+ /** Country codes, ISO 3166-1 alpha-3 (`cac:Country/cbc:IdentificationCode`): 253 codes. From `CountryCodes.json`, <https://sdk.myinvois.hasil.gov.my/codes/countries/>. */
19
+ export declare const COUNTRY_CODES: ReadonlySet<string>;
20
+ /** Currency codes, ISO 4217 (`cbc:DocumentCurrencyCode`): 180 codes. From `CurrencyCodes.json`, <https://sdk.myinvois.hasil.gov.my/codes/currencies/>. */
21
+ export declare const CURRENCY_CODES: ReadonlySet<string>;
22
+ /** Unit types, UN/ECE Recommendation 20 (`cbc:InvoicedQuantity/@unitCode`): 2162 codes. From `UnitTypes.json`, <https://sdk.myinvois.hasil.gov.my/codes/unit-types/>. */
23
+ export declare const UNIT_CODES: ReadonlySet<string>;
24
+ /** MSIC codes (`cbc:IndustryClassificationCode`): 1175 codes. From `MSICSubCategoryCodes.json`, <https://sdk.myinvois.hasil.gov.my/codes/msic-codes/>. */
25
+ export declare const MSIC_CODES: ReadonlySet<string>;
26
+ /**
27
+ * ISO 3166-1 alpha-2 to alpha-3: 248 pairs. From the M49 table of the UN Statistics
28
+ * Division (`m49-iso-alpha.csv`), <https://unstats.un.org/unsd/methodology/m49/overview/>,
29
+ * kept where the alpha-3 code is in COUNTRY_CODES.
30
+ */
31
+ export declare const ALPHA3: Readonly<Record<string, string>>;
32
+ //# sourceMappingURL=codelists.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codelists.d.ts","sourceRoot":"","sources":["../src/codelists.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,+IAA+I;AAC/I,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS3D,CAAC;AAEF,iIAAiI;AACjI,eAAO,MAAM,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQtD,CAAC;AAEF,wLAAwL;AACxL,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA8CjE,CAAC;AAEF,0IAA0I;AAC1I,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkBxD,CAAC;AAEF,+IAA+I;AAC/I,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS1D,CAAC;AAEF,2KAA2K;AAC3K,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,CAY7C,CAAC;AAEF,0JAA0J;AAC1J,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAS9C,CAAC;AAEF,yKAAyK;AACzK,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,MAAM,CAyF1C,CAAC;AAEF,0JAA0J;AAC1J,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,MAAM,CA2E1C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyPnD,CAAC"}