All checks were successful
farmcontrol/farmcontrol-api/pipeline/head This commit looks good
This commit enhances the `invoice`, `taxrecord`, `orderitem`, `purchaseorder`, `shipment`, and `salesorder` schemas by introducing new rollup configurations for various states and values. It adds methods for aggregating statistics and historical data using `aggregateRollups` and `aggregateRollupsHistory`, improving data analysis capabilities across financial and inventory records. The new rollups include counts and sums for different transaction types and states, facilitating better reporting and insights into the application's financial and inventory management.
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);
|