All checks were successful
farmcontrol/farmcontrol-ws/pipeline/head This commit looks good
- Added new rollup configurations to the `invoice`, `taxrecord`, `orderitem`, `purchaseorder`, `shipment`, and `salesorder` schemas to improve financial reporting and inventory management. - Introduced aggregate functions for calculating totals and counts based on various states, enhancing data analysis capabilities across the application.
198 lines
6.6 KiB
JavaScript
198 lines
6.6 KiB
JavaScript
import mongoose from 'mongoose';
|
|
import { generateId } from '../../utils.js';
|
|
const { Schema } = mongoose;
|
|
import { aggregateRollups, aggregateRollupsHistory, editObject } from '../../database.js';
|
|
|
|
const invoiceOrderItemSchema = new Schema(
|
|
{
|
|
orderItem: { type: Schema.Types.ObjectId, ref: 'orderItem', required: true },
|
|
taxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
|
invoiceAmountWithTax: { type: Number, required: true, default: 0 },
|
|
invoiceAmount: { type: Number, required: true, default: 0 },
|
|
invoiceQuantity: { type: Number, required: true, default: 0 },
|
|
},
|
|
{ timestamps: true }
|
|
);
|
|
|
|
const invoiceShipmentSchema = new Schema(
|
|
{
|
|
shipment: { type: Schema.Types.ObjectId, ref: 'shipment', required: true },
|
|
taxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
|
invoiceAmountWithTax: { type: Number, required: true, default: 0 },
|
|
invoiceAmount: { type: Number, required: true, default: 0 },
|
|
},
|
|
{ timestamps: true }
|
|
);
|
|
|
|
const invoiceSchema = new Schema(
|
|
{
|
|
_reference: { type: String, default: () => generateId()() },
|
|
totalAmount: { type: Number, required: true, default: 0 },
|
|
totalAmountWithTax: { type: Number, required: true, default: 0 },
|
|
shippingAmount: { type: Number, required: true, default: 0 },
|
|
shippingAmountWithTax: { type: Number, required: true, default: 0 },
|
|
grandTotalAmount: { type: Number, required: true, default: 0 },
|
|
totalTaxAmount: { type: Number, required: true, default: 0 },
|
|
from: { type: Schema.Types.ObjectId, refPath: 'fromType', required: false },
|
|
fromType: { type: String, required: false },
|
|
to: { type: Schema.Types.ObjectId, refPath: 'toType', required: false },
|
|
toType: { type: String, required: false },
|
|
state: {
|
|
type: { type: String, required: true, default: 'draft' },
|
|
},
|
|
orderType: { type: String, required: true },
|
|
order: { type: Schema.Types.ObjectId, refPath: 'orderType', required: true },
|
|
issuedAt: { type: Date, required: false },
|
|
dueAt: { type: Date, required: false },
|
|
postedAt: { type: Date, required: false },
|
|
acknowledgedAt: { type: Date, required: false },
|
|
paidAt: { type: Date, required: false },
|
|
cancelledAt: { type: Date, required: false },
|
|
invoiceOrderItems: [invoiceOrderItemSchema],
|
|
invoiceShipments: [invoiceShipmentSchema],
|
|
},
|
|
{ timestamps: true }
|
|
);
|
|
|
|
invoiceSchema.index({ orderType: 'text', fromType: 'text', toType: 'text' });
|
|
|
|
const rollupConfigs = [
|
|
{
|
|
name: 'draft',
|
|
filter: { 'state.type': 'draft' },
|
|
rollups: [
|
|
{ name: 'draftCount', property: 'state.type', operation: 'count' },
|
|
{ name: 'draftGrandTotalAmount', property: 'grandTotalAmount', operation: 'sum' },
|
|
],
|
|
},
|
|
{
|
|
name: 'sent',
|
|
filter: { 'state.type': 'sent' },
|
|
rollups: [
|
|
{ name: 'sentCount', property: 'state.type', operation: 'count' },
|
|
{ name: 'sentGrandTotalAmount', property: 'grandTotalAmount', operation: 'sum' },
|
|
],
|
|
},
|
|
{
|
|
name: 'acknowledged',
|
|
filter: { 'state.type': 'acknowledged' },
|
|
rollups: [
|
|
{ name: 'acknowledgedCount', property: 'state.type', operation: 'count' },
|
|
{ name: 'acknowledgedGrandTotalAmount', property: 'grandTotalAmount', operation: 'sum' },
|
|
],
|
|
},
|
|
{
|
|
name: 'partiallyPaid',
|
|
filter: { 'state.type': 'partiallyPaid' },
|
|
rollups: [
|
|
{ name: 'partiallyPaidCount', property: 'state.type', operation: 'count' },
|
|
{ name: 'partiallyPaidGrandTotalAmount', property: 'grandTotalAmount', operation: 'sum' },
|
|
],
|
|
},
|
|
{
|
|
name: 'paid',
|
|
filter: { 'state.type': 'paid' },
|
|
rollups: [
|
|
{ name: 'paidCount', property: 'state.type', operation: 'count' },
|
|
{ name: 'paidGrandTotalAmount', property: 'grandTotalAmount', operation: 'sum' },
|
|
],
|
|
},
|
|
{
|
|
name: 'overdue',
|
|
filter: { 'state.type': 'overdue' },
|
|
rollups: [
|
|
{ name: 'overdueCount', property: 'state.type', operation: 'count' },
|
|
{ name: 'overdueGrandTotalAmount', property: 'grandTotalAmount', operation: 'sum' },
|
|
],
|
|
},
|
|
{
|
|
name: 'cancelled',
|
|
filter: { 'state.type': 'cancelled' },
|
|
rollups: [{ name: 'cancelledCount', property: 'state.type', operation: 'count' }],
|
|
},
|
|
];
|
|
|
|
invoiceSchema.statics.stats = async function () {
|
|
const results = await aggregateRollups({
|
|
model: this,
|
|
rollupConfigs: rollupConfigs,
|
|
});
|
|
|
|
// Transform the results to match the expected format
|
|
return results;
|
|
};
|
|
|
|
invoiceSchema.statics.history = async function (from, to) {
|
|
const results = await aggregateRollupsHistory({
|
|
model: this,
|
|
startDate: from,
|
|
endDate: to,
|
|
rollupConfigs: rollupConfigs,
|
|
});
|
|
|
|
// Return time-series data array
|
|
return results;
|
|
};
|
|
|
|
invoiceSchema.statics.recalculate = async function (invoice, user) {
|
|
const invoiceId = invoice._id || invoice;
|
|
if (!invoiceId) {
|
|
return;
|
|
}
|
|
|
|
// Calculate totals from invoiceOrderItems
|
|
let totalAmount = 0;
|
|
for (const item of invoice.invoiceOrderItems || []) {
|
|
totalAmount += Number.parseFloat(item.invoiceAmount) || 0;
|
|
}
|
|
let totalAmountWithTax = 0;
|
|
for (const item of invoice.invoiceOrderItems || []) {
|
|
totalAmountWithTax += Number.parseFloat(item.invoiceAmountWithTax) || 0;
|
|
}
|
|
|
|
// Calculate shipping totals from invoiceShipments
|
|
let shippingAmount = 0;
|
|
for (const item of invoice.invoiceShipments || []) {
|
|
shippingAmount += Number.parseFloat(item.invoiceAmount) || 0;
|
|
}
|
|
let shippingAmountWithTax = 0;
|
|
for (const item of invoice.invoiceShipments || []) {
|
|
shippingAmountWithTax += Number.parseFloat(item.invoiceAmountWithTax) || 0;
|
|
}
|
|
|
|
// Calculate grand total and tax amount
|
|
const grandTotalAmount = parseFloat(totalAmountWithTax) + parseFloat(shippingAmountWithTax);
|
|
const totalTaxAmount =
|
|
parseFloat(totalAmountWithTax) -
|
|
parseFloat(totalAmount) +
|
|
(parseFloat(shippingAmountWithTax) - parseFloat(shippingAmount));
|
|
|
|
const updateData = {
|
|
totalAmount: parseFloat(totalAmount).toFixed(2),
|
|
totalAmountWithTax: parseFloat(totalAmountWithTax).toFixed(2),
|
|
shippingAmount: parseFloat(shippingAmount).toFixed(2),
|
|
shippingAmountWithTax: parseFloat(shippingAmountWithTax).toFixed(2),
|
|
grandTotalAmount: parseFloat(grandTotalAmount).toFixed(2),
|
|
totalTaxAmount: parseFloat(totalTaxAmount).toFixed(2),
|
|
};
|
|
|
|
await editObject({
|
|
model: this,
|
|
id: invoiceId,
|
|
updateData,
|
|
user,
|
|
recalculate: false,
|
|
});
|
|
};
|
|
|
|
// Add virtual id getter
|
|
invoiceSchema.virtual('id').get(function () {
|
|
return this._id;
|
|
});
|
|
|
|
// Configure JSON serialization to include virtuals
|
|
invoiceSchema.set('toJSON', { virtuals: true });
|
|
|
|
// Create and export the model
|
|
export const invoiceModel = mongoose.model('invoice', invoiceSchema);
|