diff --git a/package.json b/package.json index 145da48..f15a5d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cpg-api", - "version": "v3.2", + "version": "v3.3", "description": "Central Payment Gateway", "main": "./build/Main.js", "dependencies": { diff --git a/src/Admin/Commands/Cron.prompt.ts b/src/Admin/Commands/Cron.prompt.ts index d0744dc..7bf75d1 100644 --- a/src/Admin/Commands/Cron.prompt.ts +++ b/src/Admin/Commands/Cron.prompt.ts @@ -1,4 +1,5 @@ import { cron_chargeStripePayment, cron_notifyInvoices, cron_notifyLateInvoicePaid } from "../../Cron/Methods/Invoices.cron.methods"; +import { cron_createNewInvoicesFromOrders } from "../../Cron/Methods/Orders.cron.methods"; import Logger from "../../Lib/Logger"; export default @@ -22,6 +23,10 @@ export default { name: 'Invoice late notify', value: 'run_late_invoice_notify', + }, + { + name: "Create invoice from order", + value: 'run_create_invoice_from_order', } ], } @@ -43,6 +48,11 @@ export default Logger.info('Running Invoice late notify'); cron_notifyLateInvoicePaid() } + if(crons.includes('run_create_invoice_from_order')) + { + Logger.info('Running Create invoice from order'); + cron_createNewInvoicesFromOrders(); + } return true; } } \ No newline at end of file diff --git a/src/Admin/Commands/Invoices.prompt.ts b/src/Admin/Commands/Invoices.prompt.ts index 9c69fa3..9d8f55e 100644 --- a/src/Admin/Commands/Invoices.prompt.ts +++ b/src/Admin/Commands/Invoices.prompt.ts @@ -166,8 +166,10 @@ export default } invoice.paid = true; - + invoice.dates.date_paid = getDate(); + invoice.markModified('dates'); await invoice.save(); + mainEvent.emit("invoice_paid", invoice); Logger.info(`Invoice with id ${invoiceId} marked as paid`); break; } diff --git a/src/Admin/Commands/Orders.prompt.ts b/src/Admin/Commands/Orders.prompt.ts index fedc60d..1ee1c7b 100644 --- a/src/Admin/Commands/Orders.prompt.ts +++ b/src/Admin/Commands/Orders.prompt.ts @@ -13,6 +13,8 @@ import mainEvent from "../../Events/Main.event"; import { sendEmail } from "../../Email/Send"; import NewOrderCreated from "../../Email/Templates/Orders/NewOrderCreated"; import { Company_Name } from "../../Config"; +import { createInvoiceFromOrder } from "../../Lib/Orders/newInvoice"; +import { sendInvoiceEmail } from "../../Lib/Invoices/SendEmail"; export default { @@ -91,7 +93,7 @@ export default name: 'currency', type: 'search-list', message: 'Enter the currency', - choices: currencyCodes + choices: [...currencyCodes, "Customer Currency"] }, { name: 'products', @@ -178,18 +180,25 @@ export default } }); + const customer = await CustomerModel.findOne({ + id: customer_uid, + }); + + if(!customer) + throw new Error(`Fail to find customer with id: ${customer_uid}`); + const b_recurring = action2Result.billing_type === "recurring"; const newOrder = await (new OrderModel({ uid: idOrder(), invoices: [], - currency, + currency: currency === "Customer Currency" ? customer.currency : currency, customer_uid, dates: { createdAt: new Date(), last_recycle: b_recurring ? dateFormat.format(new Date(), "YYYY-MM-DD") : undefined, next_recycle: b_recurring ? dateFormat.format(nextRycleDate(new Date(), action2Result.billing_cycle), "YYYY-MM-DD") : undefined, }, - order_status: 'pending', + order_status: 'active', payment_method, products: newProduct, billing_type: action2Result.billing_type, @@ -200,13 +209,6 @@ export default mainEvent.emit("order_created", newOrder); - const customer = await CustomerModel.findOne({ - id: customer_uid, - }); - - if(!customer) - throw new Error(`Fail to find customer with id: ${customer_uid}`); - await sendEmail({ receiver: customer.personal.email, subject: `New order from ${await Company_Name() !== "" ? await Company_Name() : "CPG"} #${newOrder.id}`, @@ -217,6 +219,15 @@ export default Logger.info(newOrder); + // Creating new invoice + const invoice = await createInvoiceFromOrder(newOrder); + await sendInvoiceEmail(invoice, customer); + + newOrder.invoices.push(invoice.id); + await newOrder.save(); + + Logger.info(`Created new invoice:`, invoice); + break; } diff --git a/src/Cron/Methods/Invoices.cron.methods.ts b/src/Cron/Methods/Invoices.cron.methods.ts index 8bc0f2e..a897538 100644 --- a/src/Cron/Methods/Invoices.cron.methods.ts +++ b/src/Cron/Methods/Invoices.cron.methods.ts @@ -6,7 +6,7 @@ import GetText from "../../Translation/GetText"; import CustomerModel from "../../Database/Models/Customers/Customer.model"; import { sendInvoiceEmail, sendLateInvoiceEmail } from "../../Lib/Invoices/SendEmail"; import { ChargeCustomer } from "../../Payments/Stripe"; -import { InvoiceNotifiedReport } from "../../Email/Reports/InvoiceReport"; +import { InvoiceLateReport, InvoiceNotifiedReport } from "../../Email/Reports/InvoiceReport"; import mainEvent from "../../Events/Main.event"; import { getDate } from "../../Lib/Time"; @@ -44,7 +44,7 @@ export function cron_notifyInvoices() for await(const invoice of invoices) { // Get customer - const Customer = await CustomerModel.findOne({ id: invoice.customer_uid}); + const Customer = await CustomerModel.findOne({ id: invoice.customer_uid }); if(!Customer) continue; @@ -143,9 +143,9 @@ export function cron_notifyLateInvoicePaid() const Customer = await CustomerModel.findOne({ id: invoice.customer_uid}); if(!Customer) continue; - await sendLateInvoiceEmail(invoice, Customer); - } + if(invoices.length > 0) + await InvoiceLateReport(invoices); }); } \ No newline at end of file diff --git a/src/Cron/Methods/Orders.cron.methods.ts b/src/Cron/Methods/Orders.cron.methods.ts new file mode 100644 index 0000000..6ec7871 --- /dev/null +++ b/src/Cron/Methods/Orders.cron.methods.ts @@ -0,0 +1,55 @@ +import { Default_Language, d_Days } from "../../Config"; +import OrderModel from "../../Database/Models/Orders.model"; +import Logger from "../../Lib/Logger"; +import GetText from "../../Translation/GetText"; +import dateFormat from "date-and-time"; +import nextRecycleDate from "../../Lib/Dates/DateCycle"; +import { createInvoiceFromOrder } from "../../Lib/Orders/newInvoice"; +import { InvoiceCreatedReport } from "../../Email/Reports/InvoiceReport"; + +// Logger.info(`Checking orders..`); +export function cron_createNewInvoicesFromOrders() +{ + Logger.info(GetText(Default_Language).cron.txt_Orders_Checking); + // Check if the order needs to create a new invoice if order.dates.next_recycle is withing 14 days + OrderModel.find({ + order_status: "active", + // order_status: { + // $not: /fraud|cancelled|draft|refunded/g + // } + }).then(async orders => + { + const newInvoices = []; + // orders.forEach(async order => { + for await(const order of orders) + { + Logger.info(GetText(Default_Language).cron.txt_Order_Checking(order.id)); + // Logger.info(`Checking order ${order.id}`); + // Check if order.order_status is not "cancelled" or "fraud" + if(order.dates.next_recycle) + if(dateFormat.parse(order.dates.next_recycle, "YYYY-MM-DD").getTime() - new Date().getTime() <= d_Days * 24 * 60 * 60 * 1000) + { + const temptNextRecycle = order.dates.next_recycle; + order.dates.last_recycle = temptNextRecycle; + // Update order.dates.next_recycle + order.dates.next_recycle = dateFormat.format(nextRecycleDate( + dateFormat.parse(temptNextRecycle, "YYYY-MM-DD"), order.billing_cycle ?? "monthly") + , "YYYY-MM-DD"); + // Create a new invoice + const newInvoice = await createInvoiceFromOrder(order); + newInvoices.push(newInvoice); + + // Save the invoice in order.invoices array + order.invoices.push(newInvoice.id); + + // mark order updated in dates + order.markModified("dates"); + order.markModified("invoices"); + // Save the order + await order.save(); + } + if(newInvoices.length > 0) + await InvoiceCreatedReport(newInvoices); + } + }); +} \ No newline at end of file diff --git a/src/Cron/Orders.cron.ts b/src/Cron/Orders.cron.ts index fc3c03d..3750f98 100644 --- a/src/Cron/Orders.cron.ts +++ b/src/Cron/Orders.cron.ts @@ -1,62 +1,11 @@ import { CronJob } from "cron"; -import OrderModel from "../Database/Models/Orders.model"; -import Logger from "../Lib/Logger"; -import dateFormat from "date-and-time"; -import { createInvoiceFromOrder } from "../Lib/Orders/newInvoice"; -import nextRecycleDate from "../Lib/Dates/DateCycle"; -import { Default_Language, d_Days } from "../Config"; -import { InvoiceCreatedReport } from "../Email/Reports/InvoiceReport"; -import GetText from "../Translation/GetText"; +import { cron_createNewInvoicesFromOrders } from "./Methods/Orders.cron.methods"; export = function Cron_Orders() { // Every hour new CronJob("0 12 * * *", () => { - Logger.info(GetText(Default_Language).cron.txt_Orders_Checking); - // Logger.info(`Checking orders..`); - - // Check if the order needs to create a new invoice if order.dates.next_recycle is withing 14 days - OrderModel.find({ - order_status: "active", - // order_status: { - // $not: /fraud|cancelled|draft|refunded/g - // } - }).then(async orders => - { - const newInvoices = []; - // orders.forEach(async order => { - for await(const order of orders) - { - Logger.info(GetText(Default_Language).cron.txt_Order_Checking(order.id)); - // Logger.info(`Checking order ${order.id}`); - // Check if order.order_status is not "cancelled" or "fraud" - if(order.dates.next_recycle) - if(dateFormat.parse(order.dates.next_recycle, "YYYY-MM-DD").getTime() - new Date().getTime() <= d_Days * 24 * 60 * 60 * 1000) - { - const temptNextRecycle = order.dates.next_recycle; - order.dates.last_recycle = temptNextRecycle; - // Update order.dates.next_recycle - order.dates.next_recycle = dateFormat.format(nextRecycleDate( - dateFormat.parse(temptNextRecycle, "YYYY-MM-DD"), order.billing_cycle ?? "monthly") - , "YYYY-MM-DD"); - // Create a new invoice - const newInvoice = await createInvoiceFromOrder(order); - newInvoices.push(newInvoice); - - // Save the invoice in order.invoices array - order.invoices.push(newInvoice.id); - - // mark order updated in dates - order.markModified("dates"); - order.markModified("invoices"); - // Save the order - await order.save(); - } - if(newInvoices.length > 0) - await InvoiceCreatedReport(newInvoices); - } - }); - + cron_createNewInvoicesFromOrders(); }, null, true, "Europe/Stockholm"); } \ No newline at end of file diff --git a/src/Email/Reports/InvoiceReport.ts b/src/Email/Reports/InvoiceReport.ts index ce78558..ac3cdab 100644 --- a/src/Email/Reports/InvoiceReport.ts +++ b/src/Email/Reports/InvoiceReport.ts @@ -1,25 +1,29 @@ import { GetSMTPEmails } from "../../Config"; import { IInvoice } from "@interface/Invoice.interface"; import { SendEmail } from "../Send"; +import UseStyles from "../Templates/General/UseStyles"; +import { stripIndent } from "common-tags"; export const InvoiceNotifiedReport = async (invoices: IInvoice[]) => { - GetSMTPEmails().then((emails) => + GetSMTPEmails().then(async (emails) => { - for(const email of emails) + for await(const email of emails) { SendEmail(email, "Invoice(s) Notified", { isHTML: true, - body: ` -
- ${invoices.length} invoices have been notified. -
-
- The following invoices have been notified:
- ${invoices.map((invoice) => `
${invoice.id}`).join("")}
-
+ ${invoices.length} invoices have been notified. +
+
+ The following invoices have been notified:
+ ${invoices.map((invoice) => `
${invoice.id}`).join("")}
+
- ${invoices.length} invoices have been reminded. -
-
- The following invoices have been reminded:
- ${invoices.map((invoice) => `
${invoice.id}`).join("")}
-
+ ${invoices.length} invoices have been reminded. +
+
+ The following invoices have been reminded:
+ ${invoices.map((invoice) => `
${invoice.id}`).join("")}
+
- ${invoices.length} invoices have been created. -
-
- The following invoices have been created:
- ${invoices.map((invoice) => `
${invoice.id}`).join("")}
-
+ ${invoices.length} invoices have been created. +
+
+ The following invoices have been created:
+ ${invoices.map((invoice) => `
${invoice.id}`).join("")}
+
Customer: ${getFullName(c)}
+ + Invoice ID: ${t.invoice_uid} +${await PrintCompanyInformation()}
diff --git a/src/Lib/Invoices/CreatePDFInvoice.ts b/src/Lib/Invoices/CreatePDFInvoice.ts index deafff1..aaa0f09 100644 --- a/src/Lib/Invoices/CreatePDFInvoice.ts +++ b/src/Lib/Invoices/CreatePDFInvoice.ts @@ -23,6 +23,7 @@ import { import qrcode from "qrcode"; import GetText from "../../Translation/GetText"; import GetOCRNumber from "./GetOCRNumber"; +import { convertCurrency } from "../Currencies"; export default function createPDFInvoice(invoice: IInvoice): Promise