Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental release v0.1.1 release #5

Merged
merged 6 commits into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const egsunit: EGSUnitInfo = {/*...*/};
// Init EGS unit
const egs = new EGS(egsunit);
// New Keys & CSR for the EGS
await egs.generateNewKeysAndCSR(false);
await egs.generateNewKeysAndCSR(false, "solution_name");
// Issue a new compliance cert for the EGS
const compliance_rid = await egs.issueComplianceCertificate("123345");
// Sign invoice
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "zatca-xml-js",
"version": "0.1.0",
"version": "0.1.1",
"description": "An implementation of Saudi Arabia ZATCA's E-Invocing requirements, processes, and standards.",
"main": "lib/index.js",
"files": ["lib/**/*"],
"scripts": {
"build": "tsc --project tsconfig.build.json",
"test": "tsc && node testing_lib/tests/test.js",
"example": "tsc && node lib/examples/full.js",
"example": "tsc --project tsconfig.build.json && node lib/examples/full.js",
"prepare": "npm run build"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src/examples/full.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const main = async () => {
const egs = new EGS(egsunit);

// New Keys & CSR for the EGS
await egs.generateNewKeysAndCSR(false);
await egs.generateNewKeysAndCSR(false, "solution_name");

// Issue a new compliance cert for the EGS
const compliance_request_id = await egs.issueComplianceCertificate("123345");
Expand Down
23 changes: 9 additions & 14 deletions src/zatca/ZATCASimplifiedTaxInvoice.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { XMLDocument } from "../parser";
import { generateSignedXMLString } from "./signing";
import defaultSimplifiedTaxInvoice, { ZATCASimplifiedInvoiceLineItem, ZATCASimplifiedInvoiceProps } from "./templates/simplified_tax_invoice_template";

export {ZATCASimplifiedInvoiceLineItem, ZATCASimplifiedInvoiceProps};
import defaultSimplifiedTaxInvoice, {
ZATCASimplifiedInvoiceLineItem,
ZATCASimplifiedInvoiceProps,
ZATCAInvoiceTypes,
ZATCAPaymentMethods
} from "./templates/simplified_tax_invoice_template";

export {ZATCASimplifiedInvoiceLineItem, ZATCASimplifiedInvoiceProps, ZATCAInvoiceTypes, ZATCAPaymentMethods};
export class ZATCASimplifiedTaxInvoice {

private invoice_xml: XMLDocument;
Expand All @@ -29,8 +34,6 @@ export class ZATCASimplifiedTaxInvoice {
}

private constructLineItemTotals = (line_item: ZATCASimplifiedInvoiceLineItem) => {

// TODO: decimal fixing according to ZATCA

let line_item_total_discounts = 0;
let line_item_total_taxes = 0;
Expand Down Expand Up @@ -121,32 +124,25 @@ export class ZATCASimplifiedTaxInvoice {

return {
line_item_xml: {
// .. TODO
"cbc:ID": line_item.id,
// .. TODO
"cbc:InvoicedQuantity": {
"@_unitCode": "PCE",
"#text": line_item.quantity
},
// .. TODO
"cbc:LineExtensionAmount": {
"@_currencyID": "SAR",
"#text": line_item_total_tax_exclusive
},
// .. TODO
"cac:TaxTotal": cacTaxTotal,
// .. TODO
"cac:Item": {
"cbc:Name": line_item.name,
"cac:ClassifiedTaxCategory": cacClassifiedTaxCategories
},
// .. TODO
"cac:Price": {
"cbc:PriceAmount": {
"@_currencyID": "SAR",
"#text": line_item.tax_exclusive_price
},
// .. TODO
"cac:AllowanceCharge": cacAllowanceCharges
}
},
Expand All @@ -161,7 +157,6 @@ export class ZATCASimplifiedTaxInvoice {

private constructLegalMonetaryTotal = (tax_exclusive_subtotal: number, taxes_total: number) => {

// TODO: amount decimals according to ZATCA
return {
"cbc:LineExtensionAmount": {
"@_currencyID": "SAR",
Expand Down Expand Up @@ -276,7 +271,7 @@ export class ZATCASimplifiedTaxInvoice {


if(props.cancelation) {
// Invoice canceled. Make it a credit/debit note
// Invoice canceled. Tunred into credit/debit note. Must have PaymentMeans
// BR-KSA-17
this.invoice_xml.set("Invoice/cac:PaymentMeans", false, {
"cbc:PaymentMeansCode": props.cancelation.payment_method,
Expand Down
1 change: 0 additions & 1 deletion src/zatca/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import axios from "axios";
import { cleanUpCertificateString } from "../signing";
import { ZATCASimplifiedTaxInvoice } from "../ZATCASimplifiedTaxInvoice";


const settings = {
Expand Down
11 changes: 6 additions & 5 deletions src/zatca/egs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const generateSecp256k1KeyPair = async (): Promise<string> => {

// Generate a signed ecdsaWithSHA256 CSR
// 2.2.2 Profile specification of the Cryptographic Stamp identifiers. & CSR field contents / RDNs.
const generateCSR = async (egs_info: EGSUnitInfo, production: boolean): Promise<string> => {
const generateCSR = async (egs_info: EGSUnitInfo, production: boolean, solution_name: string): Promise<string> => {
if (!egs_info.private_key) throw new Error("EGS has no private key");

// This creates a temporary private file, and csr config file to pass to OpenSSL in order to create and sign the CSR.
Expand All @@ -90,7 +90,7 @@ const generateCSR = async (egs_info: EGSUnitInfo, production: boolean): Promise<
fs.writeFileSync(csr_config_file, defaultCSRConfig({
egs_model: egs_info.model,
egs_serial_number: egs_info.uuid,
solution_name: "TODONAME",
solution_name: solution_name,
vat_number: egs_info.VAT_number,
branch_location: `${egs_info.location.building} ${egs_info.location.street}, ${egs_info.location.city}`,
branch_industry: egs_info.branch_industry,
Expand Down Expand Up @@ -151,15 +151,16 @@ export class EGS {
* Generates a new secp256k1 Public/Private key pair for the EGS.
* Also generates and signs a new CSR.
* `Note`: This functions uses OpenSSL thus requires it to be installed on whatever system the package is running in.
* @param Boolean Production CSR or Compliance CSR
* @param production Boolean CSR or Compliance CSR
* @param solution_name String name of solution generating certs.
* @returns Promise void on success, throws error on fail.
*/
async generateNewKeysAndCSR(production: boolean): Promise<any> {
async generateNewKeysAndCSR(production: boolean, solution_name: string): Promise<any> {
try {
const new_private_key = await generateSecp256k1KeyPair();
this.egs_info.private_key = new_private_key;

const new_csr = await generateCSR(this.egs_info, production);
const new_csr = await generateCSR(this.egs_info, production, solution_name);
this.egs_info.csr = new_csr;
} catch (error) {
throw error;
Expand Down
4 changes: 2 additions & 2 deletions src/zatca/templates/simplified_tax_invoice_template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export interface ZATCASimplifiedInvoiceLineItem {
export interface ZATCASimplifiedInvoicCancelation{
canceled_invoice_number: number,
payment_method: ZATCAPaymentMethods,
cancelation_type: ZATCAInvoiceTypes,
reason: string
}

Expand All @@ -126,8 +127,7 @@ export interface ZATCASimplifiedInvoiceProps {
export default function populate(props: ZATCASimplifiedInvoiceProps): string {
let populated_template = template;

// TODO Debit or Credit selection
populated_template = populated_template.replace("SET_INVOICE_TYPE", props.cancelation ? ZATCAInvoiceTypes.CREDIT_NOTE : ZATCAInvoiceTypes.INVOICE);
populated_template = populated_template.replace("SET_INVOICE_TYPE", props.cancelation ? props.cancelation.cancelation_type : ZATCAInvoiceTypes.INVOICE);
// if canceled (BR-KSA-56) set reference number to canceled invoice
if(props.cancelation) {
populated_template = populated_template.replace("SET_BILLING_REFERENCE", defaultBillingReference(props.cancelation.canceled_invoice_number));
Expand Down