From 9c4b73da67732931b6d662bdc3767f9228fe2f56 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Sat, 27 Dec 2025 13:59:37 +0000 Subject: [PATCH] Enhance order item schema with new fields for state management, shipment tracking, and tax calculations. Introduce rollup statistics and history methods for improved data aggregation and reporting. --- .../schemas/inventory/orderitem.schema.js | 126 +++++++++++++++++- 1 file changed, 119 insertions(+), 7 deletions(-) diff --git a/src/database/schemas/inventory/orderitem.schema.js b/src/database/schemas/inventory/orderitem.schema.js index b5d0e9b..6b63879 100644 --- a/src/database/schemas/inventory/orderitem.schema.js +++ b/src/database/schemas/inventory/orderitem.schema.js @@ -1,6 +1,12 @@ import mongoose from 'mongoose'; import { purchaseOrderModel } from './purchaseorder.schema.js'; -import { aggregateRollups, editObject } from '../../database.js'; +import { taxRateModel } from '../management/taxrate.schema.js'; +import { + aggregateRollups, + aggregateRollupsHistory, + editObject, + getObject, +} from '../../database.js'; import { generateId } from '../../utils.js'; const { Schema } = mongoose; @@ -8,20 +14,64 @@ const orderItemSchema = new Schema( { _reference: { type: String, default: () => generateId()() }, orderType: { type: String, required: true }, + state: { + type: { type: String, required: true, default: 'draft' }, + }, order: { type: Schema.Types.ObjectId, refPath: 'orderType', required: true }, itemType: { type: String, required: true }, item: { type: Schema.Types.ObjectId, refPath: 'itemType', required: true }, - syncAmount: { type: String, required: true, default: null }, + syncAmount: { type: String, required: false, default: null }, itemAmount: { type: Number, required: true }, quantity: { type: Number, required: true }, totalAmount: { type: Number, required: true }, taxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false }, totalAmountWithTax: { type: Number, required: true }, timestamp: { type: Date, default: Date.now }, + shipment: { type: Schema.Types.ObjectId, ref: 'shipment', required: false }, + orderedAt: { type: Date, required: false }, + receivedAt: { type: Date, required: false }, }, { timestamps: true } ); +const rollupConfigs = [ + { + name: 'shipped', + filter: { 'state.type': 'shipped' }, + rollups: [{ name: 'shipped', property: 'state.type', operation: 'count' }], + }, + { + name: 'received', + filter: { 'state.type': 'received' }, + rollups: [{ name: 'received', property: 'state.type', operation: 'count' }], + }, +]; + +orderItemSchema.statics.stats = async function () { + const results = await aggregateRollups({ + model: this, + baseFilter: {}, + rollupConfigs: rollupConfigs, + }); + + console.log(results); + + // Transform the results to match the expected format + return results; +}; + +orderItemSchema.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; +}; + orderItemSchema.statics.recalculate = async function (orderItem, user) { // Only purchase orders are supported for now if (orderItem.orderType !== 'purchaseOrder') { @@ -33,6 +83,29 @@ orderItemSchema.statics.recalculate = async function (orderItem, user) { return; } + var taxRate = orderItem.taxRate; + + if (orderItem.taxRate?._id && Object.keys(orderItem.taxRate).length == 1) { + taxRate = await getObject({ + model: taxRateModel, + id: orderItem.taxRate._id, + cached: true, + }); + } + + const orderTotalAmount = orderItem.itemAmount * orderItem.quantity; + const orderTotalAmountWithTax = orderTotalAmount * (1 + (taxRate?.rate || 0) / 100); + await editObject({ + model: orderItemModel, + id: orderItem._id, + updateData: { + totalAmount: orderTotalAmount, + totalAmountWithTax: orderTotalAmountWithTax, + }, + user, + recalculate: false, + }); + const rollupResults = await aggregateRollups({ model: this, baseFilter: { @@ -51,21 +124,60 @@ orderItemSchema.statics.recalculate = async function (orderItem, user) { }, ], }, + { + name: 'overallCount', + rollups: [{ name: 'overallCount', property: '_id', operation: 'count' }], + }, + ...rollupConfigs, ], }); + console.log('rollupResults', rollupResults); + const totals = rollupResults.orderTotals || {}; const totalAmount = totals.totalAmount.sum?.toFixed(2) || 0; const totalAmountWithTax = totals.totalAmountWithTax.sum?.toFixed(2) || 0; + const purchaseOrder = await getObject({ + model: purchaseOrderModel, + id: orderId, + cached: true, + }); + + const grandTotalAmount = + parseFloat(totalAmountWithTax || 0) + parseFloat(purchaseOrder.shippingAmountWithTax || 0); + + var updateData = { + totalAmount: parseFloat(totalAmount).toFixed(2), + totalAmountWithTax: parseFloat(totalAmountWithTax).toFixed(2), + totalTaxAmount: parseFloat((totalAmountWithTax - totalAmount).toFixed(2)), + grandTotalAmount: parseFloat(grandTotalAmount).toFixed(2), + }; + + const overallCount = rollupResults.overallCount.count || 0; + const shippedCount = rollupResults.shipped.count || 0; + const receivedCount = rollupResults.received.count || 0; + + if (shippedCount > 0 && shippedCount < overallCount) { + updateData = { ...updateData, state: { type: 'partiallyShipped' } }; + } + + if (shippedCount > 0 && shippedCount == overallCount) { + updateData = { ...updateData, state: { type: 'shipped' } }; + } + + if (receivedCount > 0 && receivedCount < overallCount) { + updateData = { ...updateData, state: { type: 'partiallyReceived' } }; + } + + if (receivedCount > 0 && receivedCount == overallCount) { + updateData = { ...updateData, state: { type: 'received' } }; + } + await editObject({ model: purchaseOrderModel, id: orderId, - updateData: { - totalAmount: parseFloat(totalAmount), - totalAmountWithTax: parseFloat(totalAmountWithTax), - totalTaxAmount: parseFloat(totalAmountWithTax - totalAmount), - }, + updateData: updateData, user, }); };