I want to change the algorithm code written in JS to Mongodb aggregate for better performance and scope. MongoDB provides some custom powerful features like $function, $accumulator which can solve my global variables issue but unfortuantly digital ocean mongodb does not allow server scide scripting so i have to use built in aggregate stages for this.
This is the Algorithm written in JS:-
// make new algo with capabilities of carry forward reserver advance & payable ---------
let calcAdvance = 0; //holds calculated advance
let adReserve = 0; //holds reserve advance
let prevPayable = 0; //holds arreras
let algo = (bills || []).forEach((item, idx) => {
if (item.bill === "ADVANCE_PAYMENT") {
calcAdvance += parseFloat(item.fromAdvance); //10000
if (prevPayable && prevPayable > adReserve + calcAdvance) {
//1000
prevPayable -= adReserve + calcAdvance;
calcAdvance = 0;
adReserve = 0;
} else {
calcAdvance += adReserve - prevPayable;
prevPayable = 0;
adReserve = 0;
}
//----------------------------------------
totalAdvance += parseFloat(item.fromAdvance);
//----------------------------------------
}
if (item.bill === "INVOICE" && item.type !== "OPD") {
//-----------------------------------------
fullGrandTotal += parseFloat(item.payable);
//-----------------------------------------
if (adReserve > 0) {
adReserve += calcAdvance;
item.currentAdvance = adReserve;
//check for advance payment reserved or left
if (adReserve > parseFloat(item.payable)) {
adReserve = adReserve - parseFloat(item.payable); //change grandtotal to payNow
} else {
prevPayable = 0 + parseFloat(item.payable) - adReserve; //change grandtotal to payNow
adReserve = 0;
}
calcAdvance = 0;
} else {
item.currentAdvance = calcAdvance;
//set totalPrevPayable to next invoice
item.prevPayable = prevPayable; // new addition set prevPayable to next invoice
if (calcAdvance > parseFloat(item.payable) + prevPayable) {
//new addition add prevPayable
adReserve = calcAdvance - (parseFloat(item.payable) + prevPayable);
prevPayable = 0;
} else {
prevPayable += parseFloat(item.payable) - calcAdvance;
}
calcAdvance = 0;
}
}
});
And this is the aggregate Code:-
i had alittle experiments using map and reduce
const bills = await Bill.aggregate([
{
$addFields: {
patient: { $toString: "$patient" },
},
},
{
$match: {
patient: patientId,
},
},
{
$lookup: {
from: "users",
foreignField: "_id",
localField: "author",
as: "author",
},
},
{
$lookup: {
from: "addmissions",
foreignField: "_id",
localField: "addmission",
as: "addmission",
},
},
{
$lookup: {
from: "invoices",
let: { invoiceId: "$invoice" },
pipeline: [{ $match: { $expr: { $eq: ["$_id", "$$invoiceId"] } } }],
as: "invoice",
},
},
{
$lookup: {
from: "advancepayments",
let: { advancePaymentId: "$advancePayment" },
pipeline: [
{ $match: { $expr: { $eq: ["$_id", "$$advancePaymentId"] } } },
],
as: "advancePayment",
},
},
{
$addFields: {
author: { $arrayElemAt: ["$author", 0] },
addmission: { $arrayElemAt: ["$addmission", 0] },
advancePayment: { $arrayElemAt: ["$advancePayment", 0] },
invoice: { $arrayElemAt: ["$invoice", 0] },
},
},
/* -------------------------------CALCULATIONS---------------------------------- */
{
$group: {
_id: null,
payments: {
$push: {
$cond: [
{ $eq: ["$bill", "ADVANCE_PAYMENT"] },
{
type: "ADVANCE_PAYMENT",
amount: "$advancePayment.totalAmount",
},
{ type: "INVOICE", amount: "$invoice.payable" },
],
},
},
bills: { $push: "$$ROOT" },
},
},
{
$addFields: {
finalPayments: {
$reduce: {
input: "$payments",
initialValue: { total: 0, result: [] },
in: {
total: {
$cond: [
{ $eq: ["$$this.type", "ADVANCE_PAYMENT"] },
{ $subtract: ["$$value.total", "$$this.amount"] },
{ $add: ["$$value.total", "$$this.amount"] },
],
},
result: {
$concatArrays: [
"$$value.result",
[{ $cond: ["$$this.type", "ADVANCE_PAYMENT", "$$total"] }],
],
},
},
},
},
},
},
/* -------------------------------CALCULATIONS---------------------------------- */
]);