agmission/Development/server/controllers/log_payment.js

134 lines
5.2 KiB
JavaScript

'use strict';
const { AppParamError, AppError } = require('../helpers/app_error'),
{ Errors, UserTypes, InvoiceStatus } = require('../helpers/constants'),
{ toFixedNumberString } = require('../helpers/utils'),
moment = require('moment'),
mongoose = require('mongoose'),
mongoUtil = require('../helpers/mongo'),
env = require('../helpers/env'),
{ Invoice, LogPayment } = require('../model'),
ObjectId = require('mongodb').ObjectId;
async function getLogPayments_get(req, res) {
const invoiceId = req?.query?.invoiceId;
const puid = req.userInfo?.puid;
const invoice = await Invoice.findOne({ _id: invoiceId, byPuid: puid });
if (!invoice) AppParamError.throw(Errors.INVOICE_NOT_FOUND);
const userId = req.uid;
const option = { invoice: invoice };
const isClientRole = req.ut === UserTypes.CLIENT;
if (isClientRole) option['client'] = userId;
const logPayments = await LogPayment.find(option).populate({ path: 'client', select: 'name email address phone' }).populate({ path: 'createdBy', select: 'name email address phone' });
res.json(logPayments);
}
async function insertLogPayment(logPaymentBody, puid, createdBy, session) {
const invoiceId = logPaymentBody?.invoiceId;
const clientId = logPaymentBody?.clientId;
const amount = logPaymentBody?.amount;
const invoice = await Invoice.findOne({ 'clients.billTo': clientId, _id: invoiceId, byPuid: puid });
if (!invoice) AppParamError.throw(Errors.INVOICE_NOT_FOUND);
// Prevent further payments if the invoice is already in these following states
if (invoice.status === InvoiceStatus.Paid) AppError.throw(Errors.INVOICE_ALREADY_PAID);
if (invoice.status === InvoiceStatus.Cancelled) AppError.throw(Errors.INVOICE_CANCELLED);
if (invoice.status === InvoiceStatus.Draft) AppError.throw(Errors.INVOICE_DRAFT);
if (invoice.status === InvoiceStatus.Void) AppError.throw(Errors.INVOICE_VOID);
const client = invoice?.clients?.find((client) => client?.billTo == clientId);
if (!client) AppParamError.throw(Errors.CLIENT_NOT_FOUND);
// Calculate total amountDue
const amountPaid = client?.amountPaid ? Number(client?.amountPaid) + Number(amount) : Number(amount);
const totalExcludingTax = Number(client?.subTotal) * (1 - Number(client?.discount) / 100);
const total = toFixedNumberString(totalExcludingTax + totalExcludingTax * (Number(client?.taxRate) / 100));
const amountDue = Number(total) - amountPaid;
if (env.INV_OVERPAID_THRESHOLD && amountPaid > (Number(total) * (1 + (env.INV_OVERPAID_THRESHOLD * 1E-2)))) AppError.create(Errors.CLIENT_OVER_PAID);
// Update the specific client's payment details
client.amountPaid = toFixedNumberString(amountPaid);
client.amountDue = toFixedNumberString(amountDue);
// Check if the invoice is fully paid based on all clients
let totalAmountDue = 0;
for (const client of invoice.clients) {
const clientTotalExcludingTax = Number(client?.subTotal) * (1 - Number(client?.discount) / 100);
const clientTotal = toFixedNumberString(clientTotalExcludingTax + clientTotalExcludingTax * (Number(client?.taxRate) / 100));
const clientAmountPaid = Number(client?.amountPaid || 0);
totalAmountDue += Number(clientTotal) - clientAmountPaid;
}
if (totalAmountDue <= 0) {
invoice.status = InvoiceStatus.Paid;
invoice.paidAt = moment.utc().toDate();
// Ensure all clients' `amountDue` is set to 0
invoice.clients.forEach((client) => {
client.amountDue = toFixedNumberString(0);
});
}
const createdLogPayment = new LogPayment({ ...logPaymentBody, invoice: invoiceId, client: clientId, amount: toFixedNumberString(amount), amountDue: toFixedNumberString(amountDue), createdBy });
const logPayment = await createdLogPayment.save({ session });
await invoice.save({ session });
return logPayment;
}
async function createLogPayment_post(req, res) {
const logPaymentBody = req?.body;
const puid = req.userInfo?.puid;
if (!puid || !ObjectId.isValid(puid)) AppParamError.throw(Errors.INVALID_PUID);
const createdBy = req.uid;
if (!createdBy) AppParamError.throw(Errors.INVALID_CREATED_BY_USER_ID);
let logPayment;
await mongoUtil.runInTransaction(async (session) => {
logPayment = await insertLogPayment(logPaymentBody, puid, createdBy, session);
});
res.json(logPayment);
}
async function createLogPayments_post(req, res) {
const logPayments = [];
const logPaymentsBody = req.body;
const puid = req.userInfo?.puid;
if (!puid || !ObjectId.isValid(puid)) AppParamError.throw(Errors.INVALID_PUID);
const createdBy = req.uid;
if (!createdBy) AppParamError.throw(Errors.INVALID_CREATED_BY_USER_ID);
const session = await mongoose.startSession({ readPreference: { mode: "primary" } });
try {
session && session.startTransaction();
for (const logPayment of logPaymentsBody) {
const newLogPayment = await insertLogPayment(logPayment, puid, createdBy, session);
logPayments.push(newLogPayment);
}
session && await mongoUtil.commitWithRetry(session);
} catch (error) {
session && session.abortTransaction();
throw error;
} finally {
session && session.endSession();
}
res.json(logPayments);
}
module.exports = {
getLogPayments_get, createLogPayment_post, createLogPayments_post,
};