Salta el contingut

Migracions de schema

Guia per afegir, provar i diagnosticar migracions d'estructura de la base de dades (Google Sheets).


Conceptes bàsics

El sistema de migracions garanteix que totes les instal·lacions —noves i existents— tinguin sempre l'estructura de fulls correcta, sense perdre dades.

Element Ubicació Descripció
CURRENT_SCHEMA_VERSION Migration.gs Versió objectiu. Font de veritat.
SCHEMA_VERSION PropertiesService Versió actual de la instal·lació.
checkAndMigrate() Migration.gs S'executa a cada doGet(). Aplica les migracions pendents.
MIGRATIONS Migration.gs Array ordenat de migracions registrades.

Quan s'executa

checkAndMigrate() és cridat per doGet() a cada càrrega de la webapp. El cost habitual és mínim: una lectura de PropertiesService i una comparació d'enters. Només executa codi quan SCHEMA_VERSION < CURRENT_SCHEMA_VERSION.

Tres tipus d'instal·lació

Instal·lació nova       → Setup.gs estableix SCHEMA_VERSION = CURRENT_SCHEMA_VERSION
                          checkAndMigrate() no fa res.

Instal·lació existent   → SCHEMA_VERSION és null (anterior al sistema de migracions)
(pre-migracions)          checkAndMigrate() la marca com a v1 i comprova migracions posteriors.

Instal·lació desactualitzada → SCHEMA_VERSION < CURRENT_SCHEMA_VERSION
                               checkAndMigrate() aplica les migracions pendents en ordre.

Quan cal una migració

Cal crear una migració quan el canvi afecta l'estructura del Sheets:

Canvi Cal migració?
Nova columna a un full existent
Nou full (nova pestanya al Sheets)
Nova clau al full Config
Canvi de codi .gs o .html
Canvi d'estil o traducció
Modificació de dades existents ❌ *

* Les migracions han de ser additives: mai modifiquen ni eliminen dades o columnes existents. Si cal reorganitzar dades, documenta el procediment manual a les notes de la versió.


Com afegir una migració — pas a pas

Pas 1 — Escriu la funció de migració

A Migration.gs, afegeix una nova funció privada al final del fitxer:

function _migrate_v2() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();

  // Usa les utilitats disponibles (idempotents):
  _ensureColumn(ss.getSheetByName('Usuarios'), 'Departament');
  _ensureConfigKey(ss, 'nova_clau', 'valor_defecte', 'Descripció de la clau');
  // _ensureSheet(ss, 'NouFull', ['Col1', 'Col2'], '#4f46e5');
}

Regles obligatòries per a la funció

  • Additiva: usa _ensureColumn, _ensureSheet, _ensureConfigKey — mai deleteSheet, deleteColumn ni sobreescriptures de dades.
  • Idempotent: si s'executa dues vegades, el resultat ha de ser idèntic. Les utilitats ja ho garanteixen.
  • Aïllada: no crida altres funcions de negoci (getReservas, etc.). Treballa directament amb l'objecte Spreadsheet.

Pas 2 — Registra la migració a l'array MIGRATIONS

const MIGRATIONS = [
  { version: 2,
    description: 'Afegeix columna Departament a Usuarios i clau nova_clau a Config',
    fn: _migrate_v2 },
];

Ordre és obligatori

Les entrades han d'estar ordenades per version de menor a major. Mai eliminis ni reordenis entrades existents.


Pas 3 — Incrementa CURRENT_SCHEMA_VERSION

// Abans:
const CURRENT_SCHEMA_VERSION = 1;

// Després:
const CURRENT_SCHEMA_VERSION = 2;

Pas 4 — Actualitza el comentari d'historial

/**
 * Historial:
 *   v1 — Schema inicial: Recursos, Tramos, Disponibilidad...
 *   v2 — Afegeix columna Departament a Usuarios; nova clau a Config
 */
const CURRENT_SCHEMA_VERSION = 2;

Pas 5 — Puja el codi

clasp push

La migració s'aplicarà automàticament la propera vegada que qualsevol usuari carregui la webapp. No cal cap acció manual a les instal·lacions existents.


Pas 6 — Verifica l'execució als logs

A l'editor d'Apps Script: Execucions (menú lateral) → filtra per doGet → busca les entrades [Migration]:

[Migration] Schema actual: v1 → objectiu: v2
[Migration] Aplicant v2: Afegeix columna Departament a Usuarios...
[Migration] v2 completada.

Si la migració s'ha aplicat correctament, les execucions posteriors no mostraran res relacionat amb migracions (retorn immediat).


Referència: utilitats disponibles

_ensureColumn(sheet, columnName)

Afegeix una columna al final del full si la capçalera no existeix.

const sheet = ss.getSheetByName('Usuarios');
_ensureColumn(sheet, 'Departament');
// Si 'Departament' ja existeix → no fa res
// Si no existeix → l'afegeix a la primera columna buida

_ensureSheet(ss, sheetName, headers, color)

Crea un full nou amb capçaleres formatades si el full no existeix.

_ensureSheet(ss, 'NouFull', ['ID', 'Nom', 'Estat'], '#4f46e5');
// Si 'NouFull' ja existeix → no fa res
// Si no existeix → el crea amb les capçaleres formatades
Paràmetre Tipus Descripció
ss Spreadsheet SpreadsheetApp.getActiveSpreadsheet()
sheetName string Nom de la pestanya
headers string[] Noms de les columnes
color string (opcional) Color hex de la capçalera (p. ex. '#4f46e5')

_ensureConfigKey(ss, key, defaultValue, description)

Afegeix una nova clau al full Config si no existeix.

_ensureConfigKey(ss, 'nova_funcionalitat', 'FALSE', 'Activa la nova funcionalitat');
// Si la clau ja existeix → no fa res (no sobreescriu el valor de l'usuari)
// Si no existeix → afegeix una fila nova al final del full Config

Per a valors booleans

Usa strings 'TRUE' / 'FALSE' (no booleans JS), perquè el full Config llegeix tot com a text.


Exemple complet

Escenari: la versió 3 afegeix una nova pestanya Avisos per a comunicats als usuaris i una nova clau de configuració avisos_activats.

// ── Migration.gs ──────────────────────────────────────────────────────────

/**
 * Historial:
 *   v1 — Schema inicial
 *   v2 — Afegeix columna Departament a Usuarios
 *   v3 — Nou full Avisos; nova clau avisos_activats a Config
 */
const CURRENT_SCHEMA_VERSION = 3;

const MIGRATIONS = [
  { version: 2,
    description: 'Afegeix columna Departament a Usuarios',
    fn: _migrate_v2 },
  { version: 3,
    description: 'Nou full Avisos i clau avisos_activats a Config',
    fn: _migrate_v3 },
];

function _migrate_v2() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  _ensureColumn(ss.getSheetByName('Usuarios'), 'Departament');
}

function _migrate_v3() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  _ensureSheet(ss, 'Avisos',
    ['ID_Avis', 'Titol', 'Missatge', 'Data_Inici', 'Data_Fi', 'Actiu'],
    '#0891b2'
  );
  _ensureConfigKey(ss, 'avisos_activats', 'FALSE',
    'Mostrar avisos a la pantalla principal'
  );
}

Resultat per a cada tipus d'instal·lació:

Instal·lació SCHEMA_VERSION inicial Migracions executades
Nova (crea des de zero) 3 (Setup.gs) Cap
Existent a v2 2 _migrate_v3
Existent a v1 1 _migrate_v2, _migrate_v3
Pre-migracions (null) Detectat com 13 _migrate_v2, _migrate_v3

Si la migració falla

Diagnòstic ràpid

  1. A l'editor Apps Script: Execucions → troba l'execució fallida → expandeix el log
  2. Busca [Migration] ERROR a vN: — el missatge d'error és el punt de partida

La migració s'ha aplicat parcialment

Si _migrate_vN() ha fallat a meitat, SCHEMA_VERSION no s'ha actualitzat (el runner actualitza la versió després d'una execució reeixida). La propera càrrega de la webapp reintentarà la migració des del principi — per això les funcions han de ser idempotents.

Forçar una re-execució manual

Des de l'editor Apps Script: Executar → checkAndMigrate directament. Els logs apareixeran a la consola.

Resetar la versió manualment (últim recurs)

Si cal forçar la re-execució d'una migració específica:

// Executar des de l'editor Apps Script (no des del codi)
function _resetSchemaVersion() {
  PropertiesService.getScriptProperties().setProperty('SCHEMA_VERSION', '1');
  // Canvia '1' per la versió des de la qual vols tornar a migrar
}

Ús exclusivament de diagnòstic

No incloguis _resetSchemaVersion al flux normal. Executar-la en una instal·lació de producció pot re-aplicar migracions ja fetes.


Checklist per a cada migració

Afegeix això a la revisió del Merge Request:

  • [ ] CURRENT_SCHEMA_VERSION incrementat en 1
  • [ ] Funció _migrate_vN() escrita i registrada a MIGRATIONS
  • [ ] Comentari d'historial actualitzat
  • [ ] Funció és additiva (no elimina ni modifica dades existents)
  • [ ] Funció és idempotent (segura de re-executar)
  • [ ] Provada en una còpia de test del Sheets
  • [ ] Logs verificats a l'editor Apps Script
  • [ ] Notes de versió actualitzades (si el canvi afecta usuaris no tècnics)

Veure també: Instal·lació amb clasp · Restriccions de GAS · Model de dades