From 8e9343f4a80c8b130289d3c66c5c0d6750d64309 Mon Sep 17 00:00:00 2001 From: Tolfx Date: Thu, 10 Mar 2022 22:33:58 +0100 Subject: [PATCH 01/14] feat: :art: Added more attributes --- src/Database/Models/Quotes.model.ts | 24 +++++++++++++++++++++++- src/Interfaces/Quotes.interface.ts | 9 +++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Database/Models/Quotes.model.ts b/src/Database/Models/Quotes.model.ts index 8c944dc..e578a5d 100644 --- a/src/Database/Models/Quotes.model.ts +++ b/src/Database/Models/Quotes.model.ts @@ -5,6 +5,7 @@ import { IQuotes } from "@interface/Quotes.interface"; import Logger from "../../Lib/Logger"; import GetText from "../../Translation/GetText"; import { A_CC_Payments } from "../../Types/PaymentMethod"; +import { currencyCodes } from "../../Lib/Currencies"; const QuotesSchema = new Schema ( @@ -27,7 +28,6 @@ const QuotesSchema = new Schema type: [ { name: String, - tax_rate: Number, price: Number, quantity: Number, } @@ -50,6 +50,28 @@ const QuotesSchema = new Schema default: "", }, + currency: { + type: String, + required: true, + enum: currencyCodes, + default: "EUR", + }, + + tax_rate: { + type: Number, + default: 0, + }, + + accepted: { + type: Boolean, + default: false + }, + + declined: { + type: Boolean, + default: false + }, + payment_method: { type: String, enum: [...A_CC_Payments], diff --git a/src/Interfaces/Quotes.interface.ts b/src/Interfaces/Quotes.interface.ts index eabccc9..ac09fb7 100644 --- a/src/Interfaces/Quotes.interface.ts +++ b/src/Interfaces/Quotes.interface.ts @@ -1,3 +1,5 @@ +import { TPaymentCurrency } from "../Lib/Currencies"; +import { ICustomer } from "./Customer.interface"; import { IInvoice } from "./Invoice.interface"; import { IPayments } from "./Payments.interface"; import { IPromotionsCodes } from "./PromotionsCodes.interface"; @@ -6,7 +8,7 @@ export interface IQuotes { uid: `QUO_${string}`; id: number; - customer_uid: string; + customer_uid: ICustomer["uid"]; items: IQuoteItem[]; promotion_codes: IPromotionsCodes["id"][] | []; due_date: string; @@ -14,13 +16,16 @@ export interface IQuotes payment_method: keyof IPayments; notified: boolean; created_invoice: boolean; + tax_rate: number; + currency: TPaymentCurrency; + accepted: boolean; + declined: boolean; invoice_uid?: IInvoice["uid"] | IInvoice["id"]; } export interface IQuoteItem { name: string; - tax_rate: number; price: number; quantity: number; } \ No newline at end of file From 31af9ccae2445198d260ef624bc71bd1b9063d9f Mon Sep 17 00:00:00 2001 From: Tolfx Date: Thu, 10 Mar 2022 22:34:21 +0100 Subject: [PATCH 02/14] fix: :bug: Fixed to new attributes --- src/Lib/Quotes/CreateQuotePdf.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Lib/Quotes/CreateQuotePdf.ts b/src/Lib/Quotes/CreateQuotePdf.ts index 51d36db..b7c23fe 100644 --- a/src/Lib/Quotes/CreateQuotePdf.ts +++ b/src/Lib/Quotes/CreateQuotePdf.ts @@ -71,7 +71,7 @@ export default function createQuotePdf(quote: IQuotes): Promise return { "quantity": item.quantity, "description": item.name, - "tax-rate": item.tax_rate, + "tax-rate": quote.tax_rate, "price": item.price } }), From 977fcb448c281a16d10aa4d09b0773bc88257ca1 Mon Sep 17 00:00:00 2001 From: Tolfx Date: Thu, 10 Mar 2022 22:34:44 +0100 Subject: [PATCH 03/14] feat: :art: Quote => Invoice method --- src/Lib/Quotes/QuoteToInvoice.ts | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/Lib/Quotes/QuoteToInvoice.ts diff --git a/src/Lib/Quotes/QuoteToInvoice.ts b/src/Lib/Quotes/QuoteToInvoice.ts new file mode 100644 index 0000000..452364c --- /dev/null +++ b/src/Lib/Quotes/QuoteToInvoice.ts @@ -0,0 +1,35 @@ +import { IQuotes } from "@interface/Quotes.interface"; +import InvoiceModel from "../../Database/Models/Invoices.model"; +import { idInvoice } from "../Generator"; +import dateFormat from "date-and-time"; +import mainEvent from "../../Events/Main.event"; + +export default async (quote: IQuotes) => +{ + // Converts quote to invoice + const invoice = await (new InvoiceModel({ + uid: idInvoice(), + customer_uid: quote.customer_uid, + items: quote.items.map(item => ({ + notes: item.name, + amount: item.price, + quantity: item.quantity, + })), + dates: { + invoice_date: dateFormat.format(new Date(), "YYYY-MM-DD"), + due_date: quote.due_date, + }, + amount: quote.items.reduce((acc, item) => acc + item.price * item.quantity, 0), + currency: quote.currency, + tax_rate: quote.tax_rate, + notified: false, + transactions: [], + paid: false, + notes: quote.memo, + payment_method: quote.payment_method, + }).save()); + + mainEvent.emit("invoice_created", invoice); + + return invoice; +} \ No newline at end of file From 8511532940dd9de77065f8a3d9a914113db007b1 Mon Sep 17 00:00:00 2001 From: Tolfx Date: Thu, 10 Mar 2022 22:35:03 +0100 Subject: [PATCH 04/14] feat: :art: Quote table for email --- .../Templates/Methods/QuotesItems.print.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/Email/Templates/Methods/QuotesItems.print.ts diff --git a/src/Email/Templates/Methods/QuotesItems.print.ts b/src/Email/Templates/Methods/QuotesItems.print.ts new file mode 100644 index 0000000..329429e --- /dev/null +++ b/src/Email/Templates/Methods/QuotesItems.print.ts @@ -0,0 +1,27 @@ +import { IQuotes } from "@interface/Quotes.interface"; +import { GetCurrencySymbol } from "../../../Lib/Currencies"; +import GetTableStyle from "../CSS/GetTableStyle"; + +export default async function printQuotesItemsTable(quote: IQuotes) +{ + return ` + + + + + + + + + + ${(await Promise.all(quote.items.map(async item => ` + + + + + + `))).join('')} + +
ProductQuantityPrice
${item.name}${item.quantity}${item.price} ${GetCurrencySymbol(quote.currency)}
+ ` +} \ No newline at end of file From a0670b68a5abe3210ed205b8a02be518225ead3c Mon Sep 17 00:00:00 2001 From: Tolfx Date: Thu, 10 Mar 2022 22:35:15 +0100 Subject: [PATCH 05/14] feat: :art: Quote email template --- .../Templates/Quotes/Quote.create.template.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/Email/Templates/Quotes/Quote.create.template.ts diff --git a/src/Email/Templates/Quotes/Quote.create.template.ts b/src/Email/Templates/Quotes/Quote.create.template.ts new file mode 100644 index 0000000..e924c79 --- /dev/null +++ b/src/Email/Templates/Quotes/Quote.create.template.ts @@ -0,0 +1,39 @@ +import { stripIndents } from "common-tags"; +import { Company_Name, CPG_Customer_Panel_Domain } from "../../../Config"; +import { ICustomer } from "@interface/Customer.interface"; +import getFullName from "../../../Lib/Customers/getFullName"; +import UseStyles from "../General/UseStyles"; +import { IQuotes } from "@interface/Quotes.interface"; +import printQuotesItemsTable from "../Methods/QuotesItems.print"; + +export default async (quote: IQuotes, customer: ICustomer) => await UseStyles(stripIndents` +
+

Hello ${getFullName(customer)}.

+

+ This is a notice that ${await Company_Name()} has sent a quote to you. +

+

+ Memo: ${quote.memo} +

+

+ Due Date: ${quote.due_date} +

+

+ Payment Method: ${quote.payment_method} +

+ + ${await printQuotesItemsTable(quote)} + +

+ + Total: + + ${quote.items.reduce((total, item) => total + (item.price * item.quantity), 0) + ((quote.tax_rate/100) * quote.items.reduce((total, item) => total + (item.price * item.quantity), 0))} +

+ ${CPG_Customer_Panel_Domain ? ` +

+ View quote to accept or decline. +

+ ` : ''} +
+`); \ No newline at end of file From ca504ffaabdfc1c4147741391b42f3a5f72b17d7 Mon Sep 17 00:00:00 2001 From: Tolfx Date: Thu, 10 Mar 2022 22:35:35 +0100 Subject: [PATCH 06/14] feat: :sparkles: Modified routes for `Quotes` --- src/Server/Routes/v2/Quotes/Quotes.config.ts | 65 +++++++++++++++++-- .../Routes/v2/Quotes/Quotes.controller.ts | 19 +----- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/src/Server/Routes/v2/Quotes/Quotes.config.ts b/src/Server/Routes/v2/Quotes/Quotes.config.ts index 209bf15..8defe8f 100644 --- a/src/Server/Routes/v2/Quotes/Quotes.config.ts +++ b/src/Server/Routes/v2/Quotes/Quotes.config.ts @@ -3,9 +3,12 @@ import CustomerModel from "../../../../Database/Models/Customers/Customer.model" import QuotesModel from "../../../../Database/Models/Quotes.model"; import AW from "../../../../Lib/AW"; import createQuotePdf from "../../../../Lib/Quotes/CreateQuotePdf"; -import { APIError } from "../../../../Lib/Response"; +import QuoteToInvoice from "../../../../Lib/Quotes/QuoteToInvoice"; +import { APIError, APISuccess } from "../../../../Lib/Response"; import EnsureAdmin from "../../../../Middlewares/EnsureAdmin"; +import EnsureAuth from "../../../../Middlewares/EnsureAuth"; import QuotesController from "./Quotes.controller"; +import { sendInvoiceEmail } from "../../../../Lib/Invoices/SendEmail"; export = class QuotesRouter { @@ -27,11 +30,13 @@ export = class QuotesRouter QuotesController.getByUid ]); - this.router.get("/:uid/view", async (req, res) => + this.router.get("/:uid/view", EnsureAuth(), async (req, res) => { - // const uid = req.params.uid; - const [quote, e_quote] = await AW(await QuotesModel.findOne({ uid: uid })); + const [quote, e_quote] = await AW(await QuotesModel.findOne({ $or: [ + { uid: uid }, + { id: uid } + ] })); if(e_quote || !quote) return APIError(`Failed to fetch quote with uid ${uid}`)(res); @@ -53,6 +58,58 @@ export = class QuotesRouter res.end(result, "base64"); }); + this.router.post("/:uid/accept", EnsureAuth(), async (req, res) => + { + const uid = req.params.uid; + const [quote, e_quote] = await AW(await QuotesModel.findOne({ $or: [ + { uid: uid }, + { id: uid } + ] })); + + if(e_quote || !quote) + return APIError(`Failed to fetch quote with uid ${uid}`)(res); + + const customer = await CustomerModel.findOne({ $or: [ + { id: quote.customer_uid }, + { uid: quote.customer_uid as any } + ] }); + + if(!customer) + return APIError(`Failed to fetch customer with uid ${quote.customer_uid}`)(res); + + quote.accepted = true; + + await quote.save(); + + // Convert quote to invoice + const invoice = await QuoteToInvoice(quote); + if(!invoice) + return APIError("Failed to convert quote to invoice")(res); + + // Send email to customer, no need to await since if it fails it will run cron either way + sendInvoiceEmail(invoice, customer); + + return APISuccess(invoice)(res); + }); + + this.router.post("/:uid/decline", EnsureAuth(), async (req, res) => + { + const uid = req.params.uid; + const [quote, e_quote] = await AW(await QuotesModel.findOne({ $or: [ + { uid: uid }, + { id: uid } + ] })); + + if(e_quote || !quote) + return APIError(`Failed to fetch quote with uid ${uid}`)(res); + + quote.declined = true; + + await quote.save(); + + return APISuccess(`Declined quote offer.`)(res); + }); + this.router.post("/", [ EnsureAdmin(), QuotesController.insert diff --git a/src/Server/Routes/v2/Quotes/Quotes.controller.ts b/src/Server/Routes/v2/Quotes/Quotes.controller.ts index 5c1e1e0..bc39f81 100644 --- a/src/Server/Routes/v2/Quotes/Quotes.controller.ts +++ b/src/Server/Routes/v2/Quotes/Quotes.controller.ts @@ -1,14 +1,14 @@ import { Request, Response } from "express"; -import { Company_Name, Full_Domain } from "../../../../Config"; +import { Company_Name } from "../../../../Config"; import CustomerModel from "../../../../Database/Models/Customers/Customer.model"; import QuotesModel from "../../../../Database/Models/Quotes.model"; import { SendEmail } from "../../../../Email/Send"; import mainEvent from "../../../../Events/Main.event"; import { IQuotes } from "@interface/Quotes.interface"; -import getFullName from "../../../../Lib/Customers/getFullName"; import { idQuotes } from "../../../../Lib/Generator"; import { APISuccess } from "../../../../Lib/Response"; import BaseModelAPI from "../../../../Models/BaseModelAPI"; +import QuoteCreateTemplate from "../../../../Email/Templates/Quotes/Quote.create.template"; const API = new BaseModelAPI(idQuotes, QuotesModel); @@ -32,20 +32,7 @@ function insert(req: Request, res: Response) // Send email to customer. await SendEmail(Customer.personal.email, `Quote from ${await Company_Name() === "" ? "CPG" : await Company_Name()}`, { isHTML: true, - body: ` -

Quote

-

- Hello ${getFullName(Customer)}! -

-

- You have a new quote. -

-

- - Click here to view the quote. - -

- ` + body: await QuoteCreateTemplate(result, Customer) }); } } From 2c9d5a781e01fb4e5169cc457e7f0ff03b28f9f6 Mon Sep 17 00:00:00 2001 From: Tolfx Date: Thu, 10 Mar 2022 23:07:15 +0100 Subject: [PATCH 07/14] fix: :bug: Fixed path to customer panel --- src/Email/Templates/Invoices/LateInvoice.Template.ts | 2 +- src/Email/Templates/Orders/NewOrderCreated.ts | 2 +- src/Email/Templates/Payments/PaymentFailed.template.ts | 2 +- src/Email/Templates/Quotes/Quote.create.template.ts | 2 +- src/Email/Templates/Transaction/NewTransaction.template.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Email/Templates/Invoices/LateInvoice.Template.ts b/src/Email/Templates/Invoices/LateInvoice.Template.ts index 2566eca..e37764f 100644 --- a/src/Email/Templates/Invoices/LateInvoice.Template.ts +++ b/src/Email/Templates/Invoices/LateInvoice.Template.ts @@ -78,7 +78,7 @@ export default async (invoice: IInvoice & IInvoiceMethods, customer: ICustomer)

${CPG_Customer_Panel_Domain ? `

- View Invoice + View Invoice

` : ''} diff --git a/src/Email/Templates/Orders/NewOrderCreated.ts b/src/Email/Templates/Orders/NewOrderCreated.ts index 48c743c..91b26d2 100644 --- a/src/Email/Templates/Orders/NewOrderCreated.ts +++ b/src/Email/Templates/Orders/NewOrderCreated.ts @@ -52,7 +52,7 @@ export default async (order: IOrder, customer: ICustomer) => await UseStyles(str ${CPG_Customer_Panel_Domain ? `

- View Order + View Order

` : ''} diff --git a/src/Email/Templates/Payments/PaymentFailed.template.ts b/src/Email/Templates/Payments/PaymentFailed.template.ts index 6959d9e..5f16581 100644 --- a/src/Email/Templates/Payments/PaymentFailed.template.ts +++ b/src/Email/Templates/Payments/PaymentFailed.template.ts @@ -41,7 +41,7 @@ export = async (invoice: IInvoice, customer: ICustomer) => UseStyles(stripIndent ${CPG_Customer_Panel_Domain ? `

- View invoice + View invoice

` : ''} diff --git a/src/Email/Templates/Quotes/Quote.create.template.ts b/src/Email/Templates/Quotes/Quote.create.template.ts index e924c79..d443a43 100644 --- a/src/Email/Templates/Quotes/Quote.create.template.ts +++ b/src/Email/Templates/Quotes/Quote.create.template.ts @@ -32,7 +32,7 @@ export default async (quote: IQuotes, customer: ICustomer) => await UseStyles(st

${CPG_Customer_Panel_Domain ? `

- View quote to accept or decline. + View quote to accept or decline.

` : ''} diff --git a/src/Email/Templates/Transaction/NewTransaction.template.ts b/src/Email/Templates/Transaction/NewTransaction.template.ts index 663fec8..86a4d97 100644 --- a/src/Email/Templates/Transaction/NewTransaction.template.ts +++ b/src/Email/Templates/Transaction/NewTransaction.template.ts @@ -29,7 +29,7 @@ export = async (t: ITransactions, c: ICustomer, charged = false) => UseStyles(st

${CPG_Customer_Panel_Domain ? `

- View Transaction + View Transaction

` : ''} From 7864f9bd6b3a7536e620f6d867ed5b97cbf22ebd Mon Sep 17 00:00:00 2001 From: Tolfx Date: Thu, 10 Mar 2022 23:07:36 +0100 Subject: [PATCH 08/14] fix: :bug: Fixed await --- src/Email/Templates/Invoices/Invoice.template.ts | 2 +- src/Lib/Invoices/CreatePDFInvoice.ts | 2 +- src/Lib/Quotes/CreateQuotePdf.ts | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Email/Templates/Invoices/Invoice.template.ts b/src/Email/Templates/Invoices/Invoice.template.ts index 74181b2..36262e2 100644 --- a/src/Email/Templates/Invoices/Invoice.template.ts +++ b/src/Email/Templates/Invoices/Invoice.template.ts @@ -78,7 +78,7 @@ export default async (invoice: IInvoice & IInvoiceMethods, customer: ICustomer)

${CPG_Customer_Panel_Domain ? `

- View Invoice + View Invoice

` : ''} diff --git a/src/Lib/Invoices/CreatePDFInvoice.ts b/src/Lib/Invoices/CreatePDFInvoice.ts index 21381a6..b8f7dc4 100644 --- a/src/Lib/Invoices/CreatePDFInvoice.ts +++ b/src/Lib/Invoices/CreatePDFInvoice.ts @@ -147,7 +147,7 @@ export default function createPDFInvoice(invoice: IInvoice): Promise ) data["client"]["custom1"] = `
Innehar ${(await Company_Tax_Registered()) ? "" : "inte"} F-Skattsedel`; - if(Company_Logo_Url && PDF_Template_Url === "") + if(await Company_Logo_Url() !== "" && PDF_Template_Url === "") // @ts-ignore data["images"]["logo"] = await Company_Logo_Url(); diff --git a/src/Lib/Quotes/CreateQuotePdf.ts b/src/Lib/Quotes/CreateQuotePdf.ts index b7c23fe..126b32c 100644 --- a/src/Lib/Quotes/CreateQuotePdf.ts +++ b/src/Lib/Quotes/CreateQuotePdf.ts @@ -29,7 +29,7 @@ export default function createQuotePdf(quote: IQuotes): Promise }, "translate": { - "invoice": `Quote`, + "invoice": `Quote #${quote.id}`, "number": GetText().invoice.txt_Number, "date": GetText().invoice.txt_Date, "due-date": GetText().invoice.txt_DueDate, @@ -49,11 +49,11 @@ export default function createQuotePdf(quote: IQuotes): Promise "margin-bottom": 25, }, "sender": { - "company": Company_Name, - "address": Company_Address, - "zip": Company_Zip, - "city": Company_City, - "country": Company_Country, + "company": (await Company_Name()), + "address": await Company_Address(), + "zip": await Company_Zip(), + "city": await Company_City(), + "country": await Company_Country(), }, "client": { "company": Customer.billing.company ?? `${Customer.personal.first_name} ${Customer.personal.last_name}`, @@ -84,9 +84,9 @@ export default function createQuotePdf(quote: IQuotes): Promise data["client"]["custom1"] = `
Innehar ${await Company_Tax_Registered() ? "" : "inte"} F-Skattsedel`; - if(Company_Logo_Url && PDF_Template_Url === "") + if(await Company_Logo_Url() !== "" && PDF_Template_Url === "") // @ts-ignore - data["images"]["logo"] = Company_Logo_Url; + data["images"]["logo"] = await Company_Logo_Url(); if(PDF_Template_Url !== "") // @ts-ignore From d57cf2e3a0385e8ecc2a93fa2a5427b61d0d9d04 Mon Sep 17 00:00:00 2001 From: Tolfx Date: Thu, 10 Mar 2022 23:07:56 +0100 Subject: [PATCH 09/14] feat: :sparkles: Added `/my/quotes` --- .../Routes/v2/Customers/Customers.config.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/Server/Routes/v2/Customers/Customers.config.ts b/src/Server/Routes/v2/Customers/Customers.config.ts index 2a3eaf6..72a62c7 100644 --- a/src/Server/Routes/v2/Customers/Customers.config.ts +++ b/src/Server/Routes/v2/Customers/Customers.config.ts @@ -27,6 +27,7 @@ import { idImages } from "../../../../Lib/Generator"; import { CacheImages } from "../../../../Cache/Image.cache"; import ImageModel from "../../../../Database/Models/Images.model"; import Jimp from 'jimp'; +import QuotesModel from "../../../../Database/Models/Quotes.model"; export = class CustomerRouter { @@ -319,6 +320,62 @@ export = class CustomerRouter return APISuccess("Order cancelled.")(res); }); + this.router.get("/my/quotes", EnsureAuth(), async (req, res) => + { + const customer = await CustomerModel.findOne({ + // @ts-ignore + id: req.customer.id + }); + + if(!customer) + return APIError(`Unable to find customer`)(res); + + const data = await MongoFind(QuotesModel, req.query,{ + $or: [ + { customer_uid: customer.uid }, + { customer_uid: customer.id } + ] + }); + + res.setHeader("X-Total-Pages", data.totalPages); + res.setHeader("X-Total", data.totalCount); + + return APISuccess(data.data)(res); + }); + + this.router.get("/my/quotes/:id", EnsureAuth(), async (req, res) => + { + const quoteId = req.params.id; + + if(!quoteId) + return APIError(`Invalid invoice id`)(res); + + const customer = await CustomerModel.findOne({ + // @ts-ignore + id: req.customer.id + }); + + if(!customer) + return APIError(`Unable to find customer`)(res); + + const {data: [order]} = await MongoFind(QuotesModel, req.query,{ + $or: [ + { + customer_uid: customer.uid, + }, + { + customer_uid: customer.id, + }, + ], + id: quoteId, + }); + + if(!order) + return APIError(`Unable to find quote`)(res); + + return APISuccess(order)(res); + }); + this.router.get("/my/transactions", EnsureAuth(), async (req, res) => { const customer = await CustomerModel.findOne({ From a2383f28ad1a06273c13359c6ede5bbab373512a Mon Sep 17 00:00:00 2001 From: Tolfx Date: Thu, 10 Mar 2022 23:08:08 +0100 Subject: [PATCH 10/14] fix: :bug: Checking if already accepted --- src/Server/Routes/v2/Quotes/Quotes.config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Server/Routes/v2/Quotes/Quotes.config.ts b/src/Server/Routes/v2/Quotes/Quotes.config.ts index 8defe8f..fc3e2b9 100644 --- a/src/Server/Routes/v2/Quotes/Quotes.config.ts +++ b/src/Server/Routes/v2/Quotes/Quotes.config.ts @@ -77,6 +77,9 @@ export = class QuotesRouter if(!customer) return APIError(`Failed to fetch customer with uid ${quote.customer_uid}`)(res); + if(quote.accepted) + return APIError(`Quote already accepted`)(res); + quote.accepted = true; await quote.save(); From 7302956609ce3475438b5780aee3ffaa9987bb7f Mon Sep 17 00:00:00 2001 From: Tolfx Date: Sat, 12 Mar 2022 15:28:48 +0100 Subject: [PATCH 11/14] chore: :label: v2.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f37039..9bdfcd5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cpg-api", - "version": "v2.7", + "version": "v2.8", "description": "Central Payment Gateway", "main": "./build/Main.js", "dependencies": { From 89e26a707b18c398c61f8895d3a9f7060507fd36 Mon Sep 17 00:00:00 2001 From: Tolfx Date: Sat, 12 Mar 2022 15:29:10 +0100 Subject: [PATCH 12/14] fix: :bug: Possible fix to #94 --- src/Lib/Orders/newInvoice.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Lib/Orders/newInvoice.ts b/src/Lib/Orders/newInvoice.ts index f91ef08..15cb1da 100644 --- a/src/Lib/Orders/newInvoice.ts +++ b/src/Lib/Orders/newInvoice.ts @@ -90,7 +90,9 @@ export async function createInvoiceFromOrder(order: IOrder) customer_uid: Customer_Id, dates: { due_date: order.dates.next_recycle, - invoice_date: dateFormat.format(new Date(), "YYYY-MM-DD"), + // Possible fix to issue #94 + invoice_date: order.dates.last_recycle, + // invoice_date: dateFormat.format(new Date(), "YYYY-MM-DD"), }, amount: items.reduce((acc, item) => { From 62a743fada35ffcdcd5efc4111c38e289728ef09 Mon Sep 17 00:00:00 2001 From: Tolfx Date: Sat, 12 Mar 2022 15:43:40 +0100 Subject: [PATCH 13/14] fix: :art: Added `id` in transactions --- src/Database/Models/Transactions.model.ts | 8 ++++---- src/Interfaces/Transactions.interface.ts | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Database/Models/Transactions.model.ts b/src/Database/Models/Transactions.model.ts index a30d2b1..febf518 100644 --- a/src/Database/Models/Transactions.model.ts +++ b/src/Database/Models/Transactions.model.ts @@ -1,7 +1,7 @@ -import mongoose, { model, Schema } from "mongoose" +import mongoose, { Document, model, Schema } from "mongoose" import increment from "mongoose-auto-increment"; import { Default_Language, MongoDB_URI } from "../../Config"; -import { IDTransactions } from "@interface/Transactions.interface"; +import { ITransactions } from "@interface/Transactions.interface"; import Logger from "../../Lib/Logger"; import GetText from "../../Translation/GetText"; import { A_CC_Payments } from "../../Types/PaymentMethod"; @@ -58,7 +58,7 @@ const TransactionsSchema = new Schema ); // Log when a transaction is created -TransactionsSchema.post('save', function(doc: IDTransactions) +TransactionsSchema.post('save', function(doc: ITransactions & Document) { Logger.db(GetText(Default_Language).database.txt_Model_Created(doc.modelName, doc.uid)); // Logger.db(`Created transaction ${doc.uid}`); @@ -74,6 +74,6 @@ TransactionsSchema.plugin(increment.plugin, { incrementBy: 1 }); -const TransactionsModel = model("transactions", TransactionsSchema); +const TransactionsModel = model("transactions", TransactionsSchema); export default TransactionsModel; \ No newline at end of file diff --git a/src/Interfaces/Transactions.interface.ts b/src/Interfaces/Transactions.interface.ts index 03eaa04..d45776d 100644 --- a/src/Interfaces/Transactions.interface.ts +++ b/src/Interfaces/Transactions.interface.ts @@ -13,6 +13,7 @@ import { IInvoice } from "./Invoice.interface"; */ export interface ITransactions { + id: number; uid: `TRAN_${string}`; customer_uid: ICustomer["uid"]; invoice_uid: IInvoice["uid"]; @@ -21,6 +22,4 @@ export interface ITransactions amount: IInvoice["amount"]; currency: TPaymentCurrency; fees: number; -} - -export interface IDTransactions extends ITransactions, Document {} \ No newline at end of file +} \ No newline at end of file From 72295466315d52b740ca6eedd4fa7818624bf362 Mon Sep 17 00:00:00 2001 From: Tolfx Date: Sat, 12 Mar 2022 15:44:01 +0100 Subject: [PATCH 14/14] feat: :art: Added `Quote accepted` template email --- .../Quotes/Quote.accepted.template.ts | 23 +++++++++++++++++++ src/Server/Routes/v2/Quotes/Quotes.config.ts | 10 ++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/Email/Templates/Quotes/Quote.accepted.template.ts diff --git a/src/Email/Templates/Quotes/Quote.accepted.template.ts b/src/Email/Templates/Quotes/Quote.accepted.template.ts new file mode 100644 index 0000000..c2f4a36 --- /dev/null +++ b/src/Email/Templates/Quotes/Quote.accepted.template.ts @@ -0,0 +1,23 @@ +import { stripIndents } from "common-tags"; +import { CPG_Customer_Panel_Domain } from "../../../Config"; +import { ICustomer } from "@interface/Customer.interface"; +import getFullName from "../../../Lib/Customers/getFullName"; +import UseStyles from "../General/UseStyles"; +import { IQuotes } from "@interface/Quotes.interface"; + +export default async (quote: IQuotes, customer: ICustomer) => await UseStyles(stripIndents` +
+

Hello ${getFullName(customer)}.

+

+ This is a notice that quote #${quote.id} has been accepted. +

+

+ We will generate a invoice for you shortly. +

+ ${CPG_Customer_Panel_Domain ? ` +

+ View quote. +

+ ` : ''} +
+`); \ No newline at end of file diff --git a/src/Server/Routes/v2/Quotes/Quotes.config.ts b/src/Server/Routes/v2/Quotes/Quotes.config.ts index fc3e2b9..2ea4f45 100644 --- a/src/Server/Routes/v2/Quotes/Quotes.config.ts +++ b/src/Server/Routes/v2/Quotes/Quotes.config.ts @@ -9,6 +9,8 @@ import EnsureAdmin from "../../../../Middlewares/EnsureAdmin"; import EnsureAuth from "../../../../Middlewares/EnsureAuth"; import QuotesController from "./Quotes.controller"; import { sendInvoiceEmail } from "../../../../Lib/Invoices/SendEmail"; +import { sendEmail } from "../../../../Email/Send"; +import QuoteAcceptedTemplate from "../../../../Email/Templates/Quotes/Quote.accepted.template"; export = class QuotesRouter { @@ -84,6 +86,14 @@ export = class QuotesRouter await quote.save(); + await sendEmail({ + receiver: customer.personal.email, + subject: `Quote accepted | #${quote.id}`, + body: { + body: QuoteAcceptedTemplate(quote, customer), + } + }); + // Convert quote to invoice const invoice = await QuoteToInvoice(quote); if(!invoice)