agmission/Development/server/controllers/billing.js

223 lines
6.3 KiB
JavaScript

'use strict';
const { AppParamError } = require('../helpers/app_error');
const Customer = require('../model/customer'),
utils = require('../helpers/utils'),
excelUtil = require('../helpers/file_excel'),
moment = require('moment-timezone');
async function getCustUsage_post(req, res) {
const input = req.body;
if (!input || !utils.isValidDate(input.from) || !utils.isValidDate(input.to)) AppParamError.throw();
let matchOps = { active: true };
let from = new Date(input.from), to = new Date(input.to);
if (Boolean(input.billable))
matchOps.billable = true;
const pineline = [
{
$match: matchOps
},
{
$lookup: {
from: "jobs",
localField: "_id",
foreignField: "byPuid",
as: "cust_jobs"
}
},
{ $unwind: { path: '$cust_jobs' } },
{ // Filter jobs at least from the creation dates
$match: {
"cust_jobs.createdAt": { $gte: from }
}
},
{
$project: {
name: 1,
"cust_jobs._id": 1,
"monthYear": { $dateToString: { "format": "%Y-%m", date: "$cust_jobs.createdAt", timezone: input.tz } }
}
},
{
$lookup: {
from: "applications",
localField: "cust_jobs._id",
foreignField: "jobId",
as: "job_apps"
}
},
{ $unwind: { path: '$job_apps' } },
{ // Filter applications by processed dates
$match: {
"job_apps.updateDate": { $gte: from, $lte: to }
}
},
{
$group: {
"_id": { customer: "$name", month: "$monthYear" },
"totalSpray": { $sum: "$job_apps.totalSprayed" }
}
},
{
$replaceRoot: { newRoot: { $mergeObjects: ["$_id", "$$ROOT"] } }
},
{
$sort: { customer: 1, month: 1 }
},
{
$group: {
"_id": { customer: "$customer" },
"data": { $push: { month: "$month", sprayHa: { $round: ["$totalSpray", 2] } } }
}
},
{
$project:
{ "tmp": { $arrayToObject: { $zip: { inputs: ["$data.month", "$data.sprayHa"] } } } }
}
];
const data = await Customer.aggregate(pineline);
let usage = [];
if (data && data.length) {
// Make full default month ranges value 0 spray
let ranges = [];
const dateFrom = moment(input.from).tz(input.tz);
const dateTo = moment(input.to).tz(input.tz);
let interim = dateFrom.clone();
while (dateTo > interim || interim.format('M') === dateTo.format('M')) {
ranges[interim.format('YYYY-MM')] = 0;
interim.add(1, 'month');
}
// Merge full month ranges (with 0 spray) and found data to have full month-ranges data
usage = data.map(it => ({ ...it._id, ...{ ...ranges, ...it.tmp } }));
}
res.json(usage);
}
async function exportUsageDetail_post(req, res) {
const input = req.body;
if (!input || !utils.isValidDate(input.from) || !utils.isValidDate(input.to)) AppParamError.throw();
let matchOps = { active: true };
let from = new Date(input.from), to = new Date(input.to);
if (Boolean(input.billable))
matchOps.billable = true;
// Optional applicators filter with case insensitive regex
let applicatorFilter = {};
if (!utils.isEmptyArray(input.applicators)) {
const regexPatterns = input.applicators.map(email => ({
$regex: `^${email.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`,
$options: "i"
}));
applicatorFilter = { "cust_jobs.applicator": { $in: regexPatterns } };
}
const pineline = [
{ $match: matchOps },
{
$lookup: {
from: "jobs",
localField: "_id",
foreignField: "byPuid",
as: "cust_jobs"
}
},
{ $unwind: { path: '$cust_jobs' } },
{ // Filter jobs at least from the creation dates and applicator
$match: Object.assign({
"cust_jobs.createdAt": { $gte: from }
}, applicatorFilter)
},
{
$project: {
name: 1,
contact: 1,
address: 1,
email: 1,
phone: 1,
"cust_jobs._id": 1,
"cust_jobs.createdAt": input.tz ? { $dateToString: { date: "$cust_jobs.createdAt", format: '%Y-%m-%d', timezone: input.tz } } : 1,
"cust_jobs.applicator": 1
}
},
{
$lookup: {
from: "applications",
localField: "cust_jobs._id",
foreignField: "jobId",
as: "job_apps"
}
},
{ $unwind: { path: '$job_apps' } },
{
$match: {
"job_apps.updateDate": { $gte: from, $lte: to }
}
},
{
$group: {
"_id": {
name: "$name", contact: "$contact", address: "$address", email: "$email", phone: "$phone",
jobId: "$cust_jobs._id", "createdAt": "$cust_jobs.createdAt", applicator: "$cust_jobs.applicator"
},
"totalSpray": { $sum: "$job_apps.totalSprayed" }
}
},
{ $sort: { "_id.name": 1, "_id.createdAt": 1 } },
{
$group: {
"_id": { name: "$_id.name", contact: "$_id.contact", address: "$_id.address", email: "$_id.email", phone: "$_id.phone" },
"jobs": { $push: { jobId: "$_id.jobId", createdAt: "$_id.createdAt", applicator: "$_id.applicator", totalSpray: "$totalSpray" } }
}
},
{ $addFields: { "_id.jobs": "$jobs" } },
{ $project: { jobs: 0 } },
{ $replaceRoot: { newRoot: "$_id" } }
];
const data = await Customer.aggregate(pineline);
// Pipe the excel stream for client to download
const dateFmt = 'YYYY-MM';
const dateFrom = input.tz ? moment(input.from).tz(input.tz) : moment.utc(input.from);
const dateTo = input.tz ? moment(input.to).tz(input.tz) : moment.utc(input.from);
let fileName = `CustomerSpray_${dateFrom.format(dateFmt)}`;
if (Math.abs(dateFrom.diff(dateTo, 'months', true)) > 1.0)
fileName += `_${dateTo.format(dateFmt)}`;
res.status(200);
res.setHeader('Content-disposition', `attachment; filename=${fileName}.xlsx`);
res.setHeader('Content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
await excelUtil.exportUsageDetail(data, res);
res.end();
}
function getCustBillingStatus_get(req, res) {
// Dummy next billing cycle date
const next_cycle = new Date();
next_cycle.setDate(next_cycle.getFullYear() + 1);
res.json({
premium: 1,
status: 'active',
start: new Date(),
next_cycle: next_cycle
});
// next();
}
module.exports = {
getCustUsage_post, exportUsageDetail_post, getCustBillingStatus_get
}