import mongoose from 'mongoose'; import { generateId } from '../../utils.js'; const { Schema } = mongoose; const RENDER_DOCUMENT_TEMPLATE_CALL = /fc\.renderDocumentTemplate\s*\(\s*(['"])([^'"]+)\1/g; function extractRenderDocumentTemplateReferences(content) { if (content == null || typeof content !== 'string' || content === '') { return []; } const references = []; const seen = new Set(); const regex = new RegExp(RENDER_DOCUMENT_TEMPLATE_CALL.source, 'g'); let match; while ((match = regex.exec(content)) !== null) { const reference = match[2]?.trim(); if (!reference || seen.has(reference)) { continue; } seen.add(reference); references.push(reference); } return references; } function objectIdsFromFilterValue(value) { if (value == null) { return []; } if (value instanceof mongoose.Types.ObjectId) { return [value]; } if (typeof value === 'string' && /^[a-f\d]{24}$/i.test(value)) { return [new mongoose.Types.ObjectId(value)]; } if (Array.isArray(value)) { return value.flatMap(objectIdsFromFilterValue); } if (typeof value === 'object') { if (Array.isArray(value.$in)) { return objectIdsFromFilterValue(value.$in); } if (value._id != null) { return objectIdsFromFilterValue(value._id); } } return []; } const documentTemplateSchema = new Schema( { _reference: { type: String, default: () => generateId()() }, name: { type: String, required: true, unique: true, }, objectType: { type: String, required: false }, tags: [{ type: String }], active: { type: Boolean, required: true, default: true, }, global: { type: Boolean, required: true, default: false, }, parent: { type: Schema.Types.ObjectId, ref: 'documentTemplate', required: false, }, documentSize: { type: Schema.Types.ObjectId, ref: 'documentSize', required: true, }, documentPrinters: [ { type: Schema.Types.ObjectId, ref: 'documentPrinter', required: false, }, ], referencedTemplates: [ { type: Schema.Types.ObjectId, ref: 'documentTemplate', required: false, }, ], content: { type: String, required: false, default: '', }, testObject: { type: Schema.Types.ObjectId, refPath: 'objectType', required: false, }, }, { timestamps: true } ); documentTemplateSchema.index({ name: 'text', tags: 'text', objectType: 'text' }); // Add virtual id getter documentTemplateSchema.virtual('id').get(function () { return this._id; }); // Configure JSON serialization to include virtuals documentTemplateSchema.set('toJSON', { virtuals: true }); documentTemplateSchema.statics.recalculate = async function (documentTemplate, user) { const documentTemplateId = documentTemplate?._id || documentTemplate; if (!documentTemplateId) { return; } const stillExists = await this.exists({ _id: documentTemplateId }); if (!stillExists) { return; } const { getFilter } = await import('../../../utils.js'); const references = extractRenderDocumentTemplateReferences(documentTemplate?.content); const referencedTemplateIds = []; const seenIds = new Set(); for (const reference of references) { const filter = await getFilter({ parent: reference }, ['parent'], true, this); const ids = objectIdsFromFilterValue(filter.parent); for (const id of ids) { const idString = String(id); if (!idString || seenIds.has(idString)) { continue; } seenIds.add(idString); referencedTemplateIds.push(id); } } if (documentTemplate && typeof documentTemplate === 'object' && !documentTemplate._bsontype) { documentTemplate.referencedTemplates = referencedTemplateIds.map((id) => ({ _id: String(id), })); } const { editObject } = await import('../../database.js'); await editObject({ model: this, id: documentTemplateId, updateData: { referencedTemplates: referencedTemplateIds }, user, populate: [{ path: 'referencedTemplates', strictPopulate: false }], recalculate: false, }); }; export const documentTemplateModel = mongoose.model('documentTemplate', documentTemplateSchema);