305 lines
16 KiB
JavaScript
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 (_) {}
|
|
}
|
|
},
|
|
);
|