Enhance financial and inventory schemas with new rollup configurations
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.
This commit is contained in:
Tom Butcher 2026-08-30 14:56:57 +01:00
parent ae2cefb183
commit ccbf2ff388
7 changed files with 156 additions and 6 deletions

View File

@ -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',

View File

@ -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;
});

View File

@ -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 () {

View File

@ -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 () {

View File

@ -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;

View File

@ -1,6 +1,11 @@
import mongoose from 'mongoose';
import { generateId } from '../../utils.js';
import { getObject, editObject, aggregateRollups, aggregateRollupsHistory } from '../../database.js';
import {
getObject,
editObject,
aggregateRollups,
aggregateRollupsHistory,
} from '../../database.js';
const { Schema } = mongoose;
const parentStockModelNames = {
@ -43,8 +48,6 @@ const buildParentState = (parentType, parentStock, currentAmount, startingAmount
const progress = currentAmount / startingAmount;
console.log('progress', progress);
if (currentAmount === startingAmount) {
return { ...parentStock.state, type: fullState, progress: 1 };
}

View File

@ -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 () {