Refactor inventory and filament schemas for improved consistency and readability
All checks were successful
farmcontrol/farmcontrol-ws/pipeline/head This commit looks good

- Enhanced the stock event schema by ensuring consistent formatting and removing unnecessary line breaks for better maintainability.
- Updated the filament schema to improve code readability and maintain required fields while maintaining functionality.
- Introduced a new `recalculate` method in the printer schema to manage printer state changes effectively.
- Added logic to handle host disconnection in the socket host, ensuring proper state management and logging.
This commit is contained in:
Tom Butcher 2026-07-26 22:41:09 +01:00
parent f3a3d3914a
commit ef5cfd34e4
4 changed files with 88 additions and 67 deletions

View File

@ -6,13 +6,13 @@ const { Schema } = mongoose;
const parentStockModelNames = {
filamentStock: 'filamentStock',
partStock: 'partStock',
productStock: 'productStock'
productStock: 'productStock',
};
const initialStockStates = {
filamentStock: 'unconsumed',
partStock: 'new',
productStock: 'posted'
productStock: 'posted',
};
const getStartingAmount = (parentType, parentStock) => {
@ -23,12 +23,7 @@ const getStartingAmount = (parentType, parentStock) => {
return parentStock.startingQuantity ?? 0;
};
const buildParentState = (
parentType,
parentStock,
currentAmount,
startingAmount
) => {
const buildParentState = (parentType, parentStock, currentAmount, startingAmount) => {
if (parentStock.state?.type === 'draft') {
return undefined;
}
@ -48,6 +43,8 @@ const buildParentState = (
const progress = currentAmount / startingAmount;
console.log('progress', progress);
if (currentAmount === startingAmount) {
return { ...parentStock.state, type: fullState, progress: 1 };
}
@ -63,20 +60,18 @@ const getStockEventTotal = async (parentId, parentType) => {
if (!parentId) return null;
const objectId =
parentId instanceof mongoose.Types.ObjectId
? parentId
: new mongoose.Types.ObjectId(parentId);
parentId instanceof mongoose.Types.ObjectId ? parentId : new mongoose.Types.ObjectId(parentId);
const [result] = await mongoose
.model('stockEvent')
.aggregate([
{ $match: { parent: objectId, parentType } },
{ $group: { _id: null, total: { $sum: '$value' }, count: { $sum: 1 } } }
{ $group: { _id: null, total: { $sum: '$value' }, count: { $sum: 1 } } },
]);
return {
total: result?.total ?? 0,
count: result?.count ?? 0
count: result?.count ?? 0,
};
};
@ -113,8 +108,7 @@ const HISTORY_RATE_LIMIT_MS = 3000;
const isWithinHistoryRateLimit = (lastEntry, timestamp = new Date()) => {
if (!lastEntry?.timestamp) return false;
const elapsed =
new Date(timestamp).getTime() - new Date(lastEntry.timestamp).getTime();
const elapsed = new Date(timestamp).getTime() - new Date(lastEntry.timestamp).getTime();
return elapsed < HISTORY_RATE_LIMIT_MS;
};
@ -122,9 +116,7 @@ const getLastParentHistoryValue = (parentType, history = []) => {
const lastEntry = history.at(-1);
if (!lastEntry) return undefined;
return parentType === 'filamentStock'
? lastEntry.currentWeight
: lastEntry.currentQuantity;
return parentType === 'filamentStock' ? lastEntry.currentWeight : lastEntry.currentQuantity;
};
const parentValuesEqual = (parentType, a, b) => {
@ -155,9 +147,7 @@ const appendParentHistoryIfChanged = (
const history = parentStock.history || [];
const lastEntry = history.at(-1);
const currentValue =
parentType === 'filamentStock'
? updateData.currentWeight
: updateData.currentQuantity;
parentType === 'filamentStock' ? updateData.currentWeight : updateData.currentQuantity;
const lastHistoryValue = getLastParentHistoryValue(parentType, history);
if (parentValuesEqual(parentType, currentValue, lastHistoryValue)) {
@ -170,10 +160,7 @@ const appendParentHistoryIfChanged = (
return {
...updateData,
history: [
...history,
buildParentHistoryEntry(parentType, currentValue, timestamp)
]
history: [...history, buildParentHistoryEntry(parentType, currentValue, timestamp)],
};
};
@ -187,7 +174,7 @@ const recalculateParentStock = async (parentType, parentId, user) => {
const parentStock = await getObject({
model: parentModel,
id: parentId,
cached: true
cached: true,
});
if (!parentStock || parentStock.error) return;
@ -203,7 +190,7 @@ const recalculateParentStock = async (parentType, parentId, user) => {
buildParentUpdateData(parentType, parentStock, events)
),
user,
recalculate: false
recalculate: false,
});
};
@ -215,30 +202,30 @@ const stockEventSchema = new Schema(
parent: {
type: Schema.Types.ObjectId,
refPath: 'parentType',
required: true
required: true,
},
parentType: {
type: String,
required: true,
enum: ['filamentStock', 'partStock', 'productStock'] // Add other models as needed
enum: ['filamentStock', 'partStock', 'productStock'], // Add other models as needed
},
owner: {
type: Schema.Types.ObjectId,
refPath: 'ownerType',
required: true
required: true,
},
ownerType: {
type: String,
required: true,
enum: ['user', 'subJob', 'stockAudit', 'stockTransfer']
enum: ['user', 'subJob', 'stockAudit', 'stockTransfer'],
},
history: [
{
value: { type: Number, required: true },
timestamp: { type: Date, default: Date.now }
}
timestamp: { type: Date, default: Date.now },
},
],
timestamp: { type: Date, default: Date.now }
timestamp: { type: Date, default: Date.now },
},
{ timestamps: true }
);
@ -252,18 +239,15 @@ stockEventSchema.statics.recalculate = async function (stockEvent, user) {
const currentValue = stockEvent.value;
const timestamp = stockEvent.timestamp || new Date();
if (
currentValue !== lastHistoryValue &&
!isWithinHistoryRateLimit(lastEntry, timestamp)
) {
if (currentValue !== lastHistoryValue && !isWithinHistoryRateLimit(lastEntry, timestamp)) {
await editObject({
model: this,
id: stockEvent._id,
updateData: {
history: [...history, { value: currentValue, timestamp }]
history: [...history, { value: currentValue, timestamp }],
},
user,
recalculate: false
recalculate: false,
});
}

View File

@ -3,28 +3,21 @@ 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 },
vendor: { required: false, type: Schema.Types.ObjectId, ref: 'vendor' },
barcode: { required: false, type: String },
url: { required: false, type: String },
image: { required: false, type: Buffer },
material: { type: Schema.Types.ObjectId, ref: 'material', required: true },
diameter: { required: true, type: Number },
density: { required: true, type: Number },
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 }
);
const filamentSchema = new mongoose.Schema({
_reference: { type: String, default: () => generateId()() },
name: { required: true, type: String },
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: false },
barcode: { required: false, type: String },
url: { required: false, type: String },
image: { required: false, type: Buffer },
material: { type: Schema.Types.ObjectId, ref: 'material', required: true },
diameter: { required: true, type: Number },
density: { required: true, type: Number },
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.index({ name: 'text', barcode: 'text', url: 'text' });
@ -36,10 +29,7 @@ filamentSchema.set('toJSON', { virtuals: true });
filamentSchema.statics.recalculate = async function (filament, user) {
const filamentSkuModel = mongoose.model('filamentSku');
const skus = await filamentSkuModel
.find({ filament: filament._id })
.select('_id')
.lean();
const skus = await filamentSkuModel.find({ filament: filament._id }).select('_id').lean();
for (const sku of skus) {
await filamentSkuModel.recalculate(sku, user);
}

View File

@ -1,7 +1,7 @@
import mongoose from 'mongoose';
import { generateId } from '../../utils.js';
const { Schema } = mongoose;
import { aggregateRollups, aggregateRollupsHistory } from '../../database.js';
import { aggregateRollups, aggregateRollupsHistory, editObject } from '../../database.js';
// Define the moonraker connection schema
const moonrakerSchema = new Schema(
@ -124,6 +124,28 @@ printerSchema.statics.history = async function (from, to) {
return results;
};
printerSchema.statics.recalculate = async function (printer, user) {
if (printer.active === false && printer.state?.type !== 'inactive') {
await editObject({
model: this,
id: printer._id,
updateData: { state: { type: 'inactive' } },
user,
recalculate: false,
});
return;
}
if (printer.active === true && printer.state?.type === 'inactive' && printer.online === false) {
await editObject({
model: this,
id: printer._id,
updateData: { state: { type: 'offline' } },
user,
recalculate: false,
});
}
};
// Add virtual id getter
printerSchema.virtual('id').get(function () {
return this._id;

View File

@ -94,6 +94,25 @@ export class SocketHost {
ownerType: 'host'
});
};
const setHostOffline = async () => {
logger.info('Host disconnected.');
await editObject({
model: hostModel,
id: this.host._id,
updateData: {
online: false,
state: { type: 'offline' },
connectedAt: null
},
owner: this.host,
ownerType: 'host'
});
this.host = null;
this.id = null;
this.authenticated = false;
};
logger.trace('handleAuthenticateEvent');
const id = data.id || undefined;
const authCode = data.authCode || undefined;
@ -103,6 +122,9 @@ export class SocketHost {
logger.info('Authenticating host with id + authCode...');
const verifyResult = await this.codeAuth.verifyCode(id, authCode);
if (verifyResult.valid == true) {
if (this.host?._id) {
await setHostOffline();
}
await setHostOnline(verifyResult);
await this.initializeHost();
}
@ -115,6 +137,9 @@ export class SocketHost {
const verifyResult = await this.codeAuth.verifyOtp(otp);
if (verifyResult.valid == true) {
logger.info('Host authenticated and valid.');
if (this.host?._id) {
await setHostOffline();
}
await setHostOnline(verifyResult);
await this.initializeHost();
}