initial commit
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
/// <reference path="../pb_data/types.d.ts" />
|
||||
//
|
||||
// M7 — Add structured VIN and duration fields to appointments.
|
||||
//
|
||||
// Older scan imports stored these values inside `notes` as:
|
||||
// VIN: <value> | Duration: <number> min
|
||||
// This migration adds first-class columns, backfills them when missing, and
|
||||
// removes those machine-generated note fragments while preserving any real text.
|
||||
//
|
||||
|
||||
function compactNoteParts(raw) {
|
||||
return String(raw || '')
|
||||
.split('|')
|
||||
.map((part) => part.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
migrate(
|
||||
(db) => {
|
||||
const col = db.findCollectionByNameOrId('appointments');
|
||||
if (!col) {
|
||||
console.warn('[M7] appointments collection not found — skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!col.fields.find((f) => f.name === 'vin')) {
|
||||
col.fields.push(new TextField({ name: 'vin', required: false }));
|
||||
db.save(col);
|
||||
console.log('[M7] added vin field to appointments');
|
||||
}
|
||||
|
||||
if (!col.fields.find((f) => f.name === 'durationMinutes')) {
|
||||
col.fields.push(new NumberField({
|
||||
name: 'durationMinutes',
|
||||
required: false,
|
||||
min: 0,
|
||||
}));
|
||||
db.save(col);
|
||||
console.log('[M7] added durationMinutes field to appointments');
|
||||
}
|
||||
|
||||
const records = Array.from(db.findRecordsByFilter('appointments', '1=1', 'id', 500));
|
||||
let backfilled = 0;
|
||||
|
||||
for (const rec of records) {
|
||||
const rawNotes = String(rec.get('notes') || '');
|
||||
const vinMatch = rawNotes.match(/(?:^|\|)\s*VIN:\s*([^|]+?)\s*(?=\||$)/i);
|
||||
const durationMatch = rawNotes.match(/(?:^|\|)\s*Duration:\s*(\d+)\s*min\s*(?=\||$)/i);
|
||||
const nextVin = String(rec.get('vin') || '').trim();
|
||||
const rawDuration = rec.get('durationMinutes');
|
||||
const nextDuration = rawDuration == null || rawDuration === '' ? null : Number(rawDuration);
|
||||
|
||||
let changed = false;
|
||||
|
||||
if (!nextVin && vinMatch?.[1]) {
|
||||
rec.set('vin', String(vinMatch[1]).trim());
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ((nextDuration == null || Number.isNaN(nextDuration)) && durationMatch?.[1]) {
|
||||
rec.set('durationMinutes', Number(durationMatch[1]));
|
||||
changed = true;
|
||||
}
|
||||
|
||||
const cleanedNotes = compactNoteParts(rawNotes)
|
||||
.filter((part) => !/^VIN:\s*/i.test(part) && !/^Duration:\s*\d+\s*min$/i.test(part))
|
||||
.join(' | ');
|
||||
|
||||
if (cleanedNotes !== rawNotes.trim()) {
|
||||
rec.set('notes', cleanedNotes);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
db.saveRecord(rec);
|
||||
backfilled++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[M7] updated ${backfilled} appointment record(s)`);
|
||||
},
|
||||
(db) => {
|
||||
const col = db.findCollectionByNameOrId('appointments');
|
||||
if (!col) return;
|
||||
|
||||
const durationIdx = col.fields.findIndex((f) => f.name === 'durationMinutes');
|
||||
if (durationIdx >= 0) {
|
||||
col.fields.splice(durationIdx, 1);
|
||||
db.save(col);
|
||||
}
|
||||
|
||||
const vinIdx = col.fields.findIndex((f) => f.name === 'vin');
|
||||
if (vinIdx >= 0) {
|
||||
col.fields.splice(vinIdx, 1);
|
||||
db.save(col);
|
||||
}
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user