Compare commits

..

No commits in common. "b2c854bce9f6489eea763ef0dfe15b7d656a9f1b" and "d1dbbe2b112622cb6601e16515ca1cfee3f31ef6" have entirely different histories.

14 changed files with 51 additions and 341 deletions

2
.gitignore vendored
View File

@ -137,5 +137,3 @@ test-results.xml
DS_STORE
**/DS_Store
test-results.xml

View File

@ -19,7 +19,7 @@ const filamentStockSchema = new Schema(
net: { type: Number, required: true },
gross: { type: Number, required: true },
},
filamentSku: { type: mongoose.Schema.Types.ObjectId, ref: 'filamentSku', required: true },
filament: { type: mongoose.Schema.Types.ObjectId, ref: 'filament', required: true },
},
{ timestamps: true }
);

View File

@ -2,12 +2,6 @@ import mongoose from 'mongoose';
import { purchaseOrderModel } from './purchaseorder.schema.js';
import { salesOrderModel } from '../sales/salesorder.schema.js';
import { taxRateModel } from '../management/taxrate.schema.js';
import { filamentModel } from '../management/filament.schema.js';
import { filamentSkuModel } from '../management/filamentsku.schema.js';
import { partModel } from '../management/part.schema.js';
import { partSkuModel } from '../management/partsku.schema.js';
import { productModel } from '../management/product.schema.js';
import { productSkuModel } from '../management/productsku.schema.js';
import {
aggregateRollups,
aggregateRollupsHistory,
@ -17,18 +11,6 @@ import {
import { generateId } from '../../utils.js';
const { Schema } = mongoose;
const skuModelsByItemType = {
filament: filamentSkuModel,
part: partSkuModel,
product: productSkuModel,
};
const parentModelsByItemType = {
filament: filamentModel,
part: partModel,
product: productModel,
};
const orderItemSchema = new Schema(
{
_reference: { type: String, default: () => generateId()() },
@ -39,16 +21,7 @@ const orderItemSchema = new Schema(
},
order: { type: Schema.Types.ObjectId, refPath: 'orderType', required: true },
itemType: { type: String, required: true },
item: { type: Schema.Types.ObjectId, refPath: 'itemType', required: false },
sku: {
type: Schema.Types.ObjectId,
ref: function () {
return ['filament', 'part', 'product'].includes(this.itemType)
? this.itemType + 'Sku'
: null;
},
required: false,
},
item: { type: Schema.Types.ObjectId, refPath: 'itemType', required: true },
syncAmount: { type: String, required: false, default: null },
itemAmount: { type: Number, required: true },
quantity: { type: Number, required: true },
@ -115,55 +88,9 @@ orderItemSchema.statics.recalculate = async function (orderItem, user) {
return;
}
// If SKU present and syncAmount is set, check if override is on for the price mode and use that price instead
let effectiveItemAmount = orderItem.itemAmount;
const syncAmount = orderItem.syncAmount;
const skuId = orderItem.sku?._id || orderItem.sku;
const itemType = orderItem.itemType;
if (syncAmount && skuId && itemType && ['filament', 'part', 'product'].includes(itemType)) {
const skuModel = skuModelsByItemType[itemType];
const parentModel = parentModelsByItemType[itemType];
if (skuModel && parentModel) {
const sku = await getObject({
model: skuModel,
id: skuId,
cached: true,
});
if (sku) {
const parentId = sku.part?._id || sku.part || sku.product?._id || sku.product || sku.filament?._id || sku.filament;
if (syncAmount === 'itemCost') {
if (sku.overrideCost && sku.cost != null) {
effectiveItemAmount = sku.cost;
} else if (parentId) {
const parent = await getObject({
model: parentModel,
id: parentId,
cached: true,
});
if (parent && parent.cost != null) {
effectiveItemAmount = parent.cost;
}
}
} else if (syncAmount === 'itemPrice' && itemType !== 'filament') {
if (sku.overridePrice && sku.price != null) {
effectiveItemAmount = sku.price;
} else if (parentId) {
const parent = await getObject({
model: parentModel,
id: parentId,
cached: true,
});
if (parent && parent.price != null) {
effectiveItemAmount = parent.price;
}
}
}
}
}
}
var taxRate = orderItem.taxRate;
let taxRate = orderItem.taxRate;
if (orderItem.taxRate?._id && Object.keys(orderItem.taxRate).length === 1) {
if (orderItem.taxRate?._id && Object.keys(orderItem.taxRate).length == 1) {
taxRate = await getObject({
model: taxRateModel,
id: orderItem.taxRate._id,
@ -171,25 +98,18 @@ orderItemSchema.statics.recalculate = async function (orderItem, user) {
});
}
const orderTotalAmount = effectiveItemAmount * orderItem.quantity;
const orderTotalAmount = orderItem.itemAmount * orderItem.quantity;
const orderTotalAmountWithTax = orderTotalAmount * (1 + (taxRate?.rate || 0) / 100);
const orderItemUpdateData = {
totalAmount: orderTotalAmount,
totalAmountWithTax: orderTotalAmountWithTax,
invoicedAmountRemaining: orderTotalAmount - orderItem.invoicedAmount,
invoicedAmountWithTaxRemaining: orderTotalAmountWithTax - orderItem.invoicedAmountWithTax,
invoicedQuantityRemaining: orderItem.quantity - orderItem.invoicedQuantity,
};
if (effectiveItemAmount !== orderItem.itemAmount) {
orderItemUpdateData.itemAmount = effectiveItemAmount;
orderItem.itemAmount = effectiveItemAmount;
}
await editObject({
model: this,
model: orderItemModel,
id: orderItem._id,
updateData: orderItemUpdateData,
updateData: {
invoicedAmountRemaining: orderTotalAmount - orderItem.invoicedAmount,
invoicedAmountWithTaxRemaining: orderTotalAmountWithTax - orderItem.invoicedAmountWithTax,
invoicedQuantityRemaining: orderItem.quantity - orderItem.invoicedQuantity,
totalAmount: orderTotalAmount,
totalAmountWithTax: orderTotalAmountWithTax,
},
user,
recalculate: false,
});

View File

@ -11,7 +11,7 @@ const partStockSchema = new Schema(
type: { type: String, required: true },
progress: { type: Number, required: false },
},
partSku: { type: mongoose.Schema.Types.ObjectId, ref: 'partSku', required: true },
part: { type: mongoose.Schema.Types.ObjectId, ref: 'part', required: true },
currentQuantity: { type: Number, required: true },
sourceType: { type: String, required: true },
source: { type: Schema.Types.ObjectId, refPath: 'sourceType', required: true },

View File

@ -5,7 +5,7 @@ import { aggregateRollups, aggregateRollupsHistory } from '../../database.js';
const partStockUsageSchema = new Schema({
partStock: { type: Schema.Types.ObjectId, ref: 'partStock', required: false },
partSku: { type: Schema.Types.ObjectId, ref: 'partSku', required: true },
part: { type: Schema.Types.ObjectId, ref: 'part', required: true },
quantity: { type: Number, required: true },
});
@ -18,7 +18,7 @@ const productStockSchema = new Schema(
progress: { type: Number, required: false },
},
postedAt: { type: Date, required: false },
productSku: { type: mongoose.Schema.Types.ObjectId, ref: 'productSku', required: true },
product: { type: mongoose.Schema.Types.ObjectId, ref: 'product', required: true },
currentQuantity: { type: Number, required: true },
partStocks: [partStockUsageSchema],
},

View File

@ -2,21 +2,24 @@ import mongoose from 'mongoose';
import { generateId } from '../../utils.js';
const { Schema } = mongoose;
// Filament base - cost and tax; color and cost override at FilamentSKU
const filamentSchema = new mongoose.Schema({
_reference: { type: String, default: () => generateId()() },
name: { required: true, type: String },
barcode: { required: false, type: String },
url: { required: false, type: String },
image: { required: false, type: Buffer },
material: { type: Schema.Types.ObjectId, ref: 'material', required: true },
color: { required: true, type: String },
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: true },
type: { required: true, type: String },
cost: { required: true, type: Number },
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: true },
costWithTax: { required: true, type: Number },
diameter: { required: true, type: Number },
density: { required: true, type: Number },
createdAt: { required: true, type: Date },
updatedAt: { required: true, type: Date },
emptySpoolWeight: { required: true, type: Number },
cost: { type: Number, required: false },
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
costWithTax: { type: Number, required: false },
}, { timestamps: true });
});
filamentSchema.virtual('id').get(function () {
return this._id;
@ -24,21 +27,4 @@ filamentSchema.virtual('id').get(function () {
filamentSchema.set('toJSON', { virtuals: true });
filamentSchema.statics.recalculate = async function (filament, user) {
const orderItemModel = mongoose.model('orderItem');
const itemId = filament._id;
const draftOrderItems = await orderItemModel
.find({
'state.type': 'draft',
itemType: 'filament',
item: itemId,
})
.populate('order')
.lean();
for (const orderItem of draftOrderItems) {
await orderItemModel.recalculate(orderItem, user);
}
};
export const filamentModel = mongoose.model('filament', filamentSchema);

View File

@ -1,48 +0,0 @@
import mongoose from 'mongoose';
import { generateId } from '../../utils.js';
const { Schema } = mongoose;
// Define the main filament SKU schema - color and cost live at SKU level
const filamentSkuSchema = new Schema(
{
_reference: { type: String, default: () => generateId()() },
barcode: { type: String, required: false },
filament: { type: Schema.Types.ObjectId, ref: 'filament', required: true },
name: { type: String, required: true },
description: { type: String, required: false },
color: { type: String, required: true },
cost: { type: Number, required: false },
overrideCost: { type: Boolean, default: false },
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
costWithTax: { type: Number, required: false },
},
{ timestamps: true }
);
// Add virtual id getter
filamentSkuSchema.virtual('id').get(function () {
return this._id;
});
// Configure JSON serialization to include virtuals
filamentSkuSchema.set('toJSON', { virtuals: true });
filamentSkuSchema.statics.recalculate = async function (filamentSku, user) {
const orderItemModel = mongoose.model('orderItem');
const skuId = filamentSku._id;
const draftOrderItems = await orderItemModel
.find({
'state.type': 'draft',
itemType: 'filament',
sku: skuId,
})
.populate('order')
.lean();
for (const orderItem of draftOrderItems) {
await orderItemModel.recalculate(orderItem, user);
}
};
// Create and export the model
export const filamentSkuModel = mongoose.model('filamentSku', filamentSkuSchema);

View File

@ -1,15 +1,13 @@
import mongoose from 'mongoose';
import { generateId } from '../../utils.js';
const materialSchema = new mongoose.Schema(
{
_reference: { type: String, default: () => generateId()() },
name: { required: true, type: String },
url: { required: false, type: String },
tags: [{ type: String }],
},
{ timestamps: true }
);
const materialSchema = new mongoose.Schema({
_reference: { type: String, default: () => generateId()() },
name: { required: true, type: String },
url: { required: false, type: String },
image: { required: false, type: Buffer },
tags: [{ type: String }],
});
materialSchema.virtual('id').get(function () {
return this._id;

View File

@ -2,22 +2,22 @@ import mongoose from 'mongoose';
import { generateId } from '../../utils.js';
const { Schema } = mongoose;
// Define the main part schema - cost/price and tax; override at PartSku
// Define the main part schema
const partSchema = new Schema(
{
_reference: { type: String, default: () => generateId()() },
name: { type: String, required: true },
fileName: { type: String, required: false },
file: { type: mongoose.SchemaTypes.ObjectId, ref: 'file', required: false },
cost: { type: Number, required: false },
price: { type: Number, required: false },
priceMode: { type: String, default: 'margin' },
price: { type: Number, required: true },
cost: { type: Number, required: true },
margin: { type: Number, required: false },
amount: { type: Number, required: false },
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: true },
file: { type: mongoose.SchemaTypes.ObjectId, ref: 'file', required: false },
priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
costWithTax: { type: Number, required: false },
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
priceWithTax: { type: Number, required: false },
costWithTax: { type: Number, required: false },
},
{ timestamps: true }
);
@ -30,22 +30,5 @@ partSchema.virtual('id').get(function () {
// Configure JSON serialization to include virtuals
partSchema.set('toJSON', { virtuals: true });
partSchema.statics.recalculate = async function (part, user) {
const orderItemModel = mongoose.model('orderItem');
const itemId = part._id;
const draftOrderItems = await orderItemModel
.find({
'state.type': 'draft',
itemType: 'part',
item: itemId,
})
.populate('order')
.lean();
for (const orderItem of draftOrderItems) {
await orderItemModel.recalculate(orderItem, user);
}
};
// Create and export the model
export const partModel = mongoose.model('part', partSchema);

View File

@ -1,54 +0,0 @@
import mongoose from 'mongoose';
import { generateId } from '../../utils.js';
const { Schema } = mongoose;
// Define the main part SKU schema - pricing lives at SKU level
const partSkuSchema = new Schema(
{
_reference: { type: String, default: () => generateId()() },
barcode: { type: String, required: false },
part: { type: Schema.Types.ObjectId, ref: 'part', required: true },
name: { type: String, required: true },
description: { type: String, required: false },
priceMode: { type: String, default: 'margin' },
price: { type: Number, required: false },
cost: { type: Number, required: false },
overrideCost: { type: Boolean, default: false },
overridePrice: { type: Boolean, default: false },
margin: { type: Number, required: false },
amount: { type: Number, required: false },
priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
priceWithTax: { type: Number, required: false },
costWithTax: { type: Number, required: false },
},
{ timestamps: true }
);
// Add virtual id getter
partSkuSchema.virtual('id').get(function () {
return this._id;
});
// Configure JSON serialization to include virtuals
partSkuSchema.set('toJSON', { virtuals: true });
partSkuSchema.statics.recalculate = async function (partSku, user) {
const orderItemModel = mongoose.model('orderItem');
const skuId = partSku._id;
const draftOrderItems = await orderItemModel
.find({
'state.type': 'draft',
itemType: 'part',
sku: skuId,
})
.populate('order')
.lean();
for (const orderItem of draftOrderItems) {
await orderItemModel.recalculate(orderItem, user);
}
};
// Create and export the model
export const partSkuModel = mongoose.model('partSku', partSkuSchema);

View File

@ -2,6 +2,11 @@ import mongoose from 'mongoose';
import { generateId } from '../../utils.js';
const { Schema } = mongoose;
const partSchema = new Schema({
part: { type: Schema.Types.ObjectId, ref: 'part', required: true },
quantity: { type: Number, required: true },
});
// Define the main product schema
const productSchema = new Schema(
{
@ -9,16 +14,13 @@ const productSchema = new Schema(
name: { type: String, required: true },
tags: [{ type: String }],
version: { type: String },
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: true },
cost: { type: Number, required: false },
price: { type: Number, required: false },
priceMode: { type: String, default: 'margin' },
margin: { type: Number, required: false },
amount: { type: Number, required: false },
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: true },
parts: [partSchema],
priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
costWithTax: { type: Number, required: false },
priceWithTax: { type: Number, required: false },
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
},
{ timestamps: true }
);
@ -30,22 +32,5 @@ productSchema.virtual('id').get(function () {
// Configure JSON serialization to include virtuals
productSchema.set('toJSON', { virtuals: true });
productSchema.statics.recalculate = async function (product, user) {
const orderItemModel = mongoose.model('orderItem');
const itemId = product._id;
const draftOrderItems = await orderItemModel
.find({
'state.type': 'draft',
itemType: 'product',
item: itemId,
})
.populate('order')
.lean();
for (const orderItem of draftOrderItems) {
await orderItemModel.recalculate(orderItem, user);
}
};
// Create and export the model
export const productModel = mongoose.model('product', productSchema);

View File

@ -2,31 +2,14 @@ import mongoose from 'mongoose';
import { generateId } from '../../utils.js';
const { Schema } = mongoose;
const partSkuUsageSchema = new Schema({
partSku: { type: Schema.Types.ObjectId, ref: 'partSku', required: true },
quantity: { type: Number, required: true },
});
// Define the main product SKU schema
const productSkuSchema = new Schema(
{
_reference: { type: String, default: () => generateId()() },
barcode: { type: String, required: false },
sku: { type: String, required: true },
product: { type: Schema.Types.ObjectId, ref: 'product', required: true },
name: { type: String, required: true },
name: { type: String, required: false },
description: { type: String, required: false },
priceMode: { type: String, default: 'margin' },
price: { type: Number, required: false },
cost: { type: Number, required: false },
overrideCost: { type: Boolean, default: false },
overridePrice: { type: Boolean, default: false },
margin: { type: Number, required: false },
amount: { type: Number, required: false },
parts: [partSkuUsageSchema],
priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
priceWithTax: { type: Number, required: false },
costWithTax: { type: Number, required: false },
},
{ timestamps: true }
);
@ -39,22 +22,5 @@ productSkuSchema.virtual('id').get(function () {
// Configure JSON serialization to include virtuals
productSkuSchema.set('toJSON', { virtuals: true });
productSkuSchema.statics.recalculate = async function (productSku, user) {
const orderItemModel = mongoose.model('orderItem');
const skuId = productSku._id;
const draftOrderItems = await orderItemModel
.find({
'state.type': 'draft',
itemType: 'product',
sku: skuId,
})
.populate('order')
.lean();
for (const orderItem of draftOrderItems) {
await orderItemModel.recalculate(orderItem, user);
}
};
// Create and export the model
export const productSkuModel = mongoose.model('productSku', productSkuSchema);

View File

@ -2,14 +2,11 @@ import { jobModel } from './production/job.schema.js';
import { subJobModel } from './production/subjob.schema.js';
import { printerModel } from './production/printer.schema.js';
import { filamentModel } from './management/filament.schema.js';
import { filamentSkuModel } from './management/filamentsku.schema.js';
import { gcodeFileModel } from './production/gcodefile.schema.js';
import { partModel } from './management/part.schema.js';
import { partSkuModel } from './management/partsku.schema.js';
import { productModel } from './management/product.schema.js';
import { productSkuModel } from './management/productsku.schema.js';
import { vendorModel } from './management/vendor.schema.js';
import { materialModel } from './management/material.schema.js';
import { filamentStockModel } from './inventory/filamentstock.schema.js';
import { purchaseOrderModel } from './inventory/purchaseorder.schema.js';
import { orderItemModel } from './inventory/orderitem.schema.js';
@ -55,13 +52,6 @@ export const models = {
referenceField: '_reference',
label: 'Filament',
},
FSU: {
model: filamentSkuModel,
idField: '_id',
type: 'filamentSku',
referenceField: '_reference',
label: 'Filament SKU',
},
GCF: {
model: gcodeFileModel,
idField: '_id',
@ -77,13 +67,6 @@ export const models = {
referenceField: '_reference',
label: 'Part',
},
PSU: {
model: partSkuModel,
idField: '_id',
type: 'partSku',
referenceField: '_reference',
label: 'Part SKU',
},
PRD: {
model: productModel,
idField: '_id',
@ -91,7 +74,7 @@ export const models = {
referenceField: '_reference',
label: 'Product',
},
SKU: {
PSK: {
model: productSkuModel,
idField: '_id',
type: 'productSku',
@ -105,13 +88,6 @@ export const models = {
referenceField: '_reference',
label: 'Vendor',
},
MAT: {
model: materialModel,
idField: '_id',
type: 'material',
referenceField: '_reference',
label: 'Material',
},
SJB: {
model: subJobModel,
idField: '_id',

View File

@ -13,7 +13,7 @@ const gcodeFileSchema = new mongoose.Schema(
name: { required: true, type: String },
gcodeFileName: { required: false, type: String },
size: { type: Number, required: false },
filamentSku: { type: Schema.Types.ObjectId, ref: 'filamentSku', required: true },
filament: { type: Schema.Types.ObjectId, ref: 'filament', required: true },
parts: [partSchema],
file: { type: mongoose.SchemaTypes.ObjectId, ref: 'file', required: false },
cost: { type: Number, required: false },