@evolvus/evolvus-charges-billing
Version:
`evolvus-charges-billing` is a component that encapsulates ChargesBilling management functionality across all Evolvus products.
705 lines (686 loc) • 27 kB
JavaScript
const debug = require("debug")("evolvus-charges-billing:index");
const model = require("./model/chargesBillingSchema");
const dbSchema = require("./db/chargesBillingSchema");
const validate = require("jsonschema").validate;
const _ = require('lodash');
const glParameters = require("@evolvus/evolvus-charges-gl-parameters");
const docketClient = require("@evolvus/evolvus-docket-client");
const billAudit = docketClient.audit;
var randomString = require('random-string');
const Dao = require("@evolvus/evolvus-mongo-dao").Dao;
const collection = new Dao("billing", dbSchema);
var modelSchema = model.schema;
const sweClient = require("@evolvus/evolvus-swe-client");
const generatePdf = require("@evolvus/evolvus-charges-generate-pdf");
const generateXML = require("@evolvus/evolvus-charges-generate-xml");
var corporateLinkage = require("@evolvus/evolvus-charges-corporate-linkage");
var moment = require("moment");
var fs = require("fs");
let toWords = require('to-words');
var shortid = require("shortid");
var axios = require("axios");
var name = process.env.APPLICATION_NAME || "CHARGES";
var ChargesServiceUrl = process.env.CHARGES_SERVICE_URL || "http://192.168.1.18:9292/api";
var errorCode = process.env.ERROR_CODE || "GB3";
var reattemptInDays = process.env.REATTEMPT_IN_DAYS || 3;
billAudit.application = name;
billAudit.source = "BILLSERVICE";
module.exports = {
modelSchema,
dbSchema,
filterAttributes
};
// All validations must be performed before we save the object here
// Once the db layer is called its is assumed the object is valid.
module.exports.save = (billingObject, ipAddress, createdBy) => {
return new Promise((resolve, reject) => {
try {
if (billingObject == null) {
throw new Error("IllegalArgumentException: Input value is null or undefined");
}
billAudit.name = "BILLING_SAVE INITIALIZED";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(billingObject);
billAudit.details = `Charges bill save is initiated`;
billAudit.eventDateTime = Date.now();
billAudit.status = "SUCCESS";
docketClient.postToDocket(billAudit);
var res = validate(billingObject, modelSchema);
debug("validation status: ", JSON.stringify(res.valid));
if (!res.valid) {
reject(res.errors);
} else {
collection.save(billingObject).then((result) => {
debug(`saved successfully ${result}`);
resolve(result);
}).catch((e) => {
debug(`failed to save with an error: ${e}`);
reject(e);
});
}
} catch (e) {
billAudit.name = "EXCEPTION ON BILLING_SAVE";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(billingObject);
billAudit.details = `Charges billing save is failed`;
billAudit.eventDateTime = Date.now();
billAudit.status = "FAILURE";
docketClient.postToDocket(billAudit);
debug(`caught exception ${e}`);
reject(e);
}
});
};
module.exports.update = (billNumber, updateObject, ipAddress, createdBy) => {
return new Promise((resolve, reject) => {
try {
if (billNumber == null || updateObject == null) {
throw new Error("IllegalArgumentException: BillNumber or Input value is null or undefined");
}
billAudit.name = "BILLING_UPDATE INITIALIZED";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(updateObject);
billAudit.details = `Charges billing update is initiated`;
billAudit.eventDateTime = Date.now();
billAudit.status = "SUCCESS";
docketClient.postToDocket(billAudit);
var result;
var errors = [];
_.mapKeys(updateObject, function (value, key) {
if (modelSchema.properties[key] != null) {
result = validate(value, modelSchema.properties[key]);
if (result.errors.length != 0) {
result.errors[0].property = key;
errors.push(result.errors);
}
}
});
debug("Validation status: ", JSON.stringify(result));
if (errors.length != 0) {
reject(errors[0][0]);
} else {
collection.findOne({
"billNumber": billNumber
}).then((billObject) => {
if (billObject) {
collection.update({
"billNumber": billNumber
}, updateObject).then((result) => {
debug(`Bill updated successfully ${JSON.stringify(result)}`);
var sweEventObject = {
"tenantId": billObject.tenantId,
"wfEntity": "BILL",
"wfEntityAction": "UPDATE",
"createdBy": createdBy,
"query": billNumber,
"object": billObject
};
sweClient.initialize(sweEventObject).then((sweResult) => {
var filterBill = {
"utilityCode": billObject.utilityCode,
"billNumber": billNumber
};
debug(`calling db update filterBill :${JSON.stringify(filterBill)} is a parameter`);
collection.update(filterBill, {
"billStatus": sweResult.data.wfInstanceStatus,
"wfInstanceId": sweResult.data.wfInstanceId
}).then((bill) => {
debug(`collection.update:bill updated with workflow status and id:${JSON.stringify(bill)}`);
resolve(bill);
}).catch((e) => {
debug("updating workflow status and id promise failed", e);
reject(e)
});
}).catch((e) => {
debug(`SWE initialize promise failed: ${e}`);
reject(e);
});
}).catch((e) => {
debug(`Bill update promise failed: ${e}`);
reject(e);
});
} else {
debug(`Bill ${billNumber} not found`);
reject(`Bill ${billNumber} not found`);
}
}).catch((e) => {
debug(`Finding bill promise failed`, e);
reject(e);
});
}
} catch (e) {
billAudit.name = "EXCEPTION ON BILLING_UPDATE";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(update);
billAudit.details = `Charges billing UPDATE failed`;
billAudit.eventDateTime = Date.now();
billAudit.status = "FAILURE";
docketClient.postToDocket(billAudit);
debug(`caught exception ${e}`);
reject(e);
}
});
};
module.exports.find = (filter, orderby, skipCount, limit, ipAddress, createdBy) => {
return new Promise((resolve, reject) => {
try {
billAudit.name = "BILLING_FIND INITIALIZED";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = `The filter Object is ${JSON.stringify(filter)}`;
billAudit.details = `Charges billing find is initiated`;
billAudit.eventDateTime = Date.now();
billAudit.status = "SUCCESS";
docketClient.postToDocket(billAudit);
let filterObject = _.pick(filter, filterAttributes);
if (filter.fromDate != null && filter.toDate != null) {
filterObject = {
$and: [{
$and: [filterObject]
},
{
$and: [{
billDate: {
$gte: filter.fromDate
}
}, {
billDate: {
$lte: filter.toDate
}
}]
}
]
};
}
collection.find(filterObject, orderby, skipCount, limit).then((result) => {
debug(`Number of Bills found is ${result.length}`);
resolve(result);
}).catch((e) => {
debug(`failed to fetch GlParameters: ${e}`);
reject(e);
});
} catch (e) {
billAudit.name = "EXCEPTION ON BILLING_FIND";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = `The filter Object is ${JSON.stringify(filter)}`;
billAudit.details = `Charges Billing find failed`;
billAudit.eventDateTime = Date.now();
billAudit.status = "FAILURE";
docketClient.postToDocket(billAudit);
debug(`caught exception ${e}`);
reject(e);
}
});
};
module.exports.generateBill = (corporate, transactions, billPeriod, createdBy, ipAddress) => {
return new Promise((resolve, reject) => {
try {
billAudit.name = "BILL_GENERATION INITIALIZED";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(corporate);
billAudit.details = `Charges bill generation initiated`;
billAudit.eventDateTime = Date.now();
billAudit.status = "SUCCESS";
docketClient.postToDocket(billAudit);
let sum = 0;
let details = [];
corporate.chargePlan.chargeCodes.forEach(chargeCode => {
transactions.forEach(transaction => {
_.mapKeys(transaction, function (value, key) {
let object = {};
if (key == chargeCode.transactionType.code) {
object.name = chargeCode.transactionType.name;
object.rate = chargeCode.amount;
object.transactions = value;
sum = sum + (chargeCode.amount * Number(value));
object.actualAmount = object.finalAmount = chargeCode.amount * Number(value);
object.discount = object.actualAmount - object.finalAmount;
details.push(object);
}
});
});
});
glParameters.find({}, {}, 0, 0, ipAddress, createdBy).then((glAccount) => {
billingObject = {};
billingObject.details = details;
billingObject.actualChargesAmount = billingObject.finalChargesAmount = sum;
billingObject.billFrequency = "Monthly";
billingObject.billNumber = randomString({
length: 10,
numeric: true,
letters: false,
special: false
});
billingObject.corporateName = corporate.corporateName;
billingObject.tenantId = corporate.tenantId;
billingObject.utilityCode = corporate.utilityCode;
billingObject.chargePlan = corporate.chargePlan.name;
billingObject.createdBy = billingObject.updatedBy = createdBy;
billingObject.createdDateAndTime = billingObject.billDate = billingObject.updatedDateAndTime = new Date().toISOString();
billingObject.billPeriod = billPeriod;
billingObject.actualGSTAmount = billingObject.finalGSTAmount = sum * Number(glAccount[0].GSTRate / 100).toFixed(2);
billingObject.actualTotalAmount = billingObject.finalTotalAmount = billingObject.actualChargesAmount + billingObject.actualGSTAmount;
if (billingObject.finalTotalAmount > 0) {
collection.save(billingObject, ipAddress, createdBy).then((res) => {
generatePDF(res, corporate, glAccount[0].GSTRate).then(response => {
var xmlObject = {
utilityCode: corporate.utilityCode,
billPeriod: billPeriod,
billNumber: billingObject.billNumber,
finalTotalAmount: billingObject.finalTotalAmount,
billDate: billingObject.billDate
};
generateXML.generateXml(corporate.emailId, "I", response.filename, xmlObject).then((xml) => {
debug(xml);
resolve(xml);
}).catch((e) => {
debug(e);
});
}).catch(e => {
debug(e);
resolve(e);
})
}).catch(e => {
debug(e);
reject(e)
})
} else {
debug(`As there are no transactions, Bill will not be generated for the Utility Code ${billingObject.utilityCode}`);
resolve(`As there are no transactions, Bill will not be generated for the Utility Code ${billingObject.utilityCode}`);
}
}).catch(e => {
debug(e);
reject(e)
});
} catch (error) {
billAudit.name = "EXCEPTION ON BILL_GENERATION";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(corporate);
billAudit.details = `Charges billing save is failed`;
billAudit.eventDateTime = Date.now();
billAudit.status = "FAILURE";
docketClient.postToDocket(billAudit);
debug(error);
reject(error);
}
});
};
module.exports.updateWorkflow = (utilityCode, ipAddress, createdBy, billNumber, update) => {
debug(`index update workflow method: utilityCode :${utilityCode}, billNumber :${billNumber}, update :${JSON.stringify(update)} are parameters`);
return new Promise((resolve, reject) => {
try {
if (utilityCode == null || billNumber == null || update == null) {
throw new Error("IllegalArgumentException:utilityCode or billNumber or input is null or undefined");
}
billAudit.name = "BILL_WORKFLOW_UPDATE INITIALIZED";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = `update bill with ${JSON.stringify(update)}`;
billAudit.details = `bill update method`;
billAudit.eventDateTime = Date.now();
billAudit.status = "SUCCESS";
docketClient.postToDocket(billAudit);
let emailFormat = "F";
let GST = 0;
let flag = "2";
if (update.processingStatus === "AUTHORIZED") {
update.billStatus = "CBS_POSTING_SUCCESSFUL";
emailFormat = "S";
flag = "1";
} else if (update.processingStatus === "FAILURE") {
update.billStatus = "CBS_POSTING_FAILURE";
var date = new Date(); // Get current Date
date.setDate(date.getDate() + Number(reattemptInDays));
update.reattemptDate = date.toISOString();
emailFormat = "F";
flag = "0";
} else {
update.billStatus = "REJECTED";
};
update.reattemptedStatus = update.billStatus;
Promise.all([collection.findOne({
"billNumber": billNumber
}), corporateLinkage.find({
"utilityCode": utilityCode
}, {}, 0, 0, ipAddress, createdBy), glParameters.find({}, {}, 0, 0, ipAddress, createdBy)]).then((result) => {
if (result[0]) {
GST = result[2][0].GSTRate;
debug(`calling db update method, filterBill:${billNumber} ,update: ${JSON.stringify(update)}`);
collection.update({
"billNumber": billNumber
}, update).then((resp) => {
debug("updated successfully", resp);
if (flag === "1") {
generatePDF(result[0], result[1][0], GST).then((pdf) => {
var xmlObject = {
utilityCode: utilityCode,
billPeriod: result[0].billPeriod,
billNumber: billNumber,
finalTotalAmount: result[0].finalTotalAmount,
billDate: result[0].billDate
};
generateXML.generateXml(result[1][0].emailId, emailFormat, pdf.filename, xmlObject).then((xml) => {
debug(xml);
resolve(xml);
}).catch((e) => {
debug(e);
resolve(e);
});
}).catch(e => {
debug(e)
resolve(e)
});
} else if (flag === "0") {
var xmlObject = {
utilityCode: utilityCode,
billPeriod: result[0].billPeriod,
billNumber: billNumber,
finalTotalAmount: result[0].finalTotalAmount,
billDate: result[0].billDate,
failureReason: result[0].postingFailureReason
};
generateXML.generateXml(result[1][0].emailId, emailFormat, null, xmlObject).then((xml) => {
debug(xml);
resolve(xml);
}).catch((e) => {
debug(e);
resolve(e);
});
} else {
debug("Record rejected.");
resolve(resp);
}
}).catch((error) => {
var reference = shortid.generate();
debug(`update promise failed due to ${error}, and reference Id :${reference}`);
reject(error);
});
} else {
debug(`Bill ${billNumber} not found`);
reject(`Bill ${billNumber} not found`);
}
}).catch((e) => {
debug(`Finding bill promise failed`, e);
reject(e);
});
} catch (e) {
var reference = shortid.generate();
billAudit.name = "EXCEPTION_ON BILL_WORKFLOWUPDATE";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = `update user with object ${JSON.stringify(update)}`;
billAudit.details = `caught Exception on user_update ${e.message}`;
billAudit.eventDateTime = Date.now();
billAudit.status = "FAILURE";
docketClient.postToDocket(billAudit);
debug(`try_catch failure due to :${e} and referenceId :${reference}`);
reject(e);
}
});
};
module.exports.reattempt = (bill, createdBy, ipAddress) => {
return new Promise(async (resolve, reject) => {
try {
billAudit.name = "BILL_REATTEMPT INITIALIZED";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(bill);
billAudit.details = `bill reattempt initiated`;
billAudit.eventDateTime = Date.now();
billAudit.status = "SUCCESS";
docketClient.postToDocket(billAudit);
let corporateDetails;
if (bill.errorCode == errorCode) {
corporateDetails = await corporateLinkage.find({
"utilityCode": bill.utilityCode
}, {}, 0, 0, ipAddress, createdBy);
var result = await collection.update({
billNumber: bill.billNumber
}, {
finalTotalAmount: Number(bill.finalTotalAmount) + Number(corporateDetails[0].returnCharges)
});
debug("Updated Bill with return charges before Posting to CBS", result);
if (result.nModified != 1) {
throw new Error("Not able to update return charges");
}
}
axios.post(`${ChargesServiceUrl}/accountPosting`, {
billNumber: bill.billNumber
}, {
headers: {
"X-USER": createdBy,
"X-IP-HEADER": ipAddress
}
}).then((res) => {
let updateObject = {
"reattemptFlag": "YES",
"updatedBy": createdBy,
"updatedDateAndTime": new Date().toISOString(),
"reattemptedDateAndTime": new Date().toISOString()
}
if (res.data.data.statusFlg === "0") {
updateObject.processingStatus = "AUTHORIZED";
} else {
updateObject.processingStatus = "FAILURE";
}
collection.findOne({
billNumber: bill.billNumber
}).then(billFound => {
let reasonString = "";
if (res.data != null && res.data.data != null && res.data.data.errorDesc != null) {
let reasonStringLength = res.data.data.errorDesc.length;
if (reasonStringLength > 256) {
reasonString = res.data.data.errorDesc.substring(0, 20);
} else {
reasonString = res.data.data.errorDesc;
}
}
updateObject.postingFailureReason = reasonString;
updateObject.errorCode = res.data.data.errorCode;
module.exports.updateWithoutWorkflow(bill.billNumber, updateObject, ipAddress, createdBy).then(() => {
module.exports.updateWorkflow(billFound.utilityCode, ipAddress, createdBy, bill.billNumber, updateObject).then((updated) => {
resolve(updated);
}).catch(e => {
debug(e);
resolve(e)
});
}).catch(e => {
debug(e);
resolve(e)
})
}).catch(e => {
reject(e)
});
}).catch(e => {
debug(e);
reject(e);
});
} catch (error) {
billAudit.name = "EXCEPTION_ON BILL_REATTEMPT";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(bill);
billAudit.details = `bill reattempt failed.`;
billAudit.eventDateTime = Date.now();
billAudit.status = "FAILURE";
docketClient.postToDocket(billAudit);
reject(error);
}
});
}
module.exports.updateWithoutWorkflow = (billNumber, updateObject, ipAddress, createdBy) => {
return new Promise((resolve, reject) => {
try {
if (billNumber == null || updateObject == null) {
throw new Error("IllegalArgumentException: BillNumber or Input value is null or undefined");
}
billAudit.name = "BILL_UPDATE_WITHOUT_WRKFLW";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(updateObject);
billAudit.details = `Charges billing update is initiated`;
billAudit.eventDateTime = Date.now();
billAudit.status = "SUCCESS";
docketClient.postToDocket(billAudit);
var result;
var errors = [];
_.mapKeys(updateObject, function (value, key) {
if (modelSchema.properties[key] != null) {
result = validate(value, modelSchema.properties[key]);
if (result.errors.length != 0) {
result.errors[0].property = key;
errors.push(result.errors);
}
}
});
debug("Validation status: ", JSON.stringify(result));
if (errors.length != 0) {
reject(errors[0][0]);
} else {
collection.findOne({
"billNumber": billNumber
}).then((billObject) => {
if (billObject) {
collection.update({
"billNumber": billNumber
}, updateObject).then((result) => {
debug(`Bill updated successfully without workflow ${JSON.stringify(result)}`);
resolve(result);
}).catch((e) => {
debug(`Bill update promise failed: ${e}`);
reject(e);
});
} else {
debug(`Bill ${billNumber} not found`);
reject(`Bill ${billNumber} not found`);
}
}).catch((e) => {
debug(`Finding bill promise failed`, e);
reject(e);
});
}
} catch (e) {
billAudit.name = "EXCEPTION ON BILLING_UPDATE";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(update);
billAudit.details = `Charges billing UPDATE without workflow failed`;
billAudit.eventDateTime = Date.now();
billAudit.status = "FAILURE";
docketClient.postToDocket(billAudit);
debug(`caught exception ${e}`);
reject(e);
}
});
};
module.exports.generatePdf = (filterObj, ipAddress, createdBy) => {
return new Promise((resolve, reject) => {
try {
billAudit.name = "BILL_PDF_GENERATION_FROM_UI";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(filterObj);
billAudit.details = `Charges billing pdf generation is initiated`;
billAudit.eventDateTime = Date.now();
billAudit.status = "SUCCESS";
docketClient.postToDocket(billAudit);
var result;
Promise.all([collection.findOne({
"billNumber": filterObj.billNumber
}), corporateLinkage.find({
"utilityCode": filterObj.utilityCode
}, {}, 0, 0, ipAddress, createdBy), glParameters.find({}, {}, 0, 0, ipAddress, createdBy)]).then((result) => {
GST = result[2][0].GSTRate;
generatePDF(result[0], result[1][0], GST).then((response) => {
resolve(response);
}).catch((e) => {
debug(`Finding bill promise failed`, e);
reject(e);
});
}).catch((e) => {
debug(`Finding bill promise failed`, e);
reject(e);
});
} catch (e) {
billAudit.name = "EXCEPTION ON BILL_PDF_GENERATION_FROM_UI";
billAudit.source = "BILLSERVICE";
billAudit.ipAddress = ipAddress;
billAudit.createdBy = createdBy;
billAudit.keyDataAsJSON = JSON.stringify(update);
billAudit.details = `Charges billing pdf generation failed`;
billAudit.eventDateTime = Date.now();
billAudit.status = "FAILURE";
docketClient.postToDocket(billAudit);
debug(`caught exception ${e}`);
reject(e);
}
});
};
function generatePDF(billObject, corporateDetails, GSTRate) {
return new Promise((resolve, reject) => {
billObject = billObject.toObject();
var fromDate = moment().subtract(1, 'month').startOf('month').format('DD-MM-YYYY');
var toDate = moment().subtract(1, 'month').endOf('month').format('DD-MM-YYYY');
billObject.fromDate = fromDate;
billObject.toDate = toDate;
billObject.date = moment(billObject.billDate).format("MMMM DD YYYY");
if (billObject.finalTotalAmount > 0) {
if (Number(billObject.finalTotalAmount).toFixed(2) % 1 == 0) {
billObject.toWords = toWords(billObject.finalTotalAmount, {
currency: true
})
} else {
billObject.toWords = toWords(Number(billObject.finalTotalAmount).toFixed(2), {
currency: true
});
}
} else {
billObject.toWords = "Zero";
}
generatePdf.generatePdf(billObject, corporateDetails, GSTRate).then((pdf) => {
debug("PDF generated successfully.");
resolve(pdf);
}).catch(e => {
debug(e);
resolve(e)
});
});
}
module.exports.billingObject = {
corporateName: "",
utilityCode: "",
billPeriod: "",
billFrequency: "Monthly",
actualChargesAmount: 0,
actualGSTAmount: 0,
actualTotalAmount: 0,
finalChargesAmount: 0,
finalGSTAmount: 0,
finalTotalAmount: 0,
createdBy: "",
createdDateAndTime: "",
updatedBy: "",
updatedDateAndTime: ""
};