Skip to content
This repository was archived by the owner on May 6, 2022. It is now read-only.

Commit 412d0c3

Browse files
authored
Merge pull request #98 from Tolfix/dev
v2.9
2 parents 64a31a6 + 86bef8d commit 412d0c3

33 files changed

+231
-65
lines changed

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cpg-api",
3-
"version": "v2.8",
3+
"version": "v2.9",
44
"description": "Central Payment Gateway",
55
"main": "./build/Main.js",
66
"dependencies": {
@@ -26,6 +26,7 @@
2626
"date-and-time": "^1.0.1",
2727
"dotenv": "^10.0.0",
2828
"easyinvoice": "^2.0.8",
29+
"exchange-rates-api": "^1.1.0",
2930
"express": "^4.17.1",
3031
"express-fileupload": "^1.2.1",
3132
"express-rate-limit": "^6.2.0",
@@ -75,6 +76,7 @@
7576
"devDependencies": {
7677
"@types/cors": "^2.8.12",
7778
"@types/cron": "^1.7.3",
79+
"@types/exchange-rates-api": "^1.0.0",
7880
"@types/express": "^4.17.13",
7981
"@types/express-fileupload": "^1.1.7",
8082
"@types/express-session": "^1.17.4",

src/Cache/reCache.ts

+19-9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import ConfigModel from "../Database/Models/Configs.model";
1717
import { CacheConfig } from "./Configs.cache";
1818
import InvoiceModel from "../Database/Models/Invoices.model";
1919
import { CacheInvoice } from "./Invoices.cache";
20+
import { Company_Currency } from "../Config";
21+
import { TPaymentCurrency } from "../Lib/Currencies";
2022

2123
/**
2224
* @deprecated
@@ -51,27 +53,28 @@ export async function reCache_Admin()
5153
});
5254
}
5355

54-
/**
55-
* @deprecated
56-
*/
5756
export async function reCache_Customers()
5857
{
5958
Logger.info(`Starting caching on customers..`);
6059
return new Promise(async (resolve) =>
6160
{
6261
const customer = await CustomerModel.find();
63-
for (const c of customer)
62+
for await(const c of customer)
6463
{
64+
// Check if customer has currency
65+
if(!c.currency)
66+
{
67+
const companyCurrency = await Company_Currency();
68+
c.currency = companyCurrency.toLocaleUpperCase() as TPaymentCurrency;
69+
await c.save();
70+
}
6571
Logger.cache(`Caching customer ${c.uid}`);
6672
CacheCustomer.set(c.uid, c);
6773
}
6874
return resolve(true);
6975
});
7076
}
7177

72-
/**
73-
* @deprecated
74-
*/
7578
export async function reCache_Product()
7679
{
7780
Logger.info(`Starting caching on products..`);
@@ -80,6 +83,13 @@ export async function reCache_Product()
8083
const product = await ProductModel.find();
8184
for (const c of product)
8285
{
86+
// Check if product has currency
87+
if(!c.currency)
88+
{
89+
const companyCurrency = await Company_Currency();
90+
c.currency = companyCurrency.toLocaleUpperCase() as TPaymentCurrency;
91+
await c.save();
92+
}
8393
Logger.cache(`Caching product ${c.uid}`);
8494
CacheProduct.set(c.uid, c);
8595
}
@@ -201,8 +211,8 @@ export async function reCache()
201211
await reCache_Configs();
202212
// await reCache_Categories();
203213
await reCache_Admin();
204-
// await reCache_Customers();
205-
// await reCache_Product();
214+
await reCache_Customers();
215+
await reCache_Product();
206216
// await reCache_Transactions();
207217
// await reCache_Orders();
208218
await reCache_Images();

src/Database/Models/Products.model.ts

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Logger from "../../Lib/Logger";
66
import GetText from "../../Translation/GetText";
77
import { A_RecurringMethod } from "../../Types/PaymentMethod";
88
import { A_PaymentTypes } from "../../Types/PaymentTypes";
9+
import { currencyCodes } from "../../Lib/Currencies";
910

1011

1112
const ProductSchema = new Schema
@@ -64,6 +65,12 @@ const ProductSchema = new Schema
6465
required: true,
6566
},
6667

68+
currency: {
69+
type: String,
70+
enum: currencyCodes,
71+
required: true,
72+
},
73+
6774
setup_fee: {
6875
type: Number,
6976
required: true,

src/Database/Postgres/Models/Customer/Customer.model.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Sequelize, {
44
import { Billing, ICustomer, Personal } from "@interface/Customer.interface";
55
import { IImage } from "@interface/Images.interface";
66
import { postgres } from "../../../Postgres";
7+
import { TPaymentCurrency } from "../../../../Lib/Currencies";
78

89
export class CustomerModel extends Model<ICustomer, Optional<ICustomer, "id">> implements ICustomer
910
{
@@ -27,7 +28,7 @@ export class CustomerModel extends Model<ICustomer, Optional<ICustomer, "id">> i
2728
};
2829
declare password: string;
2930
declare profile_picture: IImage["id"] | null;
30-
declare currency: string | ICustomer["currency"];
31+
declare currency: TPaymentCurrency;
3132
declare extra: {
3233
[key: string]: any;
3334
};

src/Email/Templates/Invoices/Invoice.template.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default async (invoice: IInvoice & IInvoiceMethods, customer: ICustomer)
2525
<strong>Tax due:</strong> ${invoice.tax_rate}%
2626
</p>
2727
<p>
28-
<strong>Amount due:</strong> ${invoice.getTotalAmount({ tax: false, currency: true, symbol: true })}
28+
<strong>Amount due:</strong> ${invoice.getTotalAmount({ tax: false, currency: false, symbol: false }).toFixed(2)} ${(invoice.currency)}
2929
</p>
3030
<p>
3131
<strong>Due date:</strong> ${invoice.dates.due_date}
@@ -69,7 +69,7 @@ export default async (invoice: IInvoice & IInvoiceMethods, customer: ICustomer)
6969
<strong>
7070
Total:
7171
</strong>
72-
${invoice.getTotalAmount({ tax: true, currency: true, symbol: true })} (${invoice.tax_rate}%)
72+
${invoice.getTotalAmount({ tax: true, currency: false, symbol: false }).toFixed(2)} ${invoice.currency} (${invoice.tax_rate}%)
7373
</p>
7474
<p>
7575
<strong>

src/Email/Templates/Invoices/LateInvoice.Template.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default async (invoice: IInvoice & IInvoiceMethods, customer: ICustomer)
2525
<strong>Tax due:</strong> ${invoice.tax_rate}%
2626
</p>
2727
<p>
28-
<strong>Amount due:</strong> ${invoice.getTotalAmount({ tax: false, currency: true, symbol: true })}
28+
<strong>Amount due:</strong> ${invoice.getTotalAmount({ tax: false, currency: false, symbol: false }).toFixed(2)} ${(invoice.currency)}
2929
</p>
3030
<p>
3131
<strong>Due date:</strong> ${invoice.dates.due_date}
@@ -69,7 +69,7 @@ export default async (invoice: IInvoice & IInvoiceMethods, customer: ICustomer)
6969
<strong>
7070
Total:
7171
</strong>
72-
${invoice.getTotalAmount({ tax: true, currency: true, symbol: true })} (${invoice.tax_rate}%)
72+
${invoice.getTotalAmount({ tax: true, currency: false, symbol: false }).toFixed(2)} ${invoice.currency} (${invoice.tax_rate}%)
7373
</p>
7474
<p>
7575
<strong>

src/Email/Templates/Methods/InvoiceItems.print.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default async function printInvoiceItemsTable(invoice: IInvoice)
1818
<tr>
1919
<td>${item.notes}</td>
2020
<td>${item.quantity}</td>
21-
<td>${item.amount} ${GetCurrencySymbol(invoice.currency)}</td>
21+
<td>${item.amount.toFixed(2)} ${GetCurrencySymbol(invoice.currency)}</td>
2222
</tr>
2323
`))).join('')}
2424
</tbody>

src/Email/Templates/Methods/OrderProducts.print.ts

+14-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import { IOrder } from "@interface/Orders.interface";
22
import { stripIndents } from "common-tags";
33
import ConfigurableOptionsModel from "../../../Database/Models/ConfigurableOptions.model";
44
import getProductById from "../../../Lib/Products/getProductById";
5-
import { GetCurrencySymbol } from "../../../Lib/Currencies";
5+
import { convertCurrency, GetCurrencySymbol } from "../../../Lib/Currencies";
66
import GetTableStyle from "../CSS/GetTableStyle";
7+
import { ICustomer } from "@interface/Customer.interface";
78

8-
export default async function printOrderProductTable(order: IOrder)
9+
export default async function printOrderProductTable(order: IOrder, customer: ICustomer)
910
{
1011
return `
1112
<table style="${GetTableStyle}">
@@ -20,6 +21,10 @@ export default async function printOrderProductTable(order: IOrder)
2021
${(await Promise.all(order.products.map(async (product) =>
2122
{
2223
const p = await getProductById(product.product_id);
24+
if(!p) return 0;
25+
26+
if(p?.currency.toUpperCase() !== customer.currency.toUpperCase())
27+
p.price = await convertCurrency(p?.price, p?.currency, order.currency);
2328
const p_c = [];
2429
for await(const conf of product?.configurable_options ?? [])
2530
{
@@ -28,17 +33,22 @@ export default async function printOrderProductTable(order: IOrder)
2833
});
2934
3035
if(c)
36+
{
37+
if(p?.currency.toUpperCase() !== customer.currency.toUpperCase())
38+
// Convert to customer currency
39+
c.options[conf.option_index].price = await convertCurrency(c.options[conf.option_index].price, p?.currency, customer.currency);
3140
p_c.push({
3241
price: c.options[conf.option_index].price,
3342
name: c.options[conf.option_index].name,
3443
});
44+
}
3545
}
3646
3747
let result = stripIndents`
3848
<tr>
3949
<td>${p?.name}</td>
4050
<td>${product.quantity}</td>
41-
<td>${p?.price} ${GetCurrencySymbol(order.currency)}</td>
51+
<td>${p?.price.toFixed(2)} ${GetCurrencySymbol(order.currency)}</td>
4252
</tr>`;
4353
4454
if(p_c.length > 0)
@@ -49,7 +59,7 @@ export default async function printOrderProductTable(order: IOrder)
4959
<tr>
5060
<td>+ ${p?.name} - ${c?.name}</td>
5161
<td>1</td>
52-
<td>${c?.price} ${GetCurrencySymbol(order.currency)}</td>
62+
<td>${c?.price.toFixed(2)} ${GetCurrencySymbol(order.currency)}</td>
5363
</tr>`
5464
}
5565
}

src/Email/Templates/Orders/NewOrderCreated.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import getFullName from "../../../Lib/Customers/getFullName";
77
import getProductById from "../../../Lib/Products/getProductById";
88
import UseStyles from "../General/UseStyles";
99
import printOrderProductTable from "../Methods/OrderProducts.print";
10+
import { convertCurrency } from "../../../Lib/Currencies";
1011

1112
export default async (order: IOrder, customer: ICustomer) => await UseStyles(stripIndents`
1213
<div>
@@ -18,7 +19,7 @@ export default async (order: IOrder, customer: ICustomer) => await UseStyles(str
1819
<strong>Order number:</strong> ${order.id}
1920
</p>
2021
21-
${await printOrderProductTable(order)}
22+
${await printOrderProductTable(order, customer)}
2223
2324
<p>
2425
<strong>
@@ -27,6 +28,10 @@ export default async (order: IOrder, customer: ICustomer) => await UseStyles(str
2728
${(await Promise.all(order.products.map(async (product) =>
2829
{
2930
const p = await getProductById(product.product_id as any);
31+
if(!p) return 0;
32+
33+
if(p?.currency.toUpperCase() !== customer.currency.toUpperCase())
34+
p.price = await convertCurrency(p?.price, p?.currency, order.currency);
3035
// check if configurable options are added
3136
const p_c = [];
3237
for await(const conf of product?.configurable_options ?? [])
@@ -35,10 +40,15 @@ export default async (order: IOrder, customer: ICustomer) => await UseStyles(str
3540
id: conf.id,
3641
});
3742
if(c)
43+
{
44+
// Check if same currency
45+
if(p?.currency.toUpperCase() !== customer.currency.toUpperCase())
46+
// Convert to customer currency
47+
c.options[conf.option_index].price = await convertCurrency(c.options[conf.option_index].price, p?.currency, customer.currency);
3848
p_c.push(c.options[conf.option_index].price);
49+
}
3950
}
4051
41-
4252
if (!p)
4353
return 0;
4454
@@ -47,7 +57,7 @@ export default async (order: IOrder, customer: ICustomer) => await UseStyles(str
4757
total += p_c.reduce((a, b) => a + b);
4858
4959
return total;
50-
}))).reduce((acc, cur) => acc + cur, 0)} ${(order.currency).toLocaleUpperCase()}
60+
}))).reduce((acc, cur) => acc + cur, 0).toFixed(2)} ${(order.currency).toLocaleUpperCase()}
5161
</p>
5262
5363
${CPG_Customer_Panel_Domain ? `

src/Interfaces/Customer.interface.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { TPaymentCurrency } from "../Lib/Currencies";
2-
import { ICompanyConfig } from "./Admin/Configs.interface";
32
import { IImage } from "./Images.interface";
43

54
/**
@@ -28,7 +27,7 @@ export interface ICustomer
2827
password: string;
2928
createdAt: Date;
3029
profile_picture: IImage["id"] | null;
31-
currency: TPaymentCurrency | ICompanyConfig["currency"];
30+
currency: TPaymentCurrency;
3231
extra: {
3332
[key: string]: any;
3433
};

src/Interfaces/Products.interface.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { TPaymentCurrency } from "../Lib/Currencies";
12
import { TRecurringMethod } from "../Types/PaymentMethod";
23
import { TPaymentTypes } from "../Types/PaymentTypes";
34
import { ICategory } from "./Categories.interface";
@@ -30,6 +31,7 @@ export interface IProduct
3031
special: boolean;
3132
payment_type: Partial<TPaymentTypes>;
3233
price: number;
34+
currency: TPaymentCurrency;
3335
setup_fee: number;
3436
image?: IImage["id"][];
3537
tax_rate: number;

src/Lib/Currencies.ts

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { exchangeRates } from "exchange-rates-api";
12
// Every currency's code is in ISO 4217
23
export type TPaymentCurrency =
34
"AED"
@@ -194,4 +195,29 @@ export function GetCurrencySymbol(code: TPaymentCurrency)
194195
maximumFractionDigits: 0
195196
}
196197
).replace(/\d/g, '').trim()
197-
}
198+
}
199+
200+
export const convertCurrency = async (
201+
amount: number,
202+
fromCurrency: TPaymentCurrency,
203+
toCurrency: TPaymentCurrency,
204+
date = 'latest'
205+
) =>
206+
{
207+
const instance = exchangeRates();
208+
// @ts-ignore
209+
instance.setApiBaseUrl('https://api.exchangerate.host');
210+
211+
if (date === 'latest')
212+
instance.latest();
213+
else
214+
// @ts-ignore
215+
instance.at(date);
216+
217+
return instance
218+
.base(fromCurrency)
219+
.symbols(toCurrency)
220+
.fetch()
221+
// @ts-ignore
222+
.then((rate) => rate * amount);
223+
};

src/Lib/Invoices/CreatePDFInvoice.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export default function createPDFInvoice(invoice: IInvoice): Promise<string>
133133
"quantity": item.quantity,
134134
"description": item.notes,
135135
"tax-rate": invoice.tax_rate,
136-
"price": item.amount
136+
"price": item.amount.toFixed(2)
137137
}
138138
}),
139139
"bottomNotice": `

0 commit comments

Comments
 (0)