import mongoose from 'mongoose'; import { generateId } from '../../utils.js'; const { Schema } = mongoose; import { purchaseOrderModel } from './purchaseorder.schema.js'; import { taxRateModel } from '../management/taxrate.schema.js'; import { aggregateRollups, editObject, getObject } from '../../database.js'; const shipmentSchema = new Schema( { _reference: { type: String, default: () => generateId()() }, orderType: { type: String, required: true }, order: { type: Schema.Types.ObjectId, refPath: 'orderType', required: true }, courierService: { type: Schema.Types.ObjectId, ref: 'courierService', required: false }, trackingNumber: { type: String, required: false }, amount: { type: Number, required: true }, amountWithTax: { type: Number, required: true }, taxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false }, invoicedAmount: { type: Number, required: false, default: 0 }, invoicedAmountWithTax: { type: Number, required: false, default: 0 }, invoicedAmountRemaining: { type: Number, required: false, default: 0 }, invoicedAmountWithTaxRemaining: { type: Number, required: false, default: 0 }, shippedAt: { type: Date, required: false }, expectedAt: { type: Date, required: false }, deliveredAt: { type: Date, required: false }, cancelledAt: { type: Date, required: false }, state: { type: { type: String, required: true, }, }, }, { timestamps: true } ); shipmentSchema.statics.recalculate = async function (shipment, user) { // Only purchase orders are supported for now if (shipment.orderType !== 'purchaseOrder') { return; } const orderId = shipment.order?._id || shipment.order; if (!orderId) { return; } var taxRate = shipment.taxRate; if (shipment.taxRate?._id && Object.keys(shipment.taxRate).length == 1) { taxRate = await getObject({ model: taxRateModel, id: shipment.taxRate._id, cached: true, }); } const amountWithTax = parseFloat( (shipment.amount || 0) * (1 + (taxRate?.rate || 0) / 100) ).toFixed(2); await editObject({ model: shipmentModel, id: shipment._id, updateData: { amountWithTax: amountWithTax, invoicedAmountRemaining: shipment.amount - (shipment.invoicedAmount || 0), invoicedAmountWithTaxRemaining: amountWithTax - (shipment.invoicedAmountWithTax || 0), }, user, recalculate: false, }); const rollupResults = await aggregateRollups({ model: this, baseFilter: { order: new mongoose.Types.ObjectId(orderId), orderType: shipment.orderType, }, rollupConfigs: [ { name: 'shipmentTotals', rollups: [ { name: 'amount', property: 'amount', operation: 'sum' }, { name: 'amountWithTax', property: 'amountWithTax', operation: 'sum' }, ], }, ], }); const totals = rollupResults.shipmentTotals || {}; const totalShippingAmount = totals.amount.sum?.toFixed(2) || 0; const totalShippingAmountWithTax = totals.amountWithTax.sum?.toFixed(2) || 0; const purchaseOrder = await getObject({ model: purchaseOrderModel, id: orderId, cached: true, }); const grandTotalAmount = parseFloat(purchaseOrder.totalAmountWithTax || 0) + parseFloat(totalShippingAmountWithTax || 0); await editObject({ model: purchaseOrderModel, id: orderId, updateData: { shippingAmount: parseFloat(totalShippingAmount).toFixed(2), shippingAmountWithTax: parseFloat(totalShippingAmountWithTax).toFixed(2), grandTotalAmount: parseFloat(grandTotalAmount).toFixed(2), }, user, recalculate: false, }); }; // Add virtual id getter shipmentSchema.virtual('id').get(function () { return this._id; }); // Configure JSON serialization to include virtuals shipmentSchema.set('toJSON', { virtuals: true }); // Create and export the model export const shipmentModel = mongoose.model('shipment', shipmentSchema);