109 lines
3.8 KiB
JavaScript
109 lines
3.8 KiB
JavaScript
'use strict';
|
|
|
|
const cron = require('node-cron'),
|
|
debug = require('debug')('agm:invoice_worker'),
|
|
env = require('../helpers/env.js'),
|
|
isProd = env.PRODUCTION,
|
|
dbConn = require('../helpers/db/connect.js')(),
|
|
// dbConn = require('../helpers/db/connect-remote.js')(),
|
|
models = require('../model'),
|
|
{ InvoiceStatus } = require('../helpers/constants'),
|
|
utils = require('../helpers/utils.js'),
|
|
{ isEqual, cloneDeep } = require('lodash'),
|
|
moment = require('moment');
|
|
|
|
process
|
|
.on('uncaughtException', function (err) {
|
|
debug(err);
|
|
process.exit(1);
|
|
})
|
|
.on('unhandledRejection', (reason, p) => {
|
|
debug(reason, 'Unhandled Rejection at Promise', p);
|
|
});
|
|
|
|
|
|
// Checking on invoices and update (i.e.: status to automatically transit into the next status such as Draft to Open) them accordlingly
|
|
const processInvoices = {
|
|
schedule: isProd ? '01 10 * * *' : `*/15 * * * * *`,
|
|
status: 0,
|
|
name: "processInvoices"
|
|
};
|
|
const processInvoicesTask = cron.schedule(processInvoices.schedule, async () => {
|
|
// Check and only proceed when is idle and the db connection is connected
|
|
if (!dbConn || dbConn.readyState !== 1 || processInvoices.status)
|
|
return;
|
|
|
|
let result;
|
|
try {
|
|
processInvoices.status = 1;
|
|
|
|
const currDateUTC = moment.utc().endOf('day'); // Shift today to the end of day for easily checking with other dates counting by day
|
|
const bulkUpdateOps = [];
|
|
|
|
//1. Retreive the eligible invoices, by batch, for processing then loop through them while retreiving which is not a proper way.
|
|
const invoices = await models.Invoice.find({
|
|
$or: [{ status: InvoiceStatus.Draft, openDate: { $lte: currDateUTC } }, { status: InvoiceStatus.Open, dueDate: { $gte: currDateUTC } }]
|
|
}, 'status openDate dueDate clients', { lean: true }).limit(100);
|
|
|
|
let invUpdateOrg, invUpdate, openDate, dueDate;
|
|
|
|
for (const invoice of invoices) {
|
|
invUpdate = { ...invoice.status };
|
|
invUpdateOrg = cloneDeep(invUpdate);
|
|
|
|
openDate = moment.utc(invoice.openDate);
|
|
|
|
// Case 1: invoice status is draft and current date >= open date => set status = open
|
|
if (invoice.status == InvoiceStatus.Draft && currDateUTC >= openDate) {
|
|
invUpdate.status = InvoiceStatus.Open;
|
|
}
|
|
|
|
dueDate = moment.utc(invoice.dueDate);
|
|
// dueDate.setDate(dueDate.getDate() + 1); // Old logic - Add a day to compare with end of duedate
|
|
|
|
if (invoice.status === InvoiceStatus.Open && dueDate >= currDateUTC)
|
|
|
|
// Case 2: Check Open invoice to transit to Paid or Uncollectible (should bases on a number of days param)
|
|
if (invoice.status == InvoiceStatus.Open) {
|
|
const clients = invoice.clients;
|
|
let isPaid = true;
|
|
// Check on all clients's amountDue to decide whether the invoice was fully paid
|
|
for (const client of clients) {
|
|
if (client.amountDue == undefined || Number(client.amountDue) > 0) {
|
|
isPaid = false;
|
|
break;
|
|
}
|
|
}
|
|
if (isPaid) {
|
|
invUpdate.status = InvoiceStatus.Paid;
|
|
}
|
|
// TODO: when a parameter to decide the invoice as Uncollectible, check and update here
|
|
// else { invoice.status = InvoiceStatus.Uncollectible;}
|
|
}
|
|
|
|
if (!isEqual(invUpdateOrg, invUpdate)) {
|
|
const invUpdateDoc = { 'updateOne': { 'filter': { '_id': invoice._id }, 'update': invUpdate } };
|
|
bulkUpdateOps.push(invUpdateDoc);
|
|
}
|
|
}
|
|
|
|
if (!utils.isEmptyArray(bulkUpdateOps)) {
|
|
result = await models.Invoice.bulkWrite(bulkUpdateOps);
|
|
}
|
|
return result;
|
|
} catch (error) {
|
|
debug(error);
|
|
} finally {
|
|
if (result) {
|
|
debug('Done. processInvoicesTask:', result);
|
|
}
|
|
processInvoices.status = 0;
|
|
}
|
|
},
|
|
{
|
|
scheduled: true,
|
|
timezone: "Etc/UTC",
|
|
name: processInvoices.name,
|
|
runOnInit: true
|
|
});
|