Initial local backup snapshot

This commit is contained in:
Selecta Keke 2026-06-24 00:02:55 -03:00
commit acd9e14ba5
367 changed files with 118038 additions and 0 deletions

View file

@ -0,0 +1,13 @@
CREATE TABLE `users` (
`id` int AUTO_INCREMENT NOT NULL,
`openId` varchar(64) NOT NULL,
`name` text,
`email` varchar(320),
`loginMethod` varchar(64),
`role` enum('user','admin') NOT NULL DEFAULT 'user',
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
`lastSignedIn` timestamp NOT NULL DEFAULT (now()),
CONSTRAINT `users_id` PRIMARY KEY(`id`),
CONSTRAINT `users_openId_unique` UNIQUE(`openId`)
);

View file

@ -0,0 +1,73 @@
CREATE TABLE `associations` (
`id` int AUTO_INCREMENT NOT NULL,
`userId` int NOT NULL,
`nomAssociation` varchar(255) NOT NULL,
`siret` varchar(14),
`rna` varchar(10),
`adresse` text,
`codePostal` varchar(10),
`ville` varchar(100),
`telephone` varchar(20),
`emailContact` varchar(320),
`siteWeb` varchar(255),
`dateCreation` timestamp,
`objetAssociation` text,
`statutJuridique` enum('association_loi_1901','association_reconnue_utilite_publique','fondation','autre') DEFAULT 'association_loi_1901',
`nomRepresentant` varchar(255),
`fonctionRepresentant` varchar(100),
`profileComplete` boolean DEFAULT false,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `associations_id` PRIMARY KEY(`id`),
CONSTRAINT `associations_userId_unique` UNIQUE(`userId`)
);
--> statement-breakpoint
CREATE TABLE `documents` (
`id` int AUTO_INCREMENT NOT NULL,
`associationId` int NOT NULL,
`nom` varchar(255) NOT NULL,
`type` enum('statuts','recepisse_declaration','rib','rapport_activite','rapport_financier','pv_assemblee','liste_dirigeants','attestation_assurance','autre') NOT NULL,
`description` text,
`fileKey` varchar(512) NOT NULL,
`fileUrl` varchar(1024) NOT NULL,
`mimeType` varchar(100),
`fileSize` int,
`uploadedAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `documents_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `requestTemplates` (
`id` int AUTO_INCREMENT NOT NULL,
`type` enum('subvention_fonctionnement','subvention_projet','agrement_jeunesse_education','agrement_sport','autorisation_occupation','demande_salle','autre') NOT NULL,
`nom` varchar(255) NOT NULL,
`description` text,
`formSchema` text NOT NULL,
`documentsRequis` text,
`serviceDestinataire` varchar(255),
`emailDestinataire` varchar(320),
`actif` boolean DEFAULT true,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `requestTemplates_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `requests` (
`id` int AUTO_INCREMENT NOT NULL,
`associationId` int NOT NULL,
`type` enum('subvention_fonctionnement','subvention_projet','agrement_jeunesse_education','agrement_sport','autorisation_occupation','demande_salle','autre') NOT NULL,
`titre` varchar(255) NOT NULL,
`description` text,
`formData` text,
`status` enum('brouillon','soumise','en_cours_traitement','information_complementaire','validee','refusee') NOT NULL DEFAULT 'brouillon',
`montantDemande` int,
`montantAccorde` int,
`dateSubmission` timestamp,
`dateTraitement` timestamp,
`traitePar` int,
`commentaireAdmin` text,
`documentsJoints` text,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `requests_id` PRIMARY KEY(`id`)
);

View file

@ -0,0 +1,65 @@
CREATE TABLE `adminNotifications` (
`id` int AUTO_INCREMENT NOT NULL,
`userId` int,
`type` enum('nouvelle_demande','demande_en_retard','dossier_incomplet','nouvelle_association','systeme') NOT NULL,
`titre` varchar(255) NOT NULL,
`message` text NOT NULL,
`lien` varchar(512),
`lu` boolean DEFAULT false,
`createdAt` timestamp NOT NULL DEFAULT (now()),
CONSTRAINT `adminNotifications_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `auditLog` (
`id` int AUTO_INCREMENT NOT NULL,
`userId` int NOT NULL,
`action` varchar(100) NOT NULL,
`entityType` varchar(50) NOT NULL,
`entityId` int,
`details` text,
`ipAddress` varchar(45),
`createdAt` timestamp NOT NULL DEFAULT (now()),
CONSTRAINT `auditLog_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `portalSettings` (
`id` int AUTO_INCREMENT NOT NULL,
`cle` varchar(100) NOT NULL,
`valeur` text NOT NULL,
`description` text,
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `portalSettings_id` PRIMARY KEY(`id`),
CONSTRAINT `portalSettings_cle_unique` UNIQUE(`cle`)
);
--> statement-breakpoint
CREATE TABLE `requestHistory` (
`id` int AUTO_INCREMENT NOT NULL,
`requestId` int NOT NULL,
`action` enum('creation','soumission','assignation','changement_statut','ajout_commentaire','modification','validation','refus') NOT NULL,
`ancienStatut` enum('brouillon','soumise','en_cours_traitement','information_complementaire','validee','refusee'),
`nouveauStatut` enum('brouillon','soumise','en_cours_traitement','information_complementaire','validee','refusee'),
`commentaire` text,
`userId` int NOT NULL,
`createdAt` timestamp NOT NULL DEFAULT (now()),
CONSTRAINT `requestHistory_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `responseTemplates` (
`id` int AUTO_INCREMENT NOT NULL,
`nom` varchar(255) NOT NULL,
`type` enum('validation','refus','information_complementaire','autre') NOT NULL,
`sujet` varchar(255),
`contenu` text NOT NULL,
`actif` boolean DEFAULT true,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `responseTemplates_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
ALTER TABLE `users` MODIFY COLUMN `role` enum('user','admin','super_admin') NOT NULL DEFAULT 'user';--> statement-breakpoint
ALTER TABLE `associations` ADD `isActive` boolean DEFAULT true NOT NULL;--> statement-breakpoint
ALTER TABLE `requestTemplates` ADD `delaiTraitementJours` int DEFAULT 30;--> statement-breakpoint
ALTER TABLE `requests` ADD `priority` enum('basse','normale','haute','urgente') DEFAULT 'normale' NOT NULL;--> statement-breakpoint
ALTER TABLE `requests` ADD `dateLimiteTraitement` timestamp;--> statement-breakpoint
ALTER TABLE `requests` ADD `assigneA` int;--> statement-breakpoint
ALTER TABLE `users` ADD `isActive` boolean DEFAULT true NOT NULL;

View file

@ -0,0 +1,13 @@
CREATE TABLE `emailActionTokens` (
`id` int AUTO_INCREMENT NOT NULL,
`token` varchar(64) NOT NULL,
`requestId` int NOT NULL,
`action` enum('validee','refusee') NOT NULL,
`used` boolean NOT NULL DEFAULT false,
`expiresAt` timestamp NOT NULL,
`usedAt` timestamp,
`usedBy` int,
`createdAt` timestamp NOT NULL DEFAULT (now()),
CONSTRAINT `emailActionTokens_id` PRIMARY KEY(`id`),
CONSTRAINT `emailActionTokens_token_unique` UNIQUE(`token`)
);

View file

@ -0,0 +1,2 @@
ALTER TABLE `adminNotifications` MODIFY COLUMN `type` enum('nouvelle_demande','demande_en_retard','dossier_incomplet','nouvelle_association','systeme','changement_salle') NOT NULL;--> statement-breakpoint
ALTER TABLE `requestHistory` MODIFY COLUMN `action` enum('creation','soumission','assignation','changement_statut','ajout_commentaire','modification','validation','refus','changement_salle') NOT NULL;

View file

@ -0,0 +1,5 @@
ALTER TABLE `adminNotifications` MODIFY COLUMN `type` enum('nouvelle_demande','demande_en_retard','dossier_incomplet','nouvelle_association','systeme','changement_salle','annulation_demande','modification_demande') NOT NULL;--> statement-breakpoint
ALTER TABLE `requestHistory` MODIFY COLUMN `action` enum('creation','soumission','assignation','changement_statut','ajout_commentaire','modification','validation','refus','changement_salle','annulation') NOT NULL;--> statement-breakpoint
ALTER TABLE `requestHistory` MODIFY COLUMN `ancienStatut` enum('brouillon','soumise','en_cours_traitement','information_complementaire','validee','refusee','annulee');--> statement-breakpoint
ALTER TABLE `requestHistory` MODIFY COLUMN `nouveauStatut` enum('brouillon','soumise','en_cours_traitement','information_complementaire','validee','refusee','annulee');--> statement-breakpoint
ALTER TABLE `requests` MODIFY COLUMN `status` enum('brouillon','soumise','en_cours_traitement','information_complementaire','validee','refusee','annulee') NOT NULL DEFAULT 'brouillon';

View file

@ -0,0 +1 @@
ALTER TABLE `users` ADD `passwordHash` varchar(255);

View file

@ -0,0 +1,2 @@
ALTER TABLE `requests` MODIFY COLUMN `type` enum('subvention_fonctionnement','subvention_projet','agrement_jeunesse_education','agrement_sport','autorisation_occupation','demande_salle','demande_materiel_evenementiel','autre') NOT NULL;--> statement-breakpoint
ALTER TABLE `requestTemplates` MODIFY COLUMN `type` enum('subvention_fonctionnement','subvention_projet','agrement_jeunesse_education','agrement_sport','autorisation_occupation','demande_salle','demande_materiel_evenementiel','autre') NOT NULL;

View file

@ -0,0 +1,27 @@
CREATE TABLE `associationDirectoryEntries` (
`id` int AUTO_INCREMENT NOT NULL,
`nomAssociation` varchar(255) NOT NULL,
`emailOfficiel` varchar(320),
`emailOfficielNormalise` varchar(320),
`siret` varchar(14),
`rna` varchar(10),
`adresse` text,
`codePostal` varchar(10),
`ville` varchar(100),
`telephone` varchar(20),
`siteWeb` varchar(255),
`dateCreation` timestamp,
`objetAssociation` text,
`statutJuridique` enum('association_loi_1901','association_reconnue_utilite_publique','fondation','autre') DEFAULT 'association_loi_1901',
`nomRepresentant` varchar(255),
`fonctionRepresentant` varchar(100),
`sourceFileName` varchar(255),
`sourceRowNumber` int,
`sourceFingerprint` varchar(64),
`isActive` boolean NOT NULL DEFAULT true,
`importedAt` timestamp NOT NULL DEFAULT now(),
`updatedAt` timestamp NOT NULL DEFAULT now() ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `associationDirectoryEntries_id` PRIMARY KEY(`id`),
CONSTRAINT `associationDirectoryEntries_emailOfficielNormalise_unique` UNIQUE(`emailOfficielNormalise`)
);--> statement-breakpoint
ALTER TABLE `associations` ADD `sourceDirectoryEntryId` int;--> statement-breakpoint

View file

@ -0,0 +1,19 @@
CREATE TABLE `associationInvitations` (
`id` int AUTO_INCREMENT NOT NULL,
`directoryEntryId` int NOT NULL,
`emailOfficiel` varchar(320) NOT NULL,
`emailOfficielNormalise` varchar(320) NOT NULL,
`token` varchar(128) NOT NULL,
`deliveryMode` enum('email','manual_link') NOT NULL,
`emailSent` boolean NOT NULL DEFAULT false,
`sentByUserId` int NOT NULL,
`sentAt` timestamp NOT NULL DEFAULT now(),
`expiresAt` timestamp NOT NULL,
`usedAt` timestamp,
`revokedAt` timestamp,
`acceptedByUserId` int,
`createdAt` timestamp NOT NULL DEFAULT now(),
`updatedAt` timestamp NOT NULL DEFAULT now() ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `associationInvitations_id` PRIMARY KEY(`id`),
CONSTRAINT `associationInvitations_token_unique` UNIQUE(`token`)
);

View file

@ -0,0 +1,8 @@
ALTER TABLE `associationDirectoryEntries`
ADD `latitude` varchar(32),
ADD `longitude` varchar(32),
ADD `geoSource` enum('manual','adresse_gouv','dataasso','commune_center'),
ADD `geoPrecision` enum('exact_address','commune_center','hidden') NOT NULL DEFAULT 'commune_center',
ADD `geoLastSyncedAt` timestamp NULL,
ADD `externalSourceStatus` varchar(100),
ADD `externalSourceLabel` varchar(255);

View file

@ -0,0 +1,6 @@
ALTER TABLE `associationDirectoryEntries`
ADD `associationStatus` varchar(40),
ADD `registryLastUpdatedAt` timestamp NULL,
ADD `referenceLastCheckedAt` timestamp NULL,
ADD `referenceStatus` varchar(100),
ADD `referenceSourceLabel` varchar(255);

View file

@ -0,0 +1,3 @@
ALTER TABLE `associations`
ADD `facebookUrl` varchar(255),
ADD `instagramUrl` varchar(255);

View file

@ -0,0 +1,3 @@
ALTER TABLE `associationDirectoryEntries`
ADD `facebookUrl` varchar(255),
ADD `instagramUrl` varchar(255);

View file

@ -0,0 +1,10 @@
CREATE TABLE `operationalRecapServices` (
`id` int AUTO_INCREMENT NOT NULL,
`label` varchar(255) NOT NULL,
`description` text,
`recipientEmails` text NOT NULL,
`actif` boolean NOT NULL DEFAULT true,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `operationalRecapServices_id` PRIMARY KEY(`id`)
);

View file

@ -0,0 +1,21 @@
ALTER TABLE `associations`
ADD `thematique` ENUM(
'culture_loisirs',
'social_sante',
'education_formation',
'economie_territoire',
'environnement_patrimoine',
'institutions_divers'
);
--> statement-breakpoint
ALTER TABLE `associationDirectoryEntries`
ADD `thematique` ENUM(
'culture_loisirs',
'social_sante',
'education_formation',
'economie_territoire',
'environnement_patrimoine',
'institutions_divers'
);

View file

@ -0,0 +1,21 @@
ALTER TABLE `associations`
MODIFY `thematique` text;
--> statement-breakpoint
UPDATE `associations`
SET `thematique` = JSON_ARRAY(`thematique`)
WHERE `thematique` IS NOT NULL
AND `thematique` NOT LIKE '[%';
--> statement-breakpoint
ALTER TABLE `associationDirectoryEntries`
MODIFY `thematique` text;
--> statement-breakpoint
UPDATE `associationDirectoryEntries`
SET `thematique` = JSON_ARRAY(`thematique`)
WHERE `thematique` IS NOT NULL
AND `thematique` NOT LIKE '[%';

View file

@ -0,0 +1,6 @@
ALTER TABLE `associations`
ADD COLUMN `gouvernance` text;
--> statement-breakpoint
ALTER TABLE `associationDirectoryEntries`
ADD COLUMN `gouvernance` text;

View file

@ -0,0 +1,27 @@
CREATE TABLE `materialReturnFollowups` (
`id` int AUTO_INCREMENT NOT NULL,
`requestId` int NOT NULL,
`associationId` int NOT NULL,
`serviceLabel` varchar(255),
`recipientEmails` text,
`restitutionDate` timestamp NOT NULL,
`plannedSendAt` timestamp NOT NULL,
`sentAt` timestamp,
`status` enum('planifie','en_attente','en_cours','cloture') NOT NULL DEFAULT 'planifie',
`uploadToken` varchar(96) NOT NULL,
`uploadTokenExpiresAt` timestamp NOT NULL,
`signedFileKey` varchar(512),
`signedFileUrl` varchar(1024),
`signedFileName` varchar(255),
`signedMimeType` varchar(100),
`uploadedByName` varchar(255),
`uploadedByEmail` varchar(320),
`uploadedAt` timestamp,
`closedAt` timestamp,
`lastReminderSentAt` timestamp,
`createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `materialReturnFollowups_id` PRIMARY KEY(`id`),
CONSTRAINT `materialReturnFollowups_requestId_unique` UNIQUE(`requestId`),
CONSTRAINT `materialReturnFollowups_uploadToken_unique` UNIQUE(`uploadToken`)
);

View file

@ -0,0 +1,19 @@
ALTER TABLE `materialReturnFollowups`
ADD COLUMN `agentUserId` int NULL AFTER `uploadedByEmail`,
ADD COLUMN `agentRole` varchar(100) NULL AFTER `agentUserId`,
ADD COLUMN `borrowerName` varchar(255) NULL AFTER `agentRole`,
ADD COLUMN `borrowerRole` varchar(120) NULL AFTER `borrowerName`,
ADD COLUMN `compliance` enum('conforme','non_conforme') NULL AFTER `borrowerRole`,
ADD COLUMN `discrepancyCategories` text NULL AFTER `compliance`,
ADD COLUMN `discrepancyDetails` text NULL AFTER `discrepancyCategories`,
ADD COLUMN `issueFlag` boolean NOT NULL DEFAULT false AFTER `discrepancyDetails`,
ADD COLUMN `agentSignatureKey` varchar(512) NULL AFTER `issueFlag`,
ADD COLUMN `agentSignatureUrl` varchar(1024) NULL AFTER `agentSignatureKey`,
ADD COLUMN `borrowerSignatureKey` varchar(512) NULL AFTER `agentSignatureUrl`,
ADD COLUMN `borrowerSignatureUrl` varchar(1024) NULL AFTER `borrowerSignatureKey`,
ADD COLUMN `finalPdfKey` varchar(512) NULL AFTER `borrowerSignatureUrl`,
ADD COLUMN `finalPdfUrl` varchar(1024) NULL AFTER `finalPdfKey`,
ADD COLUMN `finalPdfName` varchar(255) NULL AFTER `finalPdfUrl`,
ADD COLUMN `geoLatitude` varchar(64) NULL AFTER `finalPdfName`,
ADD COLUMN `geoLongitude` varchar(64) NULL AFTER `geoLatitude`,
ADD COLUMN `validatedAt` timestamp NULL AFTER `uploadedAt`;

View file

@ -0,0 +1,3 @@
ALTER TABLE `materialReturnFollowups`
ADD COLUMN `geoStatus` varchar(32) NULL AFTER `geoLongitude`,
ADD COLUMN `geoFailureReason` text NULL AFTER `geoStatus`;

View file

@ -0,0 +1,13 @@
ALTER TABLE `materialReturnFollowups`
ADD COLUMN `litigationStatus` enum('none','pending','resolved') NOT NULL DEFAULT 'none' AFTER `issueFlag`,
ADD COLUMN `blockedItems` text NULL AFTER `litigationStatus`,
ADD COLUMN `estimatedDamageAmount` int NULL AFTER `blockedItems`,
ADD COLUMN `estimatedDamageSource` varchar(32) NULL AFTER `estimatedDamageAmount`,
ADD COLUMN `arbitrationDecision` enum('partial_retention','full_retention','dismissed') NULL AFTER `estimatedDamageSource`,
ADD COLUMN `arbitrationAmount` int NULL AFTER `arbitrationDecision`,
ADD COLUMN `arbitrationNotes` text NULL AFTER `arbitrationAmount`,
ADD COLUMN `arbitratedByUserId` int NULL AFTER `arbitrationNotes`,
ADD COLUMN `arbitratedAt` timestamp NULL AFTER `arbitratedByUserId`,
ADD COLUMN `litigationLetterKey` varchar(512) NULL AFTER `arbitratedAt`,
ADD COLUMN `litigationLetterUrl` varchar(1024) NULL AFTER `litigationLetterKey`,
ADD COLUMN `litigationLetterName` varchar(255) NULL AFTER `litigationLetterUrl`;

View file

@ -0,0 +1,4 @@
ALTER TABLE `materialReturnFollowups`
ADD COLUMN `reactivatedByUserId` int NULL AFTER `lastReminderSentAt`,
ADD COLUMN `reactivatedAt` timestamp NULL AFTER `reactivatedByUserId`,
ADD COLUMN `reactivationReason` text NULL AFTER `reactivatedAt`;

View file

@ -0,0 +1,3 @@
ALTER TABLE `materialReturnFollowups`
ADD COLUMN `supervisionServiceLabel` varchar(255) NULL AFTER `recipientEmails`,
ADD COLUMN `supervisionRecipientEmails` text NULL AFTER `supervisionServiceLabel`;

View file

@ -0,0 +1,6 @@
ALTER TABLE `users`
ADD COLUMN `canManageLogistics` boolean NOT NULL DEFAULT false AFTER `role`;
--> statement-breakpoint
UPDATE `users`
SET `canManageLogistics` = true
WHERE `role` = 'super_admin';

View file

@ -0,0 +1,2 @@
ALTER TABLE `emailActionTokens`
MODIFY COLUMN `action` enum('validee','refusee','acceptation_devis_salle','refus_devis_salle') NOT NULL;

View file

@ -0,0 +1,3 @@
ALTER TABLE `users`
MODIFY COLUMN `role` enum('user','admin','directrice','super_admin') NOT NULL DEFAULT 'user',
ADD COLUMN `delegatedSalleSignerUserId` int NULL AFTER `canManageLogistics`;

View file

@ -0,0 +1,2 @@
ALTER TABLE `users`
MODIFY COLUMN `role` enum('user','accueil','admin','directrice','super_admin') NOT NULL DEFAULT 'user';

View file

@ -0,0 +1,2 @@
ALTER TABLE `users`
MODIFY COLUMN `role` enum('user','accueil','logistique_controle','admin','directrice','super_admin') NOT NULL DEFAULT 'user';

View file

@ -0,0 +1,2 @@
ALTER TABLE `operationalRecapServices`
ADD COLUMN `usage` enum('terrain','controle') NOT NULL DEFAULT 'terrain' AFTER `label`;

View file

@ -0,0 +1,10 @@
ALTER TABLE `users`
MODIFY COLUMN `role` enum(
'user',
'accueil',
'service_terrain',
'logistique_controle',
'admin',
'directrice',
'super_admin'
) NOT NULL DEFAULT 'user';

View file

@ -0,0 +1,20 @@
CREATE TABLE `associationDirectoryReviews` (
`id` int AUTO_INCREMENT NOT NULL,
`sourceType` enum('portal_signup','helloasso') NOT NULL,
`sourceLabel` varchar(255),
`userId` int,
`resolvedDirectoryEntryId` int,
`proposedNomAssociation` varchar(255) NOT NULL,
`proposedEmail` varchar(320),
`proposedEmailNormalise` varchar(320),
`proposedSiret` varchar(14),
`proposedRna` varchar(10),
`proposedVille` varchar(100),
`matchStatus` varchar(40),
`payload` text,
`status` enum('pending','linked','created','ignored') NOT NULL DEFAULT 'pending',
`resolutionNote` text,
`createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `associationDirectoryReviews_id` PRIMARY KEY(`id`)
);

View file

@ -0,0 +1,191 @@
CREATE TABLE `associationDirectoryEntries` (
`id` int AUTO_INCREMENT NOT NULL,
`nomAssociation` varchar(255) NOT NULL,
`emailOfficiel` varchar(320),
`emailOfficielNormalise` varchar(320),
`siret` varchar(14),
`rna` varchar(10),
`thematique` text,
`adresse` text,
`codePostal` varchar(10),
`ville` varchar(100),
`telephone` varchar(20),
`siteWeb` varchar(255),
`facebookUrl` varchar(255),
`instagramUrl` varchar(255),
`dateCreation` timestamp,
`objetAssociation` text,
`statutJuridique` enum('association_loi_1901','association_reconnue_utilite_publique','fondation','autre') DEFAULT 'association_loi_1901',
`nomRepresentant` varchar(255),
`fonctionRepresentant` varchar(100),
`gouvernance` text,
`latitude` varchar(32),
`longitude` varchar(32),
`geoSource` enum('manual','adresse_gouv','dataasso','commune_center'),
`geoPrecision` enum('exact_address','commune_center','hidden') NOT NULL DEFAULT 'commune_center',
`geoLastSyncedAt` timestamp,
`externalSourceStatus` varchar(100),
`externalSourceLabel` varchar(255),
`associationStatus` varchar(40),
`registryLastUpdatedAt` timestamp,
`referenceLastCheckedAt` timestamp,
`referenceStatus` varchar(100),
`referenceSourceLabel` varchar(255),
`sourceFileName` varchar(255),
`sourceRowNumber` int,
`sourceFingerprint` varchar(64),
`isActive` boolean NOT NULL DEFAULT true,
`importedAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `associationDirectoryEntries_id` PRIMARY KEY(`id`),
CONSTRAINT `associationDirectoryEntries_emailOfficielNormalise_unique` UNIQUE(`emailOfficielNormalise`)
);
--> statement-breakpoint
CREATE TABLE `associationDirectoryReviews` (
`id` int AUTO_INCREMENT NOT NULL,
`sourceType` enum('portal_signup','helloasso') NOT NULL,
`sourceLabel` varchar(255),
`userId` int,
`resolvedDirectoryEntryId` int,
`proposedNomAssociation` varchar(255) NOT NULL,
`proposedEmail` varchar(320),
`proposedEmailNormalise` varchar(320),
`proposedSiret` varchar(14),
`proposedRna` varchar(10),
`proposedVille` varchar(100),
`matchStatus` varchar(40),
`payload` text,
`status` enum('pending','linked','created','ignored') NOT NULL DEFAULT 'pending',
`resolutionNote` text,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `associationDirectoryReviews_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `associationInvitations` (
`id` int AUTO_INCREMENT NOT NULL,
`directoryEntryId` int NOT NULL,
`emailOfficiel` varchar(320) NOT NULL,
`emailOfficielNormalise` varchar(320) NOT NULL,
`token` varchar(128) NOT NULL,
`deliveryMode` enum('email','manual_link') NOT NULL,
`emailSent` boolean NOT NULL DEFAULT false,
`sentByUserId` int NOT NULL,
`sentAt` timestamp NOT NULL DEFAULT (now()),
`expiresAt` timestamp NOT NULL,
`usedAt` timestamp,
`revokedAt` timestamp,
`acceptedByUserId` int,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `associationInvitations_id` PRIMARY KEY(`id`),
CONSTRAINT `associationInvitations_token_unique` UNIQUE(`token`)
);
--> statement-breakpoint
CREATE TABLE `materialReturnFollowups` (
`id` int AUTO_INCREMENT NOT NULL,
`requestId` int NOT NULL,
`associationId` int NOT NULL,
`serviceLabel` varchar(255),
`recipientEmails` text,
`supervisionServiceLabel` varchar(255),
`supervisionRecipientEmails` text,
`restitutionDate` timestamp NOT NULL,
`plannedSendAt` timestamp NOT NULL,
`sentAt` timestamp,
`status` enum('planifie','en_attente','en_cours','cloture') NOT NULL DEFAULT 'planifie',
`uploadToken` varchar(96) NOT NULL,
`uploadTokenExpiresAt` timestamp NOT NULL,
`signedFileKey` varchar(512),
`signedFileUrl` varchar(1024),
`signedFileName` varchar(255),
`signedMimeType` varchar(100),
`uploadedByName` varchar(255),
`uploadedByEmail` varchar(320),
`agentUserId` int,
`agentRole` varchar(100),
`borrowerName` varchar(255),
`borrowerRole` varchar(120),
`compliance` enum('conforme','non_conforme'),
`discrepancyCategories` text,
`discrepancyDetails` text,
`issueFlag` boolean NOT NULL DEFAULT false,
`litigationStatus` enum('none','pending','resolved') NOT NULL DEFAULT 'none',
`blockedItems` text,
`estimatedDamageAmount` int,
`estimatedDamageSource` varchar(32),
`arbitrationDecision` enum('partial_retention','full_retention','dismissed'),
`arbitrationAmount` int,
`arbitrationNotes` text,
`arbitratedByUserId` int,
`arbitratedAt` timestamp,
`litigationLetterKey` varchar(512),
`litigationLetterUrl` varchar(1024),
`litigationLetterName` varchar(255),
`agentSignatureKey` varchar(512),
`agentSignatureUrl` varchar(1024),
`borrowerSignatureKey` varchar(512),
`borrowerSignatureUrl` varchar(1024),
`finalPdfKey` varchar(512),
`finalPdfUrl` varchar(1024),
`finalPdfName` varchar(255),
`geoLatitude` varchar(64),
`geoLongitude` varchar(64),
`geoStatus` varchar(32),
`geoFailureReason` text,
`uploadedAt` timestamp,
`validatedAt` timestamp,
`closedAt` timestamp,
`lastReminderSentAt` timestamp,
`reactivatedByUserId` int,
`reactivatedAt` timestamp,
`reactivationReason` text,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `materialReturnFollowups_id` PRIMARY KEY(`id`),
CONSTRAINT `materialReturnFollowups_requestId_unique` UNIQUE(`requestId`),
CONSTRAINT `materialReturnFollowups_uploadToken_unique` UNIQUE(`uploadToken`)
);
--> statement-breakpoint
CREATE TABLE `operationalRecapServices` (
`id` int AUTO_INCREMENT NOT NULL,
`label` varchar(255) NOT NULL,
`usage` enum('terrain','controle') NOT NULL DEFAULT 'terrain',
`description` text,
`recipientEmails` text NOT NULL,
`actif` boolean NOT NULL DEFAULT true,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `operationalRecapServices_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
ALTER TABLE `emailActionTokens` MODIFY COLUMN `action` enum('validee','refusee','acceptation_devis_salle','refus_devis_salle') NOT NULL;--> statement-breakpoint
ALTER TABLE `requestTemplates` MODIFY COLUMN `type` enum('subvention_fonctionnement','subvention_projet','agrement_jeunesse_education','agrement_sport','autorisation_occupation','demande_salle','demande_materiel_evenementiel','autre') NOT NULL;--> statement-breakpoint
ALTER TABLE `requests` MODIFY COLUMN `type` enum('subvention_fonctionnement','subvention_projet','agrement_jeunesse_education','agrement_sport','autorisation_occupation','demande_salle','demande_materiel_evenementiel','autre') NOT NULL;--> statement-breakpoint
ALTER TABLE `users` MODIFY COLUMN `role` enum('user','accueil','service_terrain','logistique_controle','admin','directrice','super_admin') NOT NULL DEFAULT 'user';--> statement-breakpoint
ALTER TABLE `associations` ADD `sourceDirectoryEntryId` int;--> statement-breakpoint
ALTER TABLE `associations` ADD `thematique` text;--> statement-breakpoint
ALTER TABLE `associations` ADD `facebookUrl` varchar(255);--> statement-breakpoint
ALTER TABLE `associations` ADD `instagramUrl` varchar(255);--> statement-breakpoint
ALTER TABLE `associations` ADD `gouvernance` text;--> statement-breakpoint
ALTER TABLE `users` ADD `passwordHash` varchar(255);--> statement-breakpoint
ALTER TABLE `users` ADD `canManageLogistics` boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE `users` ADD `delegatedSalleSignerUserId` int;--> statement-breakpoint
ALTER TABLE `users` ADD `failedLoginAttempts` int DEFAULT 0 NOT NULL;--> statement-breakpoint
ALTER TABLE `users` ADD `lockedUntil` timestamp;--> statement-breakpoint
ALTER TABLE `users` ADD `mfaEnabled` boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE `users` ADD `mfaMethod` varchar(32);--> statement-breakpoint
ALTER TABLE `users` ADD `mfaChallengeToken` varchar(96);--> statement-breakpoint
ALTER TABLE `users` ADD `mfaCodeHash` varchar(255);--> statement-breakpoint
ALTER TABLE `users` ADD `mfaCodeExpiresAt` timestamp;--> statement-breakpoint
ALTER TABLE `users` ADD `mfaCodeAttempts` int DEFAULT 0 NOT NULL;--> statement-breakpoint
ALTER TABLE `users` ADD `mfaTotpSecretEncrypted` varchar(512);--> statement-breakpoint
ALTER TABLE `users` ADD `mfaTotpPendingSecretEncrypted` varchar(512);--> statement-breakpoint
ALTER TABLE `users` ADD `deletionRequestedAt` timestamp;--> statement-breakpoint
ALTER TABLE `users` ADD `purgeScheduledAt` timestamp;--> statement-breakpoint
ALTER TABLE `users` ADD `purgedAt` timestamp;--> statement-breakpoint
ALTER TABLE `users` ADD `legalHold` boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE `users` ADD `legalHoldReason` text;--> statement-breakpoint
ALTER TABLE `users` ADD `privacyConsentVersion` varchar(32);--> statement-breakpoint
ALTER TABLE `users` ADD `privacyConsentAcceptedAt` timestamp;--> statement-breakpoint
ALTER TABLE `users` ADD `privacyConsentContext` varchar(64);

View file

@ -0,0 +1,14 @@
CREATE TABLE `associationDirectoryUpdateProposals` (
`id` int AUTO_INCREMENT NOT NULL,
`directoryEntryId` int NOT NULL,
`sourceType` enum('official_registry','helloasso') NOT NULL,
`sourceLabel` varchar(255),
`summary` varchar(255),
`payload` text NOT NULL,
`status` enum('pending','applied','dismissed') NOT NULL DEFAULT 'pending',
`appliedAt` timestamp NULL,
`dismissedAt` timestamp NULL,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `associationDirectoryUpdateProposals_id` PRIMARY KEY(`id`)
);

View file

@ -0,0 +1,110 @@
{
"version": "5",
"dialect": "mysql",
"id": "61c2e7be-1b66-4e0f-918a-44917f92c891",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"openId": {
"name": "openId",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(320)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"loginMethod": {
"name": "loginMethod",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"role": {
"name": "role",
"type": "enum('user','admin')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'user'"
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
},
"lastSignedIn": {
"name": "lastSignedIn",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"users_id": {
"name": "users_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"users_openId_unique": {
"name": "users_openId_unique",
"columns": [
"openId"
]
}
},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}

View file

@ -0,0 +1,600 @@
{
"version": "5",
"dialect": "mysql",
"id": "f62ddf84-b5a1-49fb-b272-5a5d96acf838",
"prevId": "61c2e7be-1b66-4e0f-918a-44917f92c891",
"tables": {
"associations": {
"name": "associations",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"userId": {
"name": "userId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"nomAssociation": {
"name": "nomAssociation",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"siret": {
"name": "siret",
"type": "varchar(14)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"rna": {
"name": "rna",
"type": "varchar(10)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"adresse": {
"name": "adresse",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"codePostal": {
"name": "codePostal",
"type": "varchar(10)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"ville": {
"name": "ville",
"type": "varchar(100)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"telephone": {
"name": "telephone",
"type": "varchar(20)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"emailContact": {
"name": "emailContact",
"type": "varchar(320)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"siteWeb": {
"name": "siteWeb",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"dateCreation": {
"name": "dateCreation",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"objetAssociation": {
"name": "objetAssociation",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"statutJuridique": {
"name": "statutJuridique",
"type": "enum('association_loi_1901','association_reconnue_utilite_publique','fondation','autre')",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'association_loi_1901'"
},
"nomRepresentant": {
"name": "nomRepresentant",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"fonctionRepresentant": {
"name": "fonctionRepresentant",
"type": "varchar(100)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"profileComplete": {
"name": "profileComplete",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"associations_id": {
"name": "associations_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"associations_userId_unique": {
"name": "associations_userId_unique",
"columns": [
"userId"
]
}
},
"checkConstraint": {}
},
"documents": {
"name": "documents",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"associationId": {
"name": "associationId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"nom": {
"name": "nom",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "enum('statuts','recepisse_declaration','rib','rapport_activite','rapport_financier','pv_assemblee','liste_dirigeants','attestation_assurance','autre')",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"fileKey": {
"name": "fileKey",
"type": "varchar(512)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"fileUrl": {
"name": "fileUrl",
"type": "varchar(1024)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"mimeType": {
"name": "mimeType",
"type": "varchar(100)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"fileSize": {
"name": "fileSize",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"uploadedAt": {
"name": "uploadedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"documents_id": {
"name": "documents_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"requestTemplates": {
"name": "requestTemplates",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"type": {
"name": "type",
"type": "enum('subvention_fonctionnement','subvention_projet','agrement_jeunesse_education','agrement_sport','autorisation_occupation','demande_salle','autre')",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"nom": {
"name": "nom",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"formSchema": {
"name": "formSchema",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"documentsRequis": {
"name": "documentsRequis",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"serviceDestinataire": {
"name": "serviceDestinataire",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"emailDestinataire": {
"name": "emailDestinataire",
"type": "varchar(320)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"actif": {
"name": "actif",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": true
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"requestTemplates_id": {
"name": "requestTemplates_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"requests": {
"name": "requests",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"associationId": {
"name": "associationId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "enum('subvention_fonctionnement','subvention_projet','agrement_jeunesse_education','agrement_sport','autorisation_occupation','demande_salle','autre')",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"titre": {
"name": "titre",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"formData": {
"name": "formData",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"status": {
"name": "status",
"type": "enum('brouillon','soumise','en_cours_traitement','information_complementaire','validee','refusee')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'brouillon'"
},
"montantDemande": {
"name": "montantDemande",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"montantAccorde": {
"name": "montantAccorde",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"dateSubmission": {
"name": "dateSubmission",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"dateTraitement": {
"name": "dateTraitement",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"traitePar": {
"name": "traitePar",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"commentaireAdmin": {
"name": "commentaireAdmin",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"documentsJoints": {
"name": "documentsJoints",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"requests_id": {
"name": "requests_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"openId": {
"name": "openId",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(320)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"loginMethod": {
"name": "loginMethod",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"role": {
"name": "role",
"type": "enum('user','admin')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'user'"
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
},
"lastSignedIn": {
"name": "lastSignedIn",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"users_id": {
"name": "users_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"users_openId_unique": {
"name": "users_openId_unique",
"columns": [
"openId"
]
}
},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

237
drizzle/meta/_journal.json Normal file
View file

@ -0,0 +1,237 @@
{
"version": "7",
"dialect": "mysql",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1769809545494,
"tag": "0000_lush_vulcan",
"breakpoints": true
},
{
"idx": 1,
"version": "5",
"when": 1769809676445,
"tag": "0001_green_rachel_grey",
"breakpoints": true
},
{
"idx": 2,
"version": "5",
"when": 1769816629238,
"tag": "0002_numerous_cyclops",
"breakpoints": true
},
{
"idx": 3,
"version": "5",
"when": 1771026139231,
"tag": "0003_closed_night_thrasher",
"breakpoints": true
},
{
"idx": 4,
"version": "5",
"when": 1773257029833,
"tag": "0004_serious_deathstrike",
"breakpoints": true
},
{
"idx": 5,
"version": "5",
"when": 1773269471190,
"tag": "0005_rich_blockbuster",
"breakpoints": true
},
{
"idx": 6,
"version": "5",
"when": 1776595200000,
"tag": "0006_local_jwt_auth",
"breakpoints": true
},
{
"idx": 7,
"version": "5",
"when": 1777972800000,
"tag": "0007_event_material_request",
"breakpoints": true
},
{
"idx": 8,
"version": "5",
"when": 1778068800000,
"tag": "0008_association_directory",
"breakpoints": true
},
{
"idx": 9,
"version": "5",
"when": 1778155200000,
"tag": "0009_association_invitations",
"breakpoints": true
},
{
"idx": 10,
"version": "5",
"when": 1778580000000,
"tag": "0010_association_directory_geo",
"breakpoints": true
},
{
"idx": 11,
"version": "5",
"when": 1778667600000,
"tag": "0011_association_directory_reference_sync",
"breakpoints": true
},
{
"idx": 12,
"version": "5",
"when": 1778670600000,
"tag": "0012_association_social_links",
"breakpoints": true
},
{
"idx": 13,
"version": "5",
"when": 1778670900000,
"tag": "0013_association_directory_social_links",
"breakpoints": true
},
{
"idx": 14,
"version": "5",
"when": 1778752800000,
"tag": "0014_operational_recap_services",
"breakpoints": true
},
{
"idx": 15,
"version": "5",
"when": 1778763600000,
"tag": "0015_association_thematics",
"breakpoints": true
},
{
"idx": 16,
"version": "5",
"when": 1778767200000,
"tag": "0016_association_thematics_multiselect",
"breakpoints": true
},
{
"idx": 17,
"version": "5",
"when": 1778995200000,
"tag": "0017_association_governance",
"breakpoints": true
},
{
"idx": 18,
"version": "5",
"when": 1779080400000,
"tag": "0018_material_return_followups",
"breakpoints": true
},
{
"idx": 19,
"version": "5",
"when": 1779082200000,
"tag": "0019_material_return_mobile_workflow",
"breakpoints": true
},
{
"idx": 20,
"version": "5",
"when": 1779094200000,
"tag": "0020_material_return_geolocation_resilience",
"breakpoints": true
},
{
"idx": 21,
"version": "5",
"when": 1779097800000,
"tag": "0021_material_return_litigation_arbitration",
"breakpoints": true
},
{
"idx": 22,
"version": "5",
"when": 1779837000000,
"tag": "0022_material_return_reactivation_tracking",
"breakpoints": true
},
{
"idx": 23,
"version": "5",
"when": 1779840000000,
"tag": "0023_material_return_supervision_recipients",
"breakpoints": true
},
{
"idx": 24,
"version": "5",
"when": 1779920000000,
"tag": "0024_user_logistics_permissions",
"breakpoints": true
},
{
"idx": 25,
"version": "5",
"when": 1780155600000,
"tag": "0025_salle_quote_email_actions",
"breakpoints": true
},
{
"idx": 26,
"version": "5",
"when": 1780582200000,
"tag": "0026_salle_signature_delegation",
"breakpoints": true
},
{
"idx": 27,
"version": "5",
"when": 1780750000000,
"tag": "0027_accueil_role",
"breakpoints": true
},
{
"idx": 28,
"version": "5",
"when": 1780750060000,
"tag": "0028_logistique_controle_role",
"breakpoints": true
},
{
"idx": 29,
"version": "5",
"when": 1780842000000,
"tag": "0029_operational_recap_service_usage",
"breakpoints": true
},
{
"idx": 30,
"version": "5",
"when": 1780848000000,
"tag": "0030_service_terrain_role",
"breakpoints": true
},
{
"idx": 31,
"version": "5",
"when": 1780938000000,
"tag": "0031_association_directory_reviews",
"breakpoints": true
},
{
"idx": 32,
"version": "5",
"when": 1781354966000,
"tag": "0032_amusing_boomerang",
"breakpoints": true
}
]
}

View file

1
drizzle/relations.ts Normal file
View file

@ -0,0 +1 @@
import {} from "./schema";

627
drizzle/schema.ts Normal file
View file

@ -0,0 +1,627 @@
import { int, mysqlEnum, mysqlTable, text, timestamp, varchar, boolean } from "drizzle-orm/mysql-core";
import { associationGeoPrecisions, associationGeoSources } from "@shared/associationGeo";
import { associationThematicValues } from "@shared/associationThematics";
/**
* Core user table backing auth flow.
*/
export const users = mysqlTable("users", {
id: int("id").autoincrement().primaryKey(),
openId: varchar("openId", { length: 64 }).notNull().unique(),
name: text("name"),
email: varchar("email", { length: 320 }),
passwordHash: varchar("passwordHash", { length: 255 }),
loginMethod: varchar("loginMethod", { length: 64 }),
role: mysqlEnum("role", ["user", "accueil", "service_terrain", "logistique_controle", "admin", "directrice", "super_admin"]).default("user").notNull(),
canManageLogistics: boolean("canManageLogistics").default(false).notNull(),
delegatedSalleSignerUserId: int("delegatedSalleSignerUserId"),
isActive: boolean("isActive").default(true).notNull(),
failedLoginAttempts: int("failedLoginAttempts").default(0).notNull(),
lockedUntil: timestamp("lockedUntil"),
mfaEnabled: boolean("mfaEnabled").default(false).notNull(),
mfaMethod: varchar("mfaMethod", { length: 32 }),
mfaChallengeToken: varchar("mfaChallengeToken", { length: 96 }),
mfaCodeHash: varchar("mfaCodeHash", { length: 255 }),
mfaCodeExpiresAt: timestamp("mfaCodeExpiresAt"),
mfaCodeAttempts: int("mfaCodeAttempts").default(0).notNull(),
mfaTotpSecretEncrypted: varchar("mfaTotpSecretEncrypted", { length: 512 }),
mfaTotpPendingSecretEncrypted: varchar("mfaTotpPendingSecretEncrypted", { length: 512 }),
deletionRequestedAt: timestamp("deletionRequestedAt"),
purgeScheduledAt: timestamp("purgeScheduledAt"),
purgedAt: timestamp("purgedAt"),
legalHold: boolean("legalHold").default(false).notNull(),
legalHoldReason: text("legalHoldReason"),
privacyConsentVersion: varchar("privacyConsentVersion", { length: 32 }),
privacyConsentAcceptedAt: timestamp("privacyConsentAcceptedAt"),
privacyConsentContext: varchar("privacyConsentContext", { length: 64 }),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
lastSignedIn: timestamp("lastSignedIn").defaultNow().notNull(),
});
export type User = typeof users.$inferSelect;
export type InsertUser = typeof users.$inferInsert;
/**
* Association profile table - stores complete association information
*/
export const associations = mysqlTable("associations", {
id: int("id").autoincrement().primaryKey(),
userId: int("userId").notNull().unique(),
sourceDirectoryEntryId: int("sourceDirectoryEntryId"),
// Basic information
nomAssociation: varchar("nomAssociation", { length: 255 }).notNull(),
siret: varchar("siret", { length: 14 }),
rna: varchar("rna", { length: 10 }), // Numéro RNA (W + 9 chiffres)
thematique: text("thematique"),
// Address
adresse: text("adresse"),
codePostal: varchar("codePostal", { length: 10 }),
ville: varchar("ville", { length: 100 }),
// Contact
telephone: varchar("telephone", { length: 20 }),
emailContact: varchar("emailContact", { length: 320 }),
siteWeb: varchar("siteWeb", { length: 255 }),
facebookUrl: varchar("facebookUrl", { length: 255 }),
instagramUrl: varchar("instagramUrl", { length: 255 }),
// Legal information
dateCreation: timestamp("dateCreation"),
objetAssociation: text("objetAssociation"),
statutJuridique: mysqlEnum("statutJuridique", [
"association_loi_1901",
"association_reconnue_utilite_publique",
"fondation",
"autre"
]).default("association_loi_1901"),
// Representative
nomRepresentant: varchar("nomRepresentant", { length: 255 }),
fonctionRepresentant: varchar("fonctionRepresentant", { length: 100 }),
gouvernance: text("gouvernance"),
// Profile completion and status
profileComplete: boolean("profileComplete").default(false),
isActive: boolean("isActive").default(true).notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type Association = typeof associations.$inferSelect;
export type InsertAssociation = typeof associations.$inferInsert;
/**
* Imported association directory - source of truth from the Savanes registry.
*/
export const associationDirectoryEntries = mysqlTable("associationDirectoryEntries", {
id: int("id").autoincrement().primaryKey(),
nomAssociation: varchar("nomAssociation", { length: 255 }).notNull(),
emailOfficiel: varchar("emailOfficiel", { length: 320 }),
emailOfficielNormalise: varchar("emailOfficielNormalise", { length: 320 }).unique(),
siret: varchar("siret", { length: 14 }),
rna: varchar("rna", { length: 10 }),
thematique: text("thematique"),
adresse: text("adresse"),
codePostal: varchar("codePostal", { length: 10 }),
ville: varchar("ville", { length: 100 }),
telephone: varchar("telephone", { length: 20 }),
siteWeb: varchar("siteWeb", { length: 255 }),
facebookUrl: varchar("facebookUrl", { length: 255 }),
instagramUrl: varchar("instagramUrl", { length: 255 }),
dateCreation: timestamp("dateCreation"),
objetAssociation: text("objetAssociation"),
statutJuridique: mysqlEnum("statutJuridique", [
"association_loi_1901",
"association_reconnue_utilite_publique",
"fondation",
"autre"
]).default("association_loi_1901"),
nomRepresentant: varchar("nomRepresentant", { length: 255 }),
fonctionRepresentant: varchar("fonctionRepresentant", { length: 100 }),
gouvernance: text("gouvernance"),
latitude: varchar("latitude", { length: 32 }),
longitude: varchar("longitude", { length: 32 }),
geoSource: mysqlEnum("geoSource", associationGeoSources),
geoPrecision: mysqlEnum("geoPrecision", associationGeoPrecisions).default("commune_center").notNull(),
geoLastSyncedAt: timestamp("geoLastSyncedAt"),
externalSourceStatus: varchar("externalSourceStatus", { length: 100 }),
externalSourceLabel: varchar("externalSourceLabel", { length: 255 }),
associationStatus: varchar("associationStatus", { length: 40 }),
registryLastUpdatedAt: timestamp("registryLastUpdatedAt"),
referenceLastCheckedAt: timestamp("referenceLastCheckedAt"),
referenceStatus: varchar("referenceStatus", { length: 100 }),
referenceSourceLabel: varchar("referenceSourceLabel", { length: 255 }),
sourceFileName: varchar("sourceFileName", { length: 255 }),
sourceRowNumber: int("sourceRowNumber"),
sourceFingerprint: varchar("sourceFingerprint", { length: 64 }),
isActive: boolean("isActive").default(true).notNull(),
importedAt: timestamp("importedAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type AssociationDirectoryEntry = typeof associationDirectoryEntries.$inferSelect;
export type InsertAssociationDirectoryEntry = typeof associationDirectoryEntries.$inferInsert;
export const associationDirectoryReviewSourceTypes = ["portal_signup", "helloasso"] as const;
export const associationDirectoryReviewStatuses = ["pending", "linked", "created", "ignored"] as const;
export const associationDirectoryReviews = mysqlTable("associationDirectoryReviews", {
id: int("id").autoincrement().primaryKey(),
sourceType: mysqlEnum("sourceType", associationDirectoryReviewSourceTypes).notNull(),
sourceLabel: varchar("sourceLabel", { length: 255 }),
userId: int("userId"),
resolvedDirectoryEntryId: int("resolvedDirectoryEntryId"),
proposedNomAssociation: varchar("proposedNomAssociation", { length: 255 }).notNull(),
proposedEmail: varchar("proposedEmail", { length: 320 }),
proposedEmailNormalise: varchar("proposedEmailNormalise", { length: 320 }),
proposedSiret: varchar("proposedSiret", { length: 14 }),
proposedRna: varchar("proposedRna", { length: 10 }),
proposedVille: varchar("proposedVille", { length: 100 }),
matchStatus: varchar("matchStatus", { length: 40 }),
payload: text("payload"),
status: mysqlEnum("status", associationDirectoryReviewStatuses).default("pending").notNull(),
resolutionNote: text("resolutionNote"),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type AssociationDirectoryReview = typeof associationDirectoryReviews.$inferSelect;
export type InsertAssociationDirectoryReview = typeof associationDirectoryReviews.$inferInsert;
export const associationDirectoryUpdateProposalSourceTypes = ["official_registry", "helloasso"] as const;
export const associationDirectoryUpdateProposalStatuses = ["pending", "applied", "dismissed"] as const;
export const associationDirectoryUpdateProposals = mysqlTable("associationDirectoryUpdateProposals", {
id: int("id").autoincrement().primaryKey(),
directoryEntryId: int("directoryEntryId").notNull(),
sourceType: mysqlEnum("sourceType", associationDirectoryUpdateProposalSourceTypes).notNull(),
sourceLabel: varchar("sourceLabel", { length: 255 }),
summary: varchar("summary", { length: 255 }),
payload: text("payload").notNull(),
status: mysqlEnum("status", associationDirectoryUpdateProposalStatuses).default("pending").notNull(),
appliedAt: timestamp("appliedAt"),
dismissedAt: timestamp("dismissedAt"),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type AssociationDirectoryUpdateProposal = typeof associationDirectoryUpdateProposals.$inferSelect;
export type InsertAssociationDirectoryUpdateProposal = typeof associationDirectoryUpdateProposals.$inferInsert;
/**
* Document types for associations
*/
export const documentTypes = [
"statuts",
"recepisse_declaration",
"rib",
"rapport_activite",
"rapport_financier",
"pv_assemblee",
"liste_dirigeants",
"attestation_assurance",
"autre"
] as const;
/**
* Documents table - stores document metadata (files in S3)
*/
export const documents = mysqlTable("documents", {
id: int("id").autoincrement().primaryKey(),
associationId: int("associationId").notNull(),
nom: varchar("nom", { length: 255 }).notNull(),
type: mysqlEnum("type", documentTypes).notNull(),
description: text("description"),
// S3 storage
fileKey: varchar("fileKey", { length: 512 }).notNull(),
fileUrl: varchar("fileUrl", { length: 1024 }).notNull(),
mimeType: varchar("mimeType", { length: 100 }),
fileSize: int("fileSize"), // in bytes
uploadedAt: timestamp("uploadedAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type Document = typeof documents.$inferSelect;
export type InsertDocument = typeof documents.$inferInsert;
/**
* Request types available
*/
export const requestTypes = [
"subvention_fonctionnement",
"subvention_projet",
"agrement_jeunesse_education",
"agrement_sport",
"autorisation_occupation",
"demande_salle",
"demande_materiel_evenementiel",
"autre"
] as const;
/**
* Request status
*/
export const requestStatuses = [
"brouillon",
"soumise",
"en_cours_traitement",
"information_complementaire",
"validee",
"refusee",
"annulee"
] as const;
/**
* Request priority levels
*/
export const requestPriorities = [
"basse",
"normale",
"haute",
"urgente"
] as const;
/**
* Requests table - stores all requests from associations
*/
export const requests = mysqlTable("requests", {
id: int("id").autoincrement().primaryKey(),
associationId: int("associationId").notNull(),
// Request info
type: mysqlEnum("type", requestTypes).notNull(),
titre: varchar("titre", { length: 255 }).notNull(),
description: text("description"),
// Form data stored as JSON
formData: text("formData"), // JSON string
// Status tracking
status: mysqlEnum("status", requestStatuses).default("brouillon").notNull(),
priority: mysqlEnum("priority", requestPriorities).default("normale").notNull(),
// Financial (for subventions)
montantDemande: int("montantDemande"), // in cents
montantAccorde: int("montantAccorde"), // in cents
// Processing
dateSubmission: timestamp("dateSubmission"),
dateTraitement: timestamp("dateTraitement"),
dateLimiteTraitement: timestamp("dateLimiteTraitement"),
assigneA: int("assigneA"), // admin user id assigned to process
traitePar: int("traitePar"), // admin user id who processed
commentaireAdmin: text("commentaireAdmin"),
// Attached documents (JSON array of document IDs)
documentsJoints: text("documentsJoints"),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type Request = typeof requests.$inferSelect;
export type InsertRequest = typeof requests.$inferInsert;
/**
* Request history - tracks all actions on a request
*/
export const requestHistory = mysqlTable("requestHistory", {
id: int("id").autoincrement().primaryKey(),
requestId: int("requestId").notNull(),
action: mysqlEnum("action", [
"creation",
"soumission",
"assignation",
"changement_statut",
"ajout_commentaire",
"modification",
"validation",
"refus",
"changement_salle",
"annulation"
]).notNull(),
ancienStatut: mysqlEnum("ancienStatut", requestStatuses),
nouveauStatut: mysqlEnum("nouveauStatut", requestStatuses),
commentaire: text("commentaire"),
userId: int("userId").notNull(), // who performed the action
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type RequestHistory = typeof requestHistory.$inferSelect;
export type InsertRequestHistory = typeof requestHistory.$inferInsert;
/**
* Response templates - predefined responses for admins
*/
export const responseTemplates = mysqlTable("responseTemplates", {
id: int("id").autoincrement().primaryKey(),
nom: varchar("nom", { length: 255 }).notNull(),
type: mysqlEnum("type", ["validation", "refus", "information_complementaire", "autre"]).notNull(),
sujet: varchar("sujet", { length: 255 }),
contenu: text("contenu").notNull(),
actif: boolean("actif").default(true),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type ResponseTemplate = typeof responseTemplates.$inferSelect;
export type InsertResponseTemplate = typeof responseTemplates.$inferInsert;
/**
* Request templates - predefined form templates
*/
export const requestTemplates = mysqlTable("requestTemplates", {
id: int("id").autoincrement().primaryKey(),
type: mysqlEnum("type", requestTypes).notNull(),
nom: varchar("nom", { length: 255 }).notNull(),
description: text("description"),
// Form schema as JSON
formSchema: text("formSchema").notNull(),
// Required documents
documentsRequis: text("documentsRequis"), // JSON array
// Service destinataire
serviceDestinataire: varchar("serviceDestinataire", { length: 255 }),
emailDestinataire: varchar("emailDestinataire", { length: 320 }),
// Processing settings
delaiTraitementJours: int("delaiTraitementJours").default(30),
actif: boolean("actif").default(true),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type RequestTemplate = typeof requestTemplates.$inferSelect;
export type InsertRequestTemplate = typeof requestTemplates.$inferInsert;
/**
* Audit log - tracks all admin actions
*/
export const auditLog = mysqlTable("auditLog", {
id: int("id").autoincrement().primaryKey(),
userId: int("userId").notNull(),
action: varchar("action", { length: 100 }).notNull(),
entityType: varchar("entityType", { length: 50 }).notNull(), // association, request, user, etc.
entityId: int("entityId"),
details: text("details"), // JSON with action details
ipAddress: varchar("ipAddress", { length: 45 }),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type AuditLog = typeof auditLog.$inferSelect;
export type InsertAuditLog = typeof auditLog.$inferInsert;
/**
* Admin notifications
*/
export const adminNotifications = mysqlTable("adminNotifications", {
id: int("id").autoincrement().primaryKey(),
userId: int("userId"), // null = all admins
type: mysqlEnum("type", [
"nouvelle_demande",
"demande_en_retard",
"dossier_incomplet",
"nouvelle_association",
"systeme",
"changement_salle",
"annulation_demande",
"modification_demande"
]).notNull(),
titre: varchar("titre", { length: 255 }).notNull(),
message: text("message").notNull(),
lien: varchar("lien", { length: 512 }),
lu: boolean("lu").default(false),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type AdminNotification = typeof adminNotifications.$inferSelect;
export type InsertAdminNotification = typeof adminNotifications.$inferInsert;
/**
* Portal settings - configurable settings
*/
export const portalSettings = mysqlTable("portalSettings", {
id: int("id").autoincrement().primaryKey(),
cle: varchar("cle", { length: 100 }).notNull().unique(),
valeur: text("valeur").notNull(),
description: text("description"),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type PortalSetting = typeof portalSettings.$inferSelect;
export type InsertPortalSetting = typeof portalSettings.$inferInsert;
/**
* Operational recap services - reusable internal distribution lists for processed requests.
*/
export const operationalRecapServices = mysqlTable("operationalRecapServices", {
id: int("id").autoincrement().primaryKey(),
label: varchar("label", { length: 255 }).notNull(),
usage: mysqlEnum("usage", ["terrain", "controle"]).default("terrain").notNull(),
description: text("description"),
recipientEmails: text("recipientEmails").notNull(), // JSON array of emails
actif: boolean("actif").default(true).notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type OperationalRecapService = typeof operationalRecapServices.$inferSelect;
export type InsertOperationalRecapService = typeof operationalRecapServices.$inferInsert;
/**
* Association invitations - secure one-time invitations for directory onboarding.
*/
export const associationInvitations = mysqlTable("associationInvitations", {
id: int("id").autoincrement().primaryKey(),
directoryEntryId: int("directoryEntryId").notNull(),
emailOfficiel: varchar("emailOfficiel", { length: 320 }).notNull(),
emailOfficielNormalise: varchar("emailOfficielNormalise", { length: 320 }).notNull(),
token: varchar("token", { length: 128 }).notNull().unique(),
deliveryMode: mysqlEnum("deliveryMode", ["email", "manual_link"]).notNull(),
emailSent: boolean("emailSent").default(false).notNull(),
sentByUserId: int("sentByUserId").notNull(),
sentAt: timestamp("sentAt").defaultNow().notNull(),
expiresAt: timestamp("expiresAt").notNull(),
usedAt: timestamp("usedAt"),
revokedAt: timestamp("revokedAt"),
acceptedByUserId: int("acceptedByUserId"),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type AssociationInvitation = typeof associationInvitations.$inferSelect;
export type InsertAssociationInvitation = typeof associationInvitations.$inferInsert;
/**
* Email action tokens - secure one-time tokens for email-based actions (validate/refuse)
*/
export const emailActionTokens = mysqlTable("emailActionTokens", {
id: int("id").autoincrement().primaryKey(),
token: varchar("token", { length: 64 }).notNull().unique(),
requestId: int("requestId").notNull(),
action: mysqlEnum("action", ["validee", "refusee", "acceptation_devis_salle", "refus_devis_salle"]).notNull(),
used: boolean("used").default(false).notNull(),
expiresAt: timestamp("expiresAt").notNull(),
usedAt: timestamp("usedAt"),
usedBy: int("usedBy"), // admin user id
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type EmailActionToken = typeof emailActionTokens.$inferSelect;
export type InsertEmailActionToken = typeof emailActionTokens.$inferInsert;
export const materialReturnStatuses = [
"planifie",
"en_attente",
"en_cours",
"cloture",
] as const;
export const materialReturnComplianceValues = [
"conforme",
"non_conforme",
] as const;
export const materialReturnLitigationStatuses = [
"none",
"pending",
"resolved",
] as const;
export const materialReturnArbitrationDecisions = [
"partial_retention",
"full_retention",
"dismissed",
] as const;
export const materialReturnFollowups = mysqlTable("materialReturnFollowups", {
id: int("id").autoincrement().primaryKey(),
requestId: int("requestId").notNull().unique(),
associationId: int("associationId").notNull(),
serviceLabel: varchar("serviceLabel", { length: 255 }),
recipientEmails: text("recipientEmails"), // JSON array
supervisionServiceLabel: varchar("supervisionServiceLabel", { length: 255 }),
supervisionRecipientEmails: text("supervisionRecipientEmails"), // JSON array
restitutionDate: timestamp("restitutionDate").notNull(),
plannedSendAt: timestamp("plannedSendAt").notNull(),
sentAt: timestamp("sentAt"),
status: mysqlEnum("status", materialReturnStatuses).default("planifie").notNull(),
uploadToken: varchar("uploadToken", { length: 96 }).notNull().unique(),
uploadTokenExpiresAt: timestamp("uploadTokenExpiresAt").notNull(),
signedFileKey: varchar("signedFileKey", { length: 512 }),
signedFileUrl: varchar("signedFileUrl", { length: 1024 }),
signedFileName: varchar("signedFileName", { length: 255 }),
signedMimeType: varchar("signedMimeType", { length: 100 }),
uploadedByName: varchar("uploadedByName", { length: 255 }),
uploadedByEmail: varchar("uploadedByEmail", { length: 320 }),
agentUserId: int("agentUserId"),
agentRole: varchar("agentRole", { length: 100 }),
borrowerName: varchar("borrowerName", { length: 255 }),
borrowerRole: varchar("borrowerRole", { length: 120 }),
compliance: mysqlEnum("compliance", materialReturnComplianceValues),
discrepancyCategories: text("discrepancyCategories"),
discrepancyDetails: text("discrepancyDetails"),
issueFlag: boolean("issueFlag").default(false).notNull(),
litigationStatus: mysqlEnum("litigationStatus", materialReturnLitigationStatuses).default("none").notNull(),
blockedItems: text("blockedItems"),
estimatedDamageAmount: int("estimatedDamageAmount"),
estimatedDamageSource: varchar("estimatedDamageSource", { length: 32 }),
arbitrationDecision: mysqlEnum("arbitrationDecision", materialReturnArbitrationDecisions),
arbitrationAmount: int("arbitrationAmount"),
arbitrationNotes: text("arbitrationNotes"),
arbitratedByUserId: int("arbitratedByUserId"),
arbitratedAt: timestamp("arbitratedAt"),
litigationLetterKey: varchar("litigationLetterKey", { length: 512 }),
litigationLetterUrl: varchar("litigationLetterUrl", { length: 1024 }),
litigationLetterName: varchar("litigationLetterName", { length: 255 }),
agentSignatureKey: varchar("agentSignatureKey", { length: 512 }),
agentSignatureUrl: varchar("agentSignatureUrl", { length: 1024 }),
borrowerSignatureKey: varchar("borrowerSignatureKey", { length: 512 }),
borrowerSignatureUrl: varchar("borrowerSignatureUrl", { length: 1024 }),
finalPdfKey: varchar("finalPdfKey", { length: 512 }),
finalPdfUrl: varchar("finalPdfUrl", { length: 1024 }),
finalPdfName: varchar("finalPdfName", { length: 255 }),
geoLatitude: varchar("geoLatitude", { length: 64 }),
geoLongitude: varchar("geoLongitude", { length: 64 }),
geoStatus: varchar("geoStatus", { length: 32 }),
geoFailureReason: text("geoFailureReason"),
uploadedAt: timestamp("uploadedAt"),
validatedAt: timestamp("validatedAt"),
closedAt: timestamp("closedAt"),
lastReminderSentAt: timestamp("lastReminderSentAt"),
reactivatedByUserId: int("reactivatedByUserId"),
reactivatedAt: timestamp("reactivatedAt"),
reactivationReason: text("reactivationReason"),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type MaterialReturnFollowup = typeof materialReturnFollowups.$inferSelect;
export type InsertMaterialReturnFollowup = typeof materialReturnFollowups.$inferInsert;