From e75fa65945eb6db00f44e6988a00bc2e098d464f Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Mon, 8 Sep 2025 13:36:45 +0000 Subject: [PATCH] Add a system for managing customer credit notes and their associated permissions Define new 'avoir' module for permissions and create a new 'avoirs' table in the database schema with detailed fields for credit note management, including supplier, group, invoice details, status, and processing flags. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869/0C3uBiZ --- shared/permissions.ts | 11 +++++++- shared/schema.ts | 61 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/shared/permissions.ts b/shared/permissions.ts index c978306..6d96070 100644 --- a/shared/permissions.ts +++ b/shared/permissions.ts @@ -12,7 +12,8 @@ export type Module = | 'publicity' | 'customer-orders' | 'dlc' - | 'tasks'; + | 'tasks' + | 'avoir'; // Définition des permissions par module et par rôle const PERMISSIONS: Record> = { @@ -86,6 +87,14 @@ const PERMISSIONS: Record> = { directeur: ['view', 'create', 'edit', 'delete', 'validate'], manager: ['view', 'create', 'edit', 'validate'], employee: ['view'] + }, + + // Avoirs - Admin/Directeur tout, Manager create + view seulement, Employé pas d'accès + avoir: { + admin: ['view', 'create', 'edit', 'delete', 'validate'], + directeur: ['view', 'create', 'edit', 'delete', 'validate'], + manager: ['view', 'create'], + employee: [] } }; diff --git a/shared/schema.ts b/shared/schema.ts index c3c6c2f..fd4de86 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -262,6 +262,25 @@ export const tasks = pgTable("tasks", { updatedAt: timestamp("updated_at").defaultNow(), }); +// Avoirs table +export const avoirs = pgTable("avoirs", { + id: serial("id").primaryKey(), + supplierId: integer("supplier_id").notNull(), // Fournisseur + groupId: integer("group_id").notNull(), // Magasin/groupe associé - OBLIGATOIRE pour filtrage + invoiceReference: varchar("invoice_reference", { length: 255 }).notNull(), // Référence facture + amount: decimal("amount", { precision: 10, scale: 2 }).notNull(), // Montant + comment: text("comment"), // Commentaire (optionnel) + commercialProcessed: boolean("commercial_processed").default(false), // Avoir fait par commercial + status: varchar("status", { length: 50 }).notNull().default("En attente de demande"), // En attente de demande, Demandé, Reçu + webhookSent: boolean("webhook_sent").default(false), // Webhook envoyé + nocodbVerified: boolean("nocodb_verified").default(false), // Vérifié dans NocoDB (coche verte) + nocodbVerifiedAt: timestamp("nocodb_verified_at"), // Date de vérification + processedAt: timestamp("processed_at"), // Date de passage au statut "Reçu" + createdBy: varchar("created_by").notNull(), // Utilisateur créateur + createdAt: timestamp("created_at").defaultNow(), + updatedAt: timestamp("updated_at").defaultNow(), +}); + // Dashboard Messages - Utilise la table existante DASHBOARD_MESSAGES export const dashboardMessages = pgTable("dashboard_messages", { id: serial("id").primaryKey(), @@ -287,6 +306,7 @@ export const usersRelations = relations(users, ({ many }) => ({ createdTasks: many(tasks), assignedTasks: many(tasks), createdAnnouncements: many(dashboardMessages), + createdAvoirs: many(avoirs), })); export const groupsRelations = relations(groups, ({ one, many }) => ({ @@ -298,6 +318,7 @@ export const groupsRelations = relations(groups, ({ one, many }) => ({ dlcProducts: many(dlcProducts), tasks: many(tasks), announcements: many(dashboardMessages), + avoirs: many(avoirs), })); export const userGroupsRelations = relations(userGroups, ({ one }) => ({ @@ -315,6 +336,7 @@ export const suppliersRelations = relations(suppliers, ({ many }) => ({ orders: many(orders), deliveries: many(deliveries), dlcProducts: many(dlcProducts), + avoirs: many(avoirs), })); export const ordersRelations = relations(orders, ({ one, many }) => ({ @@ -418,6 +440,21 @@ export const tasksRelations = relations(tasks, ({ one }) => ({ }), })); +export const avoirsRelations = relations(avoirs, ({ one }) => ({ + supplier: one(suppliers, { + fields: [avoirs.supplierId], + references: [suppliers.id], + }), + group: one(groups, { + fields: [avoirs.groupId], + references: [groups.id], + }), + creator: one(users, { + fields: [avoirs.createdBy], + references: [users.id], + }), +})); + export const dashboardMessagesRelations = relations(dashboardMessages, ({ one }) => ({ author: one(users, { fields: [dashboardMessages.createdBy], @@ -538,6 +575,22 @@ export const insertTaskSchema = createInsertSchema(tasks).omit({ dueDate: z.coerce.date().optional().nullable(), // Date d'échéance (optionnel) }); +export const insertAvoirSchema = createInsertSchema(avoirs).omit({ + id: true, + createdAt: true, + updatedAt: true, + webhookSent: true, + nocodbVerified: true, + nocodbVerifiedAt: true, + processedAt: true, +}).extend({ + status: z.enum(["En attente de demande", "Demandé", "Reçu"]).default("En attente de demande"), + amount: z.coerce.number().positive(), // Montant doit être positif + invoiceReference: z.string().min(1, "La référence facture est obligatoire"), + comment: z.string().optional(), + commercialProcessed: z.boolean().default(false), +}); + export const insertDashboardMessageSchema = createInsertSchema(dashboardMessages).omit({ id: true, createdAt: true, @@ -601,6 +654,8 @@ export type DlcProductFrontend = Omit & { dlcDate: Dat export type InsertDlcProductFrontend = z.infer; export type Task = typeof tasks.$inferSelect; export type InsertTask = z.infer; +export type Avoir = typeof avoirs.$inferSelect; +export type InsertAvoir = z.infer; export type DashboardMessage = typeof dashboardMessages.$inferSelect; export type InsertDashboardMessage = z.infer; @@ -656,6 +711,12 @@ export type TaskWithRelations = Task & { isFutureTask?: boolean; // Pour distinguer les tâches futures (admin/directeur) }; +export type AvoirWithRelations = Avoir & { + supplier: Supplier; + group: Group; + creator: User; +}; + // Dashboard Message with relations type export type DashboardMessageWithRelations = DashboardMessage & { author: User;