Updated models inline with API.
This commit is contained in:
parent
50221a0228
commit
8eafc81321
98
src/database/schemas/finance/invoice.schema.js
Normal file
98
src/database/schemas/finance/invoice.schema.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
import { generateId } from '../../utils.js';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
import { aggregateRollups, aggregateRollupsHistory } from '../../database.js';
|
||||||
|
|
||||||
|
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 },
|
||||||
|
timestamp: { type: Date, default: Date.now },
|
||||||
|
invoiceDate: { type: Date, required: false },
|
||||||
|
dueDate: { type: Date, required: false },
|
||||||
|
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: false },
|
||||||
|
customer: { type: Schema.Types.ObjectId, ref: 'customer', required: false },
|
||||||
|
invoiceType: { type: String, required: true, default: 'sales', enum: ['sales', 'purchase'] },
|
||||||
|
relatedOrderType: { type: String, required: false },
|
||||||
|
relatedOrder: { type: Schema.Types.ObjectId, refPath: 'relatedOrderType', required: false },
|
||||||
|
state: {
|
||||||
|
type: { type: String, required: true, default: 'draft' },
|
||||||
|
},
|
||||||
|
sentAt: { type: Date, required: false },
|
||||||
|
paidAt: { type: Date, required: false },
|
||||||
|
cancelledAt: { type: Date, required: false },
|
||||||
|
overdueAt: { type: Date, required: false },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const rollupConfigs = [
|
||||||
|
{
|
||||||
|
name: 'draft',
|
||||||
|
filter: { 'state.type': 'draft' },
|
||||||
|
rollups: [{ name: 'draft', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sent',
|
||||||
|
filter: { 'state.type': 'sent' },
|
||||||
|
rollups: [{ name: 'sent', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'partiallyPaid',
|
||||||
|
filter: { 'state.type': 'partiallyPaid' },
|
||||||
|
rollups: [{ name: 'partiallyPaid', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'paid',
|
||||||
|
filter: { 'state.type': 'paid' },
|
||||||
|
rollups: [{ name: 'paid', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'overdue',
|
||||||
|
filter: { 'state.type': 'overdue' },
|
||||||
|
rollups: [{ name: 'overdue', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cancelled',
|
||||||
|
filter: { 'state.type': 'cancelled' },
|
||||||
|
rollups: [{ name: 'cancelled', 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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);
|
||||||
@ -1,6 +1,12 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import { purchaseOrderModel } from './purchaseorder.schema.js';
|
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';
|
import { generateId } from '../../utils.js';
|
||||||
const { Schema } = mongoose;
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
@ -8,20 +14,64 @@ const orderItemSchema = new Schema(
|
|||||||
{
|
{
|
||||||
_reference: { type: String, default: () => generateId()() },
|
_reference: { type: String, default: () => generateId()() },
|
||||||
orderType: { type: String, required: true },
|
orderType: { type: String, required: true },
|
||||||
|
state: {
|
||||||
|
type: { type: String, required: true, default: 'draft' },
|
||||||
|
},
|
||||||
order: { type: Schema.Types.ObjectId, refPath: 'orderType', required: true },
|
order: { type: Schema.Types.ObjectId, refPath: 'orderType', required: true },
|
||||||
itemType: { type: String, required: true },
|
itemType: { type: String, required: true },
|
||||||
item: { type: Schema.Types.ObjectId, refPath: 'itemType', 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 },
|
itemAmount: { type: Number, required: true },
|
||||||
quantity: { type: Number, required: true },
|
quantity: { type: Number, required: true },
|
||||||
totalAmount: { type: Number, required: true },
|
totalAmount: { type: Number, required: true },
|
||||||
taxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
taxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
||||||
totalAmountWithTax: { type: Number, required: true },
|
totalAmountWithTax: { type: Number, required: true },
|
||||||
timestamp: { type: Date, default: Date.now },
|
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 }
|
{ 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) {
|
orderItemSchema.statics.recalculate = async function (orderItem, user) {
|
||||||
// Only purchase orders are supported for now
|
// Only purchase orders are supported for now
|
||||||
if (orderItem.orderType !== 'purchaseOrder') {
|
if (orderItem.orderType !== 'purchaseOrder') {
|
||||||
@ -33,6 +83,29 @@ orderItemSchema.statics.recalculate = async function (orderItem, user) {
|
|||||||
return;
|
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({
|
const rollupResults = await aggregateRollups({
|
||||||
model: this,
|
model: this,
|
||||||
baseFilter: {
|
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 totals = rollupResults.orderTotals || {};
|
||||||
const totalAmount = totals.totalAmount.sum?.toFixed(2) || 0;
|
const totalAmount = totals.totalAmount.sum?.toFixed(2) || 0;
|
||||||
const totalAmountWithTax = totals.totalAmountWithTax.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({
|
await editObject({
|
||||||
model: purchaseOrderModel,
|
model: purchaseOrderModel,
|
||||||
id: orderId,
|
id: orderId,
|
||||||
updateData: {
|
updateData: updateData,
|
||||||
totalAmount: parseFloat(totalAmount),
|
|
||||||
totalAmountWithTax: parseFloat(totalAmountWithTax),
|
|
||||||
totalTaxAmount: parseFloat(totalAmountWithTax - totalAmount),
|
|
||||||
},
|
|
||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,22 +1,100 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import { generateId } from '../../utils.js';
|
import { generateId } from '../../utils.js';
|
||||||
const { Schema } = mongoose;
|
const { Schema } = mongoose;
|
||||||
|
import { aggregateRollups, aggregateRollupsHistory } from '../../database.js';
|
||||||
|
|
||||||
const purchaseOrderSchema = new Schema(
|
const purchaseOrderSchema = new Schema(
|
||||||
{
|
{
|
||||||
_reference: { type: String, default: () => generateId()() },
|
_reference: { type: String, default: () => generateId()() },
|
||||||
totalAmount: { type: Number, required: true },
|
totalAmount: { type: Number, required: true, default: 0 },
|
||||||
totalAmountWithTax: { type: Number, required: true },
|
totalAmountWithTax: { type: Number, required: true, default: 0 },
|
||||||
totalTaxAmount: { type: Number, required: true },
|
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 },
|
||||||
timestamp: { type: Date, default: Date.now },
|
timestamp: { type: Date, default: Date.now },
|
||||||
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: true },
|
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: true },
|
||||||
state: {
|
state: {
|
||||||
type: { type: String, required: true, default: 'draft' },
|
type: { type: String, required: true, default: 'draft' },
|
||||||
},
|
},
|
||||||
|
postedAt: { type: Date, required: false },
|
||||||
|
acknowledgedAt: { type: Date, required: false },
|
||||||
|
cancelledAt: { type: Date, required: false },
|
||||||
|
completedAt: { type: Date, required: false },
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const rollupConfigs = [
|
||||||
|
{
|
||||||
|
name: 'draft',
|
||||||
|
filter: { 'state.type': 'draft' },
|
||||||
|
rollups: [{ name: 'draft', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sent',
|
||||||
|
filter: { 'state.type': 'sent' },
|
||||||
|
rollups: [{ name: 'sent', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'acknowledged',
|
||||||
|
filter: { 'state.type': 'acknowledged' },
|
||||||
|
rollups: [{ name: 'acknowledged', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'partiallyShipped',
|
||||||
|
filter: { 'state.type': 'partiallyShipped' },
|
||||||
|
rollups: [{ name: 'partiallyShipped', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'shipped',
|
||||||
|
filter: { 'state.type': 'shipped' },
|
||||||
|
rollups: [{ name: 'shipped', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'partiallyReceived',
|
||||||
|
filter: { 'state.type': 'partiallyReceived' },
|
||||||
|
rollups: [{ name: 'partiallyReceived', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'received',
|
||||||
|
filter: { 'state.type': 'received' },
|
||||||
|
rollups: [{ name: 'received', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cancelled',
|
||||||
|
filter: { 'state.type': 'cancelled' },
|
||||||
|
rollups: [{ name: 'cancelled', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'completed',
|
||||||
|
filter: { 'state.type': 'completed' },
|
||||||
|
rollups: [{ name: 'completed', property: 'state.type', operation: 'count' }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
purchaseOrderSchema.statics.stats = async function () {
|
||||||
|
const results = await aggregateRollups({
|
||||||
|
model: this,
|
||||||
|
rollupConfigs: rollupConfigs,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Transform the results to match the expected format
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
purchaseOrderSchema.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;
|
||||||
|
};
|
||||||
|
|
||||||
// Add virtual id getter
|
// Add virtual id getter
|
||||||
purchaseOrderSchema.virtual('id').get(function () {
|
purchaseOrderSchema.virtual('id').get(function () {
|
||||||
return this._id;
|
return this._id;
|
||||||
|
|||||||
@ -1,43 +1,108 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import { generateId } from '../../utils.js';
|
import { generateId } from '../../utils.js';
|
||||||
const { Schema } = mongoose;
|
const { Schema } = mongoose;
|
||||||
|
import { purchaseOrderModel } from './purchaseorder.schema.js';
|
||||||
const shipmentItemSchema = new Schema({
|
import { taxRateModel } from '../management/taxrate.schema.js';
|
||||||
itemType: { type: String, required: true },
|
import { aggregateRollups, editObject, getObject } from '../../database.js';
|
||||||
item: { type: Schema.Types.ObjectId, refPath: 'itemType', required: true },
|
|
||||||
quantity: { type: Number, required: true },
|
|
||||||
itemCost: { type: Number, required: true },
|
|
||||||
totalCost: { type: Number, required: true },
|
|
||||||
totalCostWithTax: { type: Number, required: true },
|
|
||||||
taxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
|
||||||
});
|
|
||||||
|
|
||||||
const shipmentSchema = new Schema(
|
const shipmentSchema = new Schema(
|
||||||
{
|
{
|
||||||
_reference: { type: String, default: () => generateId()() },
|
_reference: { type: String, default: () => generateId()() },
|
||||||
purchaseOrder: { type: Schema.Types.ObjectId, ref: 'purchaseOrder', required: true },
|
orderType: { type: String, required: true },
|
||||||
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: true },
|
order: { type: Schema.Types.ObjectId, refPath: 'orderType', required: true },
|
||||||
courierService: { type: Schema.Types.ObjectId, ref: 'courierService', required: false },
|
courierService: { type: Schema.Types.ObjectId, ref: 'courierService', required: false },
|
||||||
trackingNumber: { type: String, required: false },
|
trackingNumber: { type: String, required: false },
|
||||||
items: [shipmentItemSchema],
|
amount: { type: Number, required: true },
|
||||||
cost: { net: { type: Number, required: true }, gross: { type: Number, required: true } },
|
amountWithTax: { type: Number, required: true },
|
||||||
shippedDate: { type: Date, required: false },
|
taxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
||||||
expectedDeliveryDate: { type: Date, required: false },
|
shippedAt: { type: Date, required: false },
|
||||||
actualDeliveryDate: { type: Date, required: false },
|
expectedAt: { type: Date, required: false },
|
||||||
|
deliveredAt: { type: Date, required: false },
|
||||||
|
cancelledAt: { type: Date, required: false },
|
||||||
state: {
|
state: {
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
default: 'pending',
|
|
||||||
enum: ['pending', 'shipped', 'in_transit', 'delivered', 'cancelled'],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
notes: { type: String },
|
|
||||||
timestamp: { type: Date, default: Date.now },
|
|
||||||
},
|
},
|
||||||
{ timestamps: 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 = shipment.amount * (1 + (taxRate?.rate || 0) / 100);
|
||||||
|
await editObject({
|
||||||
|
model: shipmentModel,
|
||||||
|
id: shipment._id,
|
||||||
|
updateData: {
|
||||||
|
amountWithTax: amountWithTax,
|
||||||
|
},
|
||||||
|
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
|
// Add virtual id getter
|
||||||
shipmentSchema.virtual('id').get(function () {
|
shipmentSchema.virtual('id').get(function () {
|
||||||
return this._id;
|
return this._id;
|
||||||
|
|||||||
25
src/database/schemas/management/taxrate.schema.js
Normal file
25
src/database/schemas/management/taxrate.schema.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
import { generateId } from '../../utils.js';
|
||||||
|
|
||||||
|
const taxRateSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
_reference: { type: String, default: () => generateId()() },
|
||||||
|
name: { required: true, type: String },
|
||||||
|
rate: { required: true, type: Number },
|
||||||
|
rateType: { required: true, type: String, enum: ['percentage', 'fixed'] },
|
||||||
|
active: { required: true, type: Boolean, default: true },
|
||||||
|
description: { required: false, type: String },
|
||||||
|
country: { required: false, type: String },
|
||||||
|
effectiveFrom: { required: false, type: Date },
|
||||||
|
effectiveTo: { required: false, type: Date },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
taxRateSchema.virtual('id').get(function () {
|
||||||
|
return this._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
taxRateSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const taxRateModel = mongoose.model('taxRate', taxRateSchema);
|
||||||
@ -24,8 +24,10 @@ import { documentJobModel } from './management/documentjob.schema.js';
|
|||||||
import { fileModel } from './management/file.schema.js';
|
import { fileModel } from './management/file.schema.js';
|
||||||
import { courierServiceModel } from './management/courierservice.schema.js';
|
import { courierServiceModel } from './management/courierservice.schema.js';
|
||||||
import { courierModel } from './management/courier.schema.js';
|
import { courierModel } from './management/courier.schema.js';
|
||||||
import { taxRateModel } from './management/taxrates.schema.js';
|
import { taxRateModel } from './management/taxrate.schema.js';
|
||||||
import { taxRecordModel } from './management/taxrecord.schema.js';
|
import { taxRecordModel } from './management/taxrecord.schema.js';
|
||||||
|
import { shipmentModel } from './inventory/shipment.schema.js';
|
||||||
|
import { invoiceModel } from './finance/invoice.schema.js';
|
||||||
|
|
||||||
// Map prefixes to models and id fields
|
// Map prefixes to models and id fields
|
||||||
export const models = {
|
export const models = {
|
||||||
@ -98,4 +100,6 @@ export const models = {
|
|||||||
COR: { model: courierModel, idField: '_id', type: 'courier', referenceField: '_reference' },
|
COR: { model: courierModel, idField: '_id', type: 'courier', referenceField: '_reference' },
|
||||||
TXR: { model: taxRateModel, idField: '_id', type: 'taxRate', referenceField: '_reference' },
|
TXR: { model: taxRateModel, idField: '_id', type: 'taxRate', referenceField: '_reference' },
|
||||||
TXD: { model: taxRecordModel, idField: '_id', type: 'taxRecord', referenceField: '_reference' },
|
TXD: { model: taxRecordModel, idField: '_id', type: 'taxRecord', referenceField: '_reference' },
|
||||||
|
SHP: { model: shipmentModel, idField: '_id', type: 'shipment', referenceField: '_reference' },
|
||||||
|
INV: { model: invoiceModel, idField: '_id', type: 'invoice', referenceField: '_reference' },
|
||||||
};
|
};
|
||||||
|
|||||||
@ -596,12 +596,20 @@ export class TemplateManager {
|
|||||||
return dayjs(date).format(format);
|
return dayjs(date).format(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getObject(objectType, id) {
|
async getObject(objectType = undefined, id = undefined, populate = []) {
|
||||||
|
if (objectType == undefined) {
|
||||||
|
logger.warn('Object type is required');
|
||||||
|
return { error: 'Object type is required' };
|
||||||
|
}
|
||||||
|
if (id == undefined) {
|
||||||
|
logger.warn('Object ID is required');
|
||||||
|
return { error: 'Object ID is required' };
|
||||||
|
}
|
||||||
const model = getModelByName(objectType);
|
const model = getModelByName(objectType);
|
||||||
if (model == undefined) {
|
if (model == undefined) {
|
||||||
throw new Error('Farm Control: Object type not found.');
|
logger.warn('Object type not found:', objectType);
|
||||||
}
|
}
|
||||||
const object = await getObject({ model, id, cached: true });
|
const object = await getObject({ model, id, cached: true, populate });
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user