/// // // M11 — Create the technicianAssignments collection. // // Each record is a sanitized copy of one RO that an advisor has assigned to a // technician. The assignment carries only the data a technician needs to do // the job — no pricing, no totals, no customer contact details. // // API rule pattern (applied after creation): // shopUserId = @request.auth.id || technicianUserId = @request.auth.id migrate( (db) => { const existing = db.findCollectionByNameOrId('technicianAssignments'); if (existing) { console.log('[M11] technicianAssignments already exists — skipping'); return; } const col = new Collection(); col.name = 'technicianAssignments'; col.type = 'base'; col.system = false; col.listRule = null; col.viewRule = null; col.createRule = null; col.updateRule = null; col.deleteRule = null; // ── Fields ────────────────────────────────────────────────────────────── // shopUserId — owner of the shop that created this assignment const shopUserIdField = new TextField({ name: 'shopUserId', required: true, }); col.fields.add(shopUserIdField); // technicianUserId — the technician who receives this work const techUserIdField = new TextField({ name: 'technicianUserId', required: true, }); col.fields.add(techUserIdField); // repairOrderId — source RO id (plain text, not a relation) const roIdField = new TextField({ name: 'repairOrderId', required: true, }); col.fields.add(roIdField); // roNumber — for display (no relation to repairOrders) const roNumField = new TextField({ name: 'roNumber', required: false, }); col.fields.add(roNumField); // vehicleInfo — e.g. "2018 Ford F-150" const vehicleField = new TextField({ name: 'vehicleInfo', required: false, }); col.fields.add(vehicleField); // vin const vinField = new TextField({ name: 'vin', required: false, }); col.fields.add(vinField); // mileage const mileageField = new TextField({ name: 'mileage', required: false, }); col.fields.add(mileageField); // serviceLocation — job-site address (mobile use-case) const locField = new TextField({ name: 'serviceLocation', required: false, }); col.fields.add(locField); // status — assignment-level job status const statusField = new SelectField({ name: 'status', required: false, values: ['pending', 'in_progress', 'waiting_parts', 'completed'], maxSelect: 1, }); col.fields.add(statusField); // services — sanitized service list (JSON, no pricing) const servicesField = new JSONField({ name: 'services', required: false, }); col.fields.add(servicesField); // internalNotes — technician notes (JSON or plain text) const notesField = new JSONField({ name: 'internalNotes', required: false, }); col.fields.add(notesField); // clockEntries — stopwatch entries (JSON array of ClockEntry) const clockField = new JSONField({ name: 'clockEntries', required: false, }); col.fields.add(clockField); // inspectionChecklist — JSON (future) const checklistField = new JSONField({ name: 'inspectionChecklist', required: false, }); col.fields.add(checklistField); // advisorRequests — messages the technician sends to the advisor const requestsField = new JSONField({ name: 'advisorRequests', required: false, }); col.fields.add(requestsField); db.save(col); console.log('[M11] created technicianAssignments collection'); // ── Apply API rules ─────────────────────────────────────────────────── const saved = db.findCollectionByNameOrId('technicianAssignments'); saved.listRule = 'shopUserId = @request.auth.id || technicianUserId = @request.auth.id'; saved.viewRule = 'shopUserId = @request.auth.id || technicianUserId = @request.auth.id'; saved.createRule = 'shopUserId = @request.auth.id'; saved.updateRule = 'shopUserId = @request.auth.id || technicianUserId = @request.auth.id'; saved.deleteRule = 'shopUserId = @request.auth.id'; db.save(saved); console.log('[M11] applied API rules to technicianAssignments'); }, (db) => { const col = db.findCollectionByNameOrId('technicianAssignments'); if (col) { db.deleteCollection(col); console.log('[M11] deleted technicianAssignments collection'); } } );