Files
mdb/pocketbase/pb_migrations/1746000000_init_collections.js
Bastien COIGNOUX 2b8741de08 recherche
2026-05-04 21:52:51 +02:00

305 lines
16 KiB
JavaScript

/// <reference path="../pb_data/types.d.ts" />
/**
* Collections métier PocketBase (v0.23+).
* Ne recrée PAS `users` : elle existe déjà (auth par défaut).
* Idempotent : si une collection existe déjà, on la réutilise (évite "name must be unique" au redémarrage).
*/
migrate(
(app) => {
const usersId = app.findCollectionByNameOrId("users").id;
/** Retrouve une collection même si findCollectionByNameOrId échoue (casse, cache, image Docker). */
function findExistingCollection(name) {
try {
return app.findCollectionByNameOrId(name);
} catch (_) {}
try {
const all = app.findAllCollections();
const want = String(name).toLowerCase();
for (let i = 0; i < all.length; i++) {
const c = all[i];
if (c && c.name && String(c.name).toLowerCase() === want) {
return c;
}
}
} catch (_) {}
return null;
}
function loadOrCreate(name, factory) {
const existing = findExistingCollection(name);
if (existing != null) {
return existing;
}
try {
const col = factory();
app.save(col);
return col;
} catch (err) {
const msg = String(err && err.value ? err.value : err && err.message ? err.message : err);
if (msg.includes("unique") || msg.includes("Unique")) {
const again = findExistingCollection(name);
if (again != null) {
return again;
}
}
throw err;
}
}
// ── 1. Étapes pipeline ───────────────────────────────────
const etapes = loadOrCreate("etapes_pipeline", () => new Collection({
name: "etapes_pipeline",
type: "base",
fields: [
{ name: "user", type: "relation", required: true, options: { collectionId: usersId, maxSelect: 1, cascadeDelete: true } },
{ name: "nom", type: "text", required: true },
{ name: "ordre", type: "number", required: true },
{ name: "couleur", type: "text" },
{ name: "is_terminal", type: "bool" },
],
listRule: "@request.auth.id != \"\" && user = @request.auth.id",
viewRule: "@request.auth.id != \"\" && user = @request.auth.id",
createRule: "@request.auth.id != \"\"",
updateRule: "@request.auth.id != \"\" && user = @request.auth.id",
deleteRule: "@request.auth.id != \"\" && user = @request.auth.id",
}));
// ── 2. Contacts ──────────────────────────────────────────
const contacts = loadOrCreate("contacts", () => new Collection({
name: "contacts",
type: "base",
fields: [
{ name: "user", type: "relation", required: true, options: { collectionId: usersId, maxSelect: 1, cascadeDelete: true } },
{ name: "nom", type: "text", required: true },
{ name: "prenom", type: "text" },
{ name: "societe", type: "text" },
{ name: "categorie", type: "select", required: true, options: { maxSelect: 1, values: ["notaire", "agent_immo", "artisan_gros_oeuvre", "artisan_second_oeuvre", "artisan_finitions", "banquier", "courtier", "diagnostiqueur", "geometre", "avocat", "comptable", "vendeur", "acheteur", "autre"] } },
{ name: "specialite", type: "text" },
{ name: "telephone", type: "text" },
{ name: "telephone_2", type: "text" },
{ name: "email", type: "email" },
{ name: "ville", type: "text" },
{ name: "zone_intervention", type: "text" },
{ name: "note", type: "number", options: { min: 1, max: 5 } },
{ name: "recommande", type: "bool" },
{ name: "taux_horaire", type: "number" },
{ name: "notes", type: "text" },
{ name: "is_favori", type: "bool" },
],
listRule: "@request.auth.id != \"\" && user = @request.auth.id",
viewRule: "@request.auth.id != \"\" && user = @request.auth.id",
createRule: "@request.auth.id != \"\"",
updateRule: "@request.auth.id != \"\" && user = @request.auth.id",
deleteRule: "@request.auth.id != \"\" && user = @request.auth.id",
}));
// ── 3. Biens ─────────────────────────────────────────────
const biens = loadOrCreate("biens", () => new Collection({
name: "biens",
type: "base",
fields: [
{ name: "user", type: "relation", required: true, options: { collectionId: usersId, maxSelect: 1, cascadeDelete: true } },
{ name: "etape", type: "relation", options: { collectionId: etapes.id, maxSelect: 1 } },
{ name: "source_contact", type: "relation", options: { collectionId: contacts.id, maxSelect: 1 } },
{ name: "titre", type: "text" },
{ name: "type_bien", type: "select", options: { maxSelect: 1, values: ["appartement", "maison", "immeuble", "terrain", "local_commercial", "parking", "cave", "autre"] } },
{ name: "adresse", type: "text" },
{ name: "code_postal", type: "text" },
{ name: "ville", type: "text" },
{ name: "latitude", type: "number" },
{ name: "longitude", type: "number" },
{ name: "surface_habitable", type: "number" },
{ name: "surface_totale", type: "number" },
{ name: "nb_pieces", type: "number" },
{ name: "nb_chambres", type: "number" },
{ name: "annee_construction", type: "number" },
{ name: "dpe_lettre", type: "select", options: { maxSelect: 1, values: ["A", "B", "C", "D", "E", "F", "G"] } },
{ name: "dpe_valeur", type: "number" },
{ name: "source", type: "select", options: { maxSelect: 1, values: ["particulier", "agence", "notaire", "tribunal", "succession", "reseau", "autre"] } },
{ name: "url_annonce", type: "url" },
{ name: "statut", type: "select", options: { maxSelect: 1, values: ["actif", "abandonne", "vendu"] } },
{ name: "priorite", type: "number" },
{ name: "is_off_market", type: "bool" },
{ name: "date_premiere_visite", type: "text" },
{ name: "date_offre", type: "text" },
{ name: "date_compromis", type: "text" },
{ name: "date_acte", type: "text" },
{ name: "description", type: "text" },
{ name: "points_forts", type: "text" },
{ name: "points_faibles", type: "text" },
{ name: "photo_principale", type: "file", options: { maxSelect: 1, maxSize: 10485760, mimeTypes: ["image/jpeg", "image/png", "image/webp"] } },
],
listRule: "@request.auth.id != \"\" && user = @request.auth.id",
viewRule: "@request.auth.id != \"\" && user = @request.auth.id",
createRule: "@request.auth.id != \"\"",
updateRule: "@request.auth.id != \"\" && user = @request.auth.id",
deleteRule: "@request.auth.id != \"\" && user = @request.auth.id",
}));
// ── 4. Analyses financières ───────────────────────────────
loadOrCreate("analyses_financieres", () => new Collection({
name: "analyses_financieres",
type: "base",
fields: [
{ name: "user", type: "relation", required: true, options: { collectionId: usersId, maxSelect: 1, cascadeDelete: true } },
{ name: "bien", type: "relation", required: true, options: { collectionId: biens.id, maxSelect: 1, cascadeDelete: true } },
{ name: "prix_achat", type: "number" },
{ name: "type_bien_fiscal", type: "select", options: { maxSelect: 1, values: ["ancien", "neuf"] } },
{ name: "frais_notaire", type: "number" },
{ name: "frais_agence_achat", type: "number" },
{ name: "budget_travaux", type: "number" },
{ name: "reserve_imprevus_pct", type: "number" },
{ name: "duree_portage_mois", type: "number" },
{ name: "taux_credit", type: "number" },
{ name: "taxe_fonciere_annuelle", type: "number" },
{ name: "charges_copropriete_mensuelle", type: "number" },
{ name: "prix_revente_cible", type: "number" },
{ name: "frais_agence_vente_pct", type: "number" },
{ name: "taux_impot", type: "number" },
{ name: "marge_brute", type: "number" },
{ name: "marge_brute_pct", type: "number" },
{ name: "marge_nette", type: "number" },
{ name: "marge_nette_pct", type: "number" },
{ name: "notes", type: "text" },
],
listRule: "@request.auth.id != \"\" && user = @request.auth.id",
viewRule: "@request.auth.id != \"\" && user = @request.auth.id",
createRule: "@request.auth.id != \"\"",
updateRule: "@request.auth.id != \"\" && user = @request.auth.id",
deleteRule: "@request.auth.id != \"\" && user = @request.auth.id",
}));
// ── 5. Visites ────────────────────────────────────────────
loadOrCreate("visites", () => new Collection({
name: "visites",
type: "base",
fields: [
{ name: "user", type: "relation", required: true, options: { collectionId: usersId, maxSelect: 1, cascadeDelete: true } },
{ name: "bien", type: "relation", required: true, options: { collectionId: biens.id, maxSelect: 1, cascadeDelete: true } },
{ name: "date_visite", type: "date", required: true },
{ name: "duree_minutes", type: "number" },
{ name: "type_visite", type: "select", options: { maxSelect: 1, values: ["premiere", "seconde", "expert", "contradiction"] } },
{ name: "notes_brutes", type: "text" },
{ name: "rapport_genere", type: "text" },
{ name: "checklist_reponses", type: "json" },
{ name: "estimation_travaux_min", type: "number" },
{ name: "estimation_travaux_max", type: "number" },
{ name: "avis_global", type: "select", options: { maxSelect: 1, values: ["coup_de_coeur", "interessant", "neutre", "a_eviter"] } },
{ name: "score_opportunite", type: "number", options: { min: 1, max: 10 } },
{ name: "photos", type: "file", options: { maxSelect: 20, maxSize: 10485760, mimeTypes: ["image/jpeg", "image/png", "image/webp"] } },
],
listRule: "@request.auth.id != \"\" && user = @request.auth.id",
viewRule: "@request.auth.id != \"\" && user = @request.auth.id",
createRule: "@request.auth.id != \"\"",
updateRule: "@request.auth.id != \"\" && user = @request.auth.id",
deleteRule: "@request.auth.id != \"\" && user = @request.auth.id",
}));
// ── 6. Tâches ─────────────────────────────────────────────
loadOrCreate("taches", () => new Collection({
name: "taches",
type: "base",
fields: [
{ name: "user", type: "relation", required: true, options: { collectionId: usersId, maxSelect: 1, cascadeDelete: true } },
{ name: "bien", type: "relation", options: { collectionId: biens.id, maxSelect: 1 } },
{ name: "contact", type: "relation", options: { collectionId: contacts.id, maxSelect: 1 } },
{ name: "titre", type: "text", required: true },
{ name: "description", type: "text" },
{ name: "type_tache", type: "select", options: { maxSelect: 1, values: ["visite", "appel", "email", "relance", "administratif", "travaux", "banque", "autre"] } },
{ name: "priorite", type: "number" },
{ name: "statut", type: "select", options: { maxSelect: 1, values: ["a_faire", "en_cours", "fait", "annule"] } },
{ name: "date_echeance", type: "date" },
{ name: "date_rappel", type: "date" },
{ name: "is_urgent", type: "bool" },
],
listRule: "@request.auth.id != \"\" && user = @request.auth.id",
viewRule: "@request.auth.id != \"\" && user = @request.auth.id",
createRule: "@request.auth.id != \"\"",
updateRule: "@request.auth.id != \"\" && user = @request.auth.id",
deleteRule: "@request.auth.id != \"\" && user = @request.auth.id",
}));
// ── 7. Notes biens ────────────────────────────────────────
loadOrCreate("notes_biens", () => new Collection({
name: "notes_biens",
type: "base",
fields: [
{ name: "user", type: "relation", required: true, options: { collectionId: usersId, maxSelect: 1, cascadeDelete: true } },
{ name: "bien", type: "relation", required: true, options: { collectionId: biens.id, maxSelect: 1, cascadeDelete: true } },
{ name: "contenu", type: "text", required: true },
{ name: "type_note", type: "select", options: { maxSelect: 1, values: ["libre", "contact_vendeur", "negociation", "info_marche"] } },
],
listRule: "@request.auth.id != \"\" && user = @request.auth.id",
viewRule: "@request.auth.id != \"\" && user = @request.auth.id",
createRule: "@request.auth.id != \"\"",
updateRule: "@request.auth.id != \"\" && user = @request.auth.id",
deleteRule: "@request.auth.id != \"\" && user = @request.auth.id",
}));
// ── 8. Documents biens ────────────────────────────────────
loadOrCreate("documents_biens", () => new Collection({
name: "documents_biens",
type: "base",
fields: [
{ name: "user", type: "relation", required: true, options: { collectionId: usersId, maxSelect: 1, cascadeDelete: true } },
{ name: "bien", type: "relation", required: true, options: { collectionId: biens.id, maxSelect: 1, cascadeDelete: true } },
{ name: "nom", type: "text", required: true },
{ name: "type_document", type: "select", options: { maxSelect: 1, values: ["compromis", "acte", "dpe", "diagnostics", "devis", "facture", "titre_propriete", "autre"] } },
{ name: "date_document", type: "date" },
{ name: "notes", type: "text" },
{ name: "fichier", type: "file", required: true, options: { maxSelect: 1, maxSize: 52428800, mimeTypes: ["application/pdf", "image/jpeg", "image/png"] } },
],
listRule: "@request.auth.id != \"\" && user = @request.auth.id",
viewRule: "@request.auth.id != \"\" && user = @request.auth.id",
createRule: "@request.auth.id != \"\"",
updateRule: "@request.auth.id != \"\" && user = @request.auth.id",
deleteRule: "@request.auth.id != \"\" && user = @request.auth.id",
}));
// ── 9. Devis travaux ─────────────────────────────────────
loadOrCreate("devis_travaux", () => new Collection({
name: "devis_travaux",
type: "base",
fields: [
{ name: "user", type: "relation", required: true, options: { collectionId: usersId, maxSelect: 1, cascadeDelete: true } },
{ name: "bien", type: "relation", required: true, options: { collectionId: biens.id, maxSelect: 1, cascadeDelete: true } },
{ name: "contact", type: "relation", options: { collectionId: contacts.id, maxSelect: 1 } },
{ name: "lot", type: "text", required: true },
{ name: "description", type: "text" },
{ name: "montant_ht", type: "number" },
{ name: "taux_tva", type: "number" },
{ name: "montant_ttc", type: "number" },
{ name: "statut", type: "select", options: { maxSelect: 1, values: ["en_attente", "refuse", "accepte", "en_cours", "termine", "paye"] } },
{ name: "date_devis", type: "date" },
{ name: "date_debut", type: "date" },
{ name: "date_fin_prevu", type: "date" },
{ name: "notes", type: "text" },
{ name: "fichier_pdf", type: "file", options: { maxSelect: 1, maxSize: 10485760, mimeTypes: ["application/pdf"] } },
],
listRule: "@request.auth.id != \"\" && user = @request.auth.id",
viewRule: "@request.auth.id != \"\" && user = @request.auth.id",
createRule: "@request.auth.id != \"\"",
updateRule: "@request.auth.id != \"\" && user = @request.auth.id",
deleteRule: "@request.auth.id != \"\" && user = @request.auth.id",
}));
},
(app) => {
for (const name of [
"devis_travaux",
"documents_biens",
"notes_biens",
"taches",
"visites",
"analyses_financieres",
"biens",
"contacts",
"etapes_pipeline",
]) {
try {
app.delete(app.findCollectionByNameOrId(name));
} catch (_) {}
}
},
);