From 6393ed5e3eb97d1486c6c32375aea03bfca23492 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Sun, 30 Aug 2026 14:56:48 +0100 Subject: [PATCH] Add rollup configurations and statistics methods to finance and inventory schemas 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. --- .../schemas/finance/invoice.schema.js | 10 +++- .../schemas/finance/taxrecord.schema.js | 39 +++++++++++++ .../schemas/inventory/orderitem.schema.js | 20 +++++++ .../schemas/inventory/purchaseorder.schema.js | 14 +++++ .../schemas/inventory/shipment.schema.js | 56 ++++++++++++++++++- .../schemas/sales/salesorder.schema.js | 14 +++++ 6 files changed, 150 insertions(+), 3 deletions(-) diff --git a/src/database/schemas/finance/invoice.schema.js b/src/database/schemas/finance/invoice.schema.js index 3b7bffe..bd9eea4 100644 --- a/src/database/schemas/finance/invoice.schema.js +++ b/src/database/schemas/finance/invoice.schema.js @@ -92,12 +92,18 @@ const rollupConfigs = [ { name: 'paid', filter: { 'state.type': 'paid' }, - rollups: [{ name: 'paidCount', property: 'state.type', operation: 'count' }], + 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' }], + rollups: [ + { name: 'overdueCount', property: 'state.type', operation: 'count' }, + { name: 'overdueGrandTotalAmount', property: 'grandTotalAmount', operation: 'sum' }, + ], }, { name: 'cancelled', diff --git a/src/database/schemas/finance/taxrecord.schema.js b/src/database/schemas/finance/taxrecord.schema.js index 278bcaa..d19c141 100644 --- a/src/database/schemas/finance/taxrecord.schema.js +++ b/src/database/schemas/finance/taxrecord.schema.js @@ -1,5 +1,6 @@ import mongoose from 'mongoose'; import { generateId } from '../../utils.js'; +import { aggregateRollups, aggregateRollupsHistory } from '../../database.js'; const { Schema } = mongoose; const taxRecordSchema = new Schema( @@ -21,6 +22,44 @@ const taxRecordSchema = new Schema( taxRecordSchema.index({ transactionType: 'text' }); +const rollupConfigs = [ + { + name: 'total', + filter: {}, + rollups: [ + { name: 'count', property: 'amount', operation: 'count' }, + { name: 'amount', property: 'amount', operation: 'sum' }, + { name: 'taxAmount', property: 'taxAmount', operation: 'sum' }, + ], + }, + { + name: 'salesOrder', + filter: { transactionType: 'salesOrder' }, + rollups: [{ name: 'taxAmount', property: 'taxAmount', operation: 'sum' }], + }, + { + name: 'purchaseOrder', + filter: { transactionType: 'purchaseOrder' }, + rollups: [{ name: 'taxAmount', property: 'taxAmount', operation: 'sum' }], + }, +]; + +taxRecordSchema.statics.stats = async function () { + return aggregateRollups({ + model: this, + rollupConfigs, + }); +}; + +taxRecordSchema.statics.history = async function (from, to) { + return aggregateRollupsHistory({ + model: this, + startDate: from, + endDate: to, + rollupConfigs, + }); +}; + taxRecordSchema.virtual('id').get(function () { return this._id; }); diff --git a/src/database/schemas/inventory/orderitem.schema.js b/src/database/schemas/inventory/orderitem.schema.js index 1a932f6..7286d1f 100644 --- a/src/database/schemas/inventory/orderitem.schema.js +++ b/src/database/schemas/inventory/orderitem.schema.js @@ -76,6 +76,16 @@ orderItemSchema.index({ name: 'text', itemType: 'text', orderType: 'text' }); orderItemSchema.index({ order: 1, externalReference: 1 }, { unique: true, sparse: true }); const rollupConfigs = [ + { + name: 'draft', + filter: { 'state.type': 'draft' }, + rollups: [{ name: 'draft', property: 'state.type', operation: 'count' }], + }, + { + name: 'ordered', + filter: { 'state.type': 'ordered' }, + rollups: [{ name: 'ordered', property: 'state.type', operation: 'count' }], + }, { name: 'shipped', filter: { 'state.type': 'shipped' }, @@ -86,6 +96,16 @@ const rollupConfigs = [ filter: { 'state.type': 'received' }, rollups: [{ name: 'received', property: 'state.type', operation: 'count' }], }, + { + name: 'shippedValue', + filter: { 'state.type': 'shipped' }, + rollups: [{ name: 'totalAmountWithTax', property: 'totalAmountWithTax', operation: 'sum' }], + }, + { + name: 'receivedValue', + filter: { 'state.type': 'received' }, + rollups: [{ name: 'totalAmountWithTax', property: 'totalAmountWithTax', operation: 'sum' }], + }, ]; orderItemSchema.statics.stats = async function () { diff --git a/src/database/schemas/inventory/purchaseorder.schema.js b/src/database/schemas/inventory/purchaseorder.schema.js index 9873c99..d6ff0dd 100644 --- a/src/database/schemas/inventory/purchaseorder.schema.js +++ b/src/database/schemas/inventory/purchaseorder.schema.js @@ -73,6 +73,20 @@ const rollupConfigs = [ filter: { 'state.type': 'completed' }, rollups: [{ name: 'completed', property: 'state.type', operation: 'count' }], }, + { + name: 'awaitingReceiptValue', + filter: { + 'state.type': { + $in: ['sent', 'acknowledged', 'partiallyShipped', 'shipped', 'partiallyReceived'], + }, + }, + rollups: [{ name: 'grandTotalAmount', property: 'grandTotalAmount', operation: 'sum' }], + }, + { + name: 'receivedValue', + filter: { 'state.type': { $in: ['received', 'completed'] } }, + rollups: [{ name: 'grandTotalAmount', property: 'grandTotalAmount', operation: 'sum' }], + }, ]; purchaseOrderSchema.statics.stats = async function () { diff --git a/src/database/schemas/inventory/shipment.schema.js b/src/database/schemas/inventory/shipment.schema.js index 95698b8..95f9beb 100644 --- a/src/database/schemas/inventory/shipment.schema.js +++ b/src/database/schemas/inventory/shipment.schema.js @@ -4,7 +4,12 @@ const { Schema } = mongoose; import { purchaseOrderModel } from './purchaseorder.schema.js'; import { salesOrderModel } from '../sales/salesorder.schema.js'; import { taxRateModel } from '../management/taxrate.schema.js'; -import { aggregateRollups, editObject, getObject } from '../../database.js'; +import { + aggregateRollups, + aggregateRollupsHistory, + editObject, + getObject, +} from '../../database.js'; const shipmentSchema = new Schema( { @@ -38,6 +43,55 @@ const shipmentSchema = new Schema( shipmentSchema.index({ trackingNumber: 'text', orderType: 'text' }); shipmentSchema.index({ order: 1, externalReference: 1 }, { unique: true, sparse: true }); +const rollupConfigs = [ + { + name: 'draft', + filter: { 'state.type': 'draft' }, + rollups: [{ name: 'draft', property: 'state.type', operation: 'count' }], + }, + { + name: 'planned', + filter: { 'state.type': 'planned' }, + rollups: [{ name: 'planned', property: 'state.type', operation: 'count' }], + }, + { + name: 'shipped', + filter: { 'state.type': 'shipped' }, + rollups: [{ name: 'shipped', property: 'state.type', operation: 'count' }], + }, + { + name: 'delivered', + filter: { 'state.type': 'delivered' }, + rollups: [{ name: 'delivered', property: 'state.type', operation: 'count' }], + }, + { + name: 'cancelled', + filter: { 'state.type': 'cancelled' }, + rollups: [{ name: 'cancelled', property: 'state.type', operation: 'count' }], + }, + { + name: 'inTransitValue', + filter: { 'state.type': 'shipped' }, + rollups: [{ name: 'amountWithTax', property: 'amountWithTax', operation: 'sum' }], + }, +]; + +shipmentSchema.statics.stats = async function () { + return aggregateRollups({ + model: this, + rollupConfigs, + }); +}; + +shipmentSchema.statics.history = async function (from, to) { + return aggregateRollupsHistory({ + model: this, + startDate: from, + endDate: to, + rollupConfigs, + }); +}; + shipmentSchema.statics.recalculate = async function (shipment, user) { if (shipment.orderType !== 'purchaseOrder' && shipment.orderType !== 'salesOrder') { return; diff --git a/src/database/schemas/sales/salesorder.schema.js b/src/database/schemas/sales/salesorder.schema.js index bd0632e..3690856 100644 --- a/src/database/schemas/sales/salesorder.schema.js +++ b/src/database/schemas/sales/salesorder.schema.js @@ -76,6 +76,20 @@ const rollupConfigs = [ filter: { 'state.type': 'completed' }, rollups: [{ name: 'completed', property: 'state.type', operation: 'count' }], }, + { + name: 'pipelineValue', + filter: { + 'state.type': { + $in: ['sent', 'confirmed', 'partiallyShipped', 'shipped', 'partiallyDelivered'], + }, + }, + rollups: [{ name: 'grandTotalAmount', property: 'grandTotalAmount', operation: 'sum' }], + }, + { + name: 'completedValue', + filter: { 'state.type': { $in: ['delivered', 'completed'] } }, + rollups: [{ name: 'grandTotalAmount', property: 'grandTotalAmount', operation: 'sum' }], + }, ]; salesOrderSchema.statics.stats = async function () {