Files
LogiFlow/shared/schema.ts
T

974 lines
36 KiB
TypeScript

import {
pgTable,
text,
varchar,
timestamp,
jsonb,
index,
serial,
integer,
boolean,
date,
decimal,
primaryKey,
bigint,
} from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
// Session storage table - mandatory for Replit Auth
export const sessions = pgTable(
"sessions",
{
sid: varchar("sid").primaryKey(),
sess: jsonb("sess").notNull(),
expire: timestamp("expire").notNull(),
},
(table) => [index("IDX_session_expire").on(table.expire)],
);
// User storage table - supports both Replit Auth and local auth
export const users = pgTable("users", {
id: varchar("id").primaryKey().notNull(),
username: varchar("username").unique().notNull(), // For simple login
email: varchar("email").unique(),
name: varchar("name"), // Single name field for compatibility
firstName: varchar("first_name"),
lastName: varchar("last_name"),
profileImageUrl: varchar("profile_image_url"),
password: varchar("password"), // For local auth only
role: varchar("role").notNull().default("employee"), // Legacy role field for compatibility
passwordChanged: boolean("password_changed").default(false), // Track if default password was changed
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Store/Group management
export const groups = pgTable("groups", {
id: serial("id").primaryKey(),
name: varchar("name").notNull(),
color: varchar("color").default("#1976D2"), // hex color code
// Configuration NocoDB pour vérification automatique des factures
nocodbConfigId: integer("nocodb_config_id"), // Référence vers la configuration NocoDB globale
nocodbTableName: varchar("nocodb_table_name"), // Nom de la table spécifique au magasin
nocodbTableId: varchar("nocodb_table_id"), // ID réel de la table dans NocoDB (ex: mrr733dfb8wtt9b)
// Mapping des colonnes par magasin dans leur table NocoDB
invoiceColumnName: varchar("invoice_column_name"), // Nom de la colonne facture dans leur table
nocodbBlColumnName: varchar("nocodb_bl_column_name"), // Nom de la colonne BL dans leur table
nocodbAmountColumnName: varchar("nocodb_amount_column_name"), // Nom de la colonne montant HT dans leur table
nocodbInvoiceAmountTTCColumnName: varchar("nocodb_invoice_amount_ttc_column_name"), // Nom de la colonne montant TTC dans leur table
nocodbSupplierColumnName: varchar("nocodb_supplier_column_name"), // Nom de la colonne fournisseur dans leur table
nocodbDueDateColumnName: varchar("nocodb_due_date_column_name"), // Nom de la colonne date d'échéance dans leur table
webhookUrl: varchar("webhook_url", { length: 500 }), // URL de webhook pour notifications par magasin
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// User-Group assignments (many-to-many)
export const userGroups = pgTable("user_groups", {
userId: varchar("user_id").notNull(),
groupId: integer("group_id").notNull(),
createdAt: timestamp("created_at").defaultNow(),
});
// Suppliers - Schema exact correspondant à la production
export const suppliers = pgTable("suppliers", {
id: serial("id").primaryKey(),
name: varchar("name").notNull(),
contact: varchar("contact"),
phone: varchar("phone"),
codefou: varchar("codefou"), // Code fournisseur dans l'API ffnancy
email: varchar("email"),
hasDlc: boolean("has_dlc").default(false), // Coche DLC pour la gestion DLC
paymentMethod: varchar("payment_method"), // Mode de paiement: Virement, Traite, Traite Magnétique, Chèque
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
automaticReconciliation: boolean("automatic_reconciliation").default(false), // Rapprochement automatique BL/Factures
requiresControl: boolean("requires_control").default(false), // Fournisseur nécessite un contrôle à la livraison
});
// Contacts libres (hors fournisseurs), rattachés à un magasin
export const contacts = pgTable("contacts", {
id: serial("id").primaryKey(),
groupId: integer("group_id").notNull(),
name: varchar("name").notNull(),
role: varchar("role"),
phone: varchar("phone"),
email: varchar("email"),
notes: text("notes"),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Orders
export const orders = pgTable("orders", {
id: serial("id").primaryKey(),
supplierId: integer("supplier_id").notNull(),
groupId: integer("group_id").notNull(),
plannedDate: date("planned_date").notNull(),
quantity: integer("quantity"), // Optional - will be set when delivery is linked
unit: varchar("unit"), // Optional - 'palettes' or 'colis'
status: varchar("status").notNull().default("pending"), // pending, planned, delivered
notes: text("notes"),
createdBy: varchar("created_by").notNull(),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Deliveries
export const deliveries = pgTable("deliveries", {
id: serial("id").primaryKey(),
orderId: integer("order_id"), // optional link to order
supplierId: integer("supplier_id").notNull(),
groupId: integer("group_id").notNull(),
scheduledDate: date("scheduled_date").notNull(),
deliveredDate: timestamp("delivered_date"),
quantity: integer("quantity").notNull(),
unit: varchar("unit").notNull(), // 'palettes' or 'colis'
status: varchar("status").notNull().default("planned"), // planned, delivered
notes: text("notes"),
// Champs pour le rapprochement BL/Factures
blNumber: varchar("bl_number"), // Numéro de Bon de Livraison
blAmount: decimal("bl_amount", { precision: 10, scale: 2 }), // Montant BL
invoiceReference: varchar("invoice_reference"), // Référence facture
invoiceAmount: decimal("invoice_amount", { precision: 10, scale: 2 }), // Montant facture HT
invoiceAmountTTC: decimal("invoice_amount_ttc", { precision: 10, scale: 2 }), // Montant facture TTC
dueDate: timestamp("due_date"), // Date d'échéance de paiement (récupérée depuis NocoDB)
reconciled: boolean("reconciled").default(false), // Rapprochement effectué
validatedAt: timestamp("validated_at"), // Date de validation de la livraison
// Champs pour le contrôle des livraisons (fournisseurs nécessitant un contrôle)
controlValidated: boolean("control_validated").default(false), // Contrôle effectué
controlValidatedBy: varchar("control_validated_by"), // Utilisateur ayant validé le contrôle
controlValidatedAt: timestamp("control_validated_at"), // Date de validation du contrôle
createdBy: varchar("created_by").notNull(),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Publicities
export const publicities = pgTable("publicities", {
id: serial("id").primaryKey(),
pubNumber: varchar("pub_number").notNull().unique(),
designation: text("designation").notNull(),
startDate: date("start_date").notNull(),
endDate: date("end_date").notNull(),
year: integer("year").notNull(),
createdBy: varchar("created_by").notNull(),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Publicity Participations
export const publicityParticipations = pgTable("publicity_participations", {
publicityId: integer("publicity_id").notNull(),
groupId: integer("group_id").notNull(),
}, (table) => ({
pk: primaryKey({ columns: [table.publicityId, table.groupId] })
}));
// Configuration NocoDB globale pour la vérification des factures
export const nocodbConfig = pgTable("nocodb_config", {
id: serial("id").primaryKey(),
name: varchar("name").notNull(),
baseUrl: varchar("base_url").notNull(),
projectId: varchar("project_id").notNull(),
apiToken: varchar("api_token").notNull(),
description: text("description"),
isActive: boolean("is_active").default(true),
createdBy: varchar("created_by"),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Cache de vérification des factures
export const invoiceVerificationCache = pgTable("invoice_verification_cache", {
id: serial("id").primaryKey(),
cacheKey: varchar("cache_key", { length: 255 }).notNull().unique(),
groupId: integer("group_id").notNull(),
invoiceReference: varchar("invoice_reference", { length: 255 }).notNull(),
supplierName: varchar("supplier_name", { length: 255 }),
invoiceAmount: decimal("invoice_amount", { precision: 10, scale: 2 }), // Montant de la facture HT
invoiceAmountTTC: decimal("invoice_amount_ttc", { precision: 10, scale: 2 }), // Montant de la facture TTC
dueDate: timestamp("due_date"), // Date d'échéance
exists: boolean("exists").notNull(),
matchType: varchar("match_type", { length: 50 }).notNull(),
errorMessage: text("error_message"),
cacheHit: boolean("cache_hit").default(false),
apiCallTime: integer("api_call_time"),
isReconciled: boolean("is_reconciled").default(false), // Si true, cache permanent
expiresAt: timestamp("expires_at").notNull(),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Commentaires de rapprochement - Système indépendant pour le module de rapprochement
export const reconciliationComments = pgTable("reconciliation_comments", {
id: serial("id").primaryKey(),
deliveryId: integer("delivery_id").notNull(), // Référence vers la livraison concernée
groupId: integer("group_id").notNull(), // Magasin/groupe pour filtrage
content: text("content").notNull(), // Contenu du commentaire
type: varchar("type").notNull().default("info"), // Type du commentaire: info, warning, error, success
authorId: varchar("author_id").notNull(), // Utilisateur auteur du commentaire
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Customer Orders (Commandes Client)
export const customerOrders = pgTable("customer_orders", {
id: serial("id").primaryKey(),
// Information de commande
orderTaker: varchar("order_taker").notNull(), // Qui a pris la commande
customerName: varchar("customer_name").notNull(), // Nom du client
customerPhone: varchar("customer_phone").notNull(), // N° de téléphone
customerEmail: varchar("customer_email"), // Email du client (optionnel)
// Information produit
productDesignation: text("product_designation").notNull(), // Désignation du produit
productReference: varchar("product_reference"), // Référence
gencode: varchar("gencode").notNull(), // Code à barres (obligatoire)
quantity: integer("quantity").notNull().default(1), // Quantité commandée
supplierId: integer("supplier_id").notNull(), // Fournisseur
// Statuts
status: varchar("status").notNull().default("En attente de Commande"), // Statut du produit
// Options financières
deposit: decimal("deposit", { precision: 10, scale: 2 }).default("0.00"), // Acompte
isPromotionalPrice: boolean("is_promotional_price").default(false), // Prix publicité
// Communication client
customerNotified: boolean("customer_notified").default(false), // Client appelé
notifiedAt: timestamp("notified_at"), // Date/heure du contact
notifiedComment: text("notified_comment"), // Commentaire lors de l'appel
// Notes additionnelles
notes: text("notes"), // Notes sur la commande
// Métadonnées
groupId: integer("group_id").notNull(), // Magasin
createdBy: varchar("created_by").notNull(), // Créateur
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// DLC Products (Date Limite de Consommation)
export const dlcProducts = pgTable("dlc_products", {
id: serial("id").primaryKey(),
name: varchar("name").notNull(), // Nom du produit (requis dans la base de données)
productName: varchar("product_name"), // Nom du produit (champ alternatif)
gencode: varchar("gencode"), // Code-barres/gencode EAN13 (optionnel)
supplierId: integer("supplier_id").notNull(), // ID du fournisseur
groupId: integer("group_id").notNull(), // ID du magasin/groupe
expiryDate: date("expiry_date").notNull(), // Date limite de consommation
dateType: varchar("date_type").notNull().default("dlc"), // Type: dlc, ddm, dluo
quantity: integer("quantity").notNull().default(1), // Quantité
unit: varchar("unit").notNull().default("unité"), // Unité (fixe par défaut)
location: varchar("location").notNull().default("Magasin"), // Emplacement (fixe par défaut)
alertThreshold: integer("alert_threshold").notNull().default(15), // Seuil d'alerte fixe à 15 jours
status: varchar("status").notNull().default("en_cours"), // Statut: en_cours, expires_soon, expires, valides
stockEpuise: boolean("stock_epuise").default(false), // Produit épuisé en stock (pas périmé)
stockEpuiseBy: varchar("stock_epuise_by"), // Marqué comme épuisé par (optionnel)
stockEpuiseAt: timestamp("stock_epuise_at"), // Date de marquage épuisé (optionnel)
processedAt: timestamp("processed_at"), // Date de traitement "expire bientôt" (optionnel)
processedBy: varchar("processed_by"), // Qui a marqué comme traité (optionnel)
processedUntilExpiry: boolean("processed_until_expiry").default(false), // Traité jusqu'à expiration
notes: text("notes"), // Notes (optionnel)
createdBy: varchar("created_by").notNull(), // Créateur
validatedBy: varchar("validated_by"), // Validé par (optionnel)
validatedAt: timestamp("validated_at"), // Date de validation (optionnel)
createdAt: timestamp("created_at").defaultNow(), // Date de création
updatedAt: timestamp("updated_at").defaultNow(), // Date de mise à jour
});
// Tasks table
export const tasks = pgTable("tasks", {
id: serial("id").primaryKey(),
title: varchar("title").notNull(), // Titre de la tâche (requis)
description: text("description"), // Description de la tâche (optionnel)
startDate: timestamp("start_date"), // Date de départ (optionnel) - aligné avec production
dueDate: date("due_date"), // Date d'échéance (optionnel)
priority: varchar("priority").notNull().default("medium"), // low, medium, high
status: varchar("status").notNull().default("pending"), // pending, completed
assignedTo: text("assigned_to").notNull(), // Utilisateur responsable (champ libre)
createdBy: varchar("created_by").notNull(), // Utilisateur créateur
groupId: integer("group_id").notNull(), // Magasin/groupe associé
completedAt: timestamp("completed_at"), // Date de completion
completedBy: varchar("completed_by"), // Utilisateur qui a complété la tâche
createdAt: timestamp("created_at").defaultNow(),
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 }), // Référence facture (OPTIONNEL)
amount: decimal("amount", { precision: 10, scale: 2 }), // Montant (OPTIONNEL)
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(),
title: varchar("title", { length: 255 }).notNull(), // Titre du message
content: text("content").notNull(), // Contenu du message
type: varchar("type", { length: 50 }).notNull().default("info"), // info, warning, error, success
storeId: integer("store_id"), // Filtrage par magasin (null = global)
createdBy: varchar("created_by", { length: 255 }).notNull(), // Référence vers l'utilisateur créateur
createdAt: timestamp("created_at").defaultNow(),
});
// Alias pour la compatibilité avec le code existant
export const announcements = dashboardMessages;
// Relations
export const usersRelations = relations(users, ({ many }) => ({
userGroups: many(userGroups),
createdOrders: many(orders),
createdDeliveries: many(deliveries),
createdPublicities: many(publicities),
createdCustomerOrders: many(customerOrders),
createdDlcProducts: many(dlcProducts),
createdTasks: many(tasks),
assignedTasks: many(tasks),
createdAnnouncements: many(dashboardMessages),
createdAvoirs: many(avoirs),
createdReconciliationComments: many(reconciliationComments),
}));
export const groupsRelations = relations(groups, ({ one, many }) => ({
userGroups: many(userGroups),
orders: many(orders),
deliveries: many(deliveries),
publicityParticipations: many(publicityParticipations),
customerOrders: many(customerOrders),
dlcProducts: many(dlcProducts),
tasks: many(tasks),
announcements: many(dashboardMessages),
avoirs: many(avoirs),
reconciliationComments: many(reconciliationComments),
}));
export const userGroupsRelations = relations(userGroups, ({ one }) => ({
user: one(users, {
fields: [userGroups.userId],
references: [users.id],
}),
group: one(groups, {
fields: [userGroups.groupId],
references: [groups.id],
}),
}));
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 }) => ({
supplier: one(suppliers, {
fields: [orders.supplierId],
references: [suppliers.id],
}),
group: one(groups, {
fields: [orders.groupId],
references: [groups.id],
}),
creator: one(users, {
fields: [orders.createdBy],
references: [users.id],
}),
deliveries: many(deliveries),
}));
export const deliveriesRelations = relations(deliveries, ({ one, many }) => ({
order: one(orders, {
fields: [deliveries.orderId],
references: [orders.id],
}),
supplier: one(suppliers, {
fields: [deliveries.supplierId],
references: [suppliers.id],
}),
group: one(groups, {
fields: [deliveries.groupId],
references: [groups.id],
}),
creator: one(users, {
fields: [deliveries.createdBy],
references: [users.id],
}),
reconciliationComments: many(reconciliationComments),
}));
export const publicitiesRelations = relations(publicities, ({ one, many }) => ({
creator: one(users, {
fields: [publicities.createdBy],
references: [users.id],
}),
participations: many(publicityParticipations),
}));
export const publicityParticipationsRelations = relations(publicityParticipations, ({ one }) => ({
publicity: one(publicities, {
fields: [publicityParticipations.publicityId],
references: [publicities.id],
}),
group: one(groups, {
fields: [publicityParticipations.groupId],
references: [groups.id],
}),
}));
export const customerOrdersRelations = relations(customerOrders, ({ one }) => ({
group: one(groups, {
fields: [customerOrders.groupId],
references: [groups.id],
}),
creator: one(users, {
fields: [customerOrders.createdBy],
references: [users.id],
}),
supplier: one(suppliers, {
fields: [customerOrders.supplierId],
references: [suppliers.id],
}),
}));
export const dlcProductsRelations = relations(dlcProducts, ({ one }) => ({
supplier: one(suppliers, {
fields: [dlcProducts.supplierId],
references: [suppliers.id],
}),
group: one(groups, {
fields: [dlcProducts.groupId],
references: [groups.id],
}),
creator: one(users, {
fields: [dlcProducts.createdBy],
references: [users.id],
}),
validator: one(users, {
fields: [dlcProducts.validatedBy],
references: [users.id],
}),
}));
export const tasksRelations = relations(tasks, ({ one }) => ({
creator: one(users, {
fields: [tasks.createdBy],
references: [users.id],
}),
group: one(groups, {
fields: [tasks.groupId],
references: [groups.id],
}),
}));
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],
references: [users.id],
}),
group: one(groups, {
fields: [dashboardMessages.storeId],
references: [groups.id],
}),
}));
// Alias pour compatibilité
export const announcementsRelations = dashboardMessagesRelations;
// Relations pour les commentaires de rapprochement
export const reconciliationCommentsRelations = relations(reconciliationComments, ({ one }) => ({
delivery: one(deliveries, {
fields: [reconciliationComments.deliveryId],
references: [deliveries.id],
}),
group: one(groups, {
fields: [reconciliationComments.groupId],
references: [groups.id],
}),
author: one(users, {
fields: [reconciliationComments.authorId],
references: [users.id],
}),
}));
// Zod schemas
export const insertUserSchema = createInsertSchema(users).pick({
id: true,
username: true,
email: true,
firstName: true,
lastName: true,
name: true,
profileImageUrl: true,
password: true,
role: true,
passwordChanged: true,
}).extend({
email: z.union([z.string().email(), z.literal("")]).optional(),
});
export const insertGroupSchema = createInsertSchema(groups).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertSupplierSchema = createInsertSchema(suppliers).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertContactSchema = createInsertSchema(contacts).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertOrderSchema = createInsertSchema(orders).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertDeliverySchema = createInsertSchema(deliveries).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertUserGroupSchema = createInsertSchema(userGroups).omit({
createdAt: true,
});
export const insertPublicitySchema = createInsertSchema(publicities).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertPublicityParticipationSchema = createInsertSchema(publicityParticipations);
export const insertCustomerOrderSchema = createInsertSchema(customerOrders).omit({
id: true,
createdAt: true,
updatedAt: true,
});
// Frontend-compatible schema with different field names
export const insertCustomerOrderFrontendSchema = z.object({
customerName: z.string().min(1, "Customer name is required"),
contactNumber: z.string().min(1, "Contact number is required"), // Maps to customerPhone
productName: z.string().min(1, "Product name is required"), // Maps to productDesignation
productDescription: z.string().optional(), // Optional description
quantity: z.coerce.number().int().positive().default(1),
groupId: z.coerce.number().int().positive(),
isPickup: z.boolean().default(false),
notes: z.string().optional(),
// Optional fields with defaults
orderTaker: z.string().optional(), // Will be set to creator name if not provided
gencode: z.string().optional().default(""), // Will be empty if not provided
supplierId: z.coerce.number().int().positive().optional().default(1), // Default supplier ID
deposit: z.coerce.number().optional().default(0),
isPromotionalPrice: z.boolean().default(false),
customerEmail: z.string().email().optional().or(z.literal("")),
productReference: z.string().optional(),
});
export const insertDlcProductSchema = createInsertSchema(dlcProducts).omit({
id: true,
createdAt: true,
updatedAt: true,
});
// Frontend-compatible schema with dlcDate instead of expiryDate
export const insertDlcProductFrontendSchema = insertDlcProductSchema
.omit({ expiryDate: true })
.extend({ dlcDate: z.coerce.date() });
export const insertTaskSchema = createInsertSchema(tasks).omit({
id: true,
createdAt: true,
updatedAt: true,
completedAt: true,
completedBy: true,
}).extend({
startDate: z.coerce.date().optional().nullable(), // Date de départ (optionnel)
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().optional().nullable(), // Montant optionnel, peut être vide, zéro, positif ou négatif
invoiceReference: z.string().optional(), // Référence facture optionnelle
comment: z.string().optional(),
commercialProcessed: z.boolean().default(false),
});
export const insertDashboardMessageSchema = createInsertSchema(dashboardMessages).omit({
id: true,
createdAt: true,
});
export const insertAnnouncementSchema = insertDashboardMessageSchema;
export const insertReconciliationCommentSchema = createInsertSchema(reconciliationComments).omit({
id: true,
createdAt: true,
updatedAt: true,
}).extend({
type: z.enum(["info", "warning", "error", "success"]).default("info"),
});
export const insertNocodbConfigSchema = createInsertSchema(nocodbConfig).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertInvoiceVerificationCacheSchema = createInsertSchema(invoiceVerificationCache).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const databaseBackups = pgTable("database_backups", {
id: varchar("id", { length: 255 }).primaryKey(),
filename: varchar("filename", { length: 255 }).notNull(),
description: text("description"),
size: bigint("size", { mode: "number" }).default(0),
createdAt: timestamp("created_at").defaultNow(),
createdBy: varchar("created_by", { length: 255 }).notNull(),
tablesCount: integer("tables_count").default(0),
status: varchar("status", { length: 50 }).default("creating"),
backupType: varchar("backup_type", { length: 10 }).default("manual"),
});
export const insertDatabaseBackupSchema = createInsertSchema(databaseBackups);
// Utilities - Configuration générale de l'application
export const utilities = pgTable("utilities", {
id: serial("id").primaryKey(),
salesAnalysisUrl: varchar("sales_analysis_url", { length: 500 }), // URL pour l'analyse des ventes
automaticBackupsEnabled: boolean("automatic_backups_enabled").default(true), // Activer/désactiver les backups automatiques
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
export const insertUtilitiesSchema = createInsertSchema(utilities).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export type InsertNocodbConfig = z.infer<typeof insertNocodbConfigSchema>;
export type InsertInvoiceVerificationCache = z.infer<typeof insertInvoiceVerificationCacheSchema>;
export type DatabaseBackup = typeof databaseBackups.$inferSelect;
export type InsertDatabaseBackup = z.infer<typeof insertDatabaseBackupSchema>;
export type Utilities = typeof utilities.$inferSelect;
export type InsertUtilities = z.infer<typeof insertUtilitiesSchema>;
// Types
export type User = typeof users.$inferSelect;
export type UpsertUser = z.infer<typeof insertUserSchema>;
export type Group = typeof groups.$inferSelect;
export type InsertGroup = z.infer<typeof insertGroupSchema>;
export type Supplier = typeof suppliers.$inferSelect;
export type InsertSupplier = z.infer<typeof insertSupplierSchema>;
export type Contact = typeof contacts.$inferSelect;
export type InsertContact = z.infer<typeof insertContactSchema>;
export type Order = typeof orders.$inferSelect;
export type InsertOrder = z.infer<typeof insertOrderSchema>;
export type Delivery = typeof deliveries.$inferSelect;
export type InsertDelivery = z.infer<typeof insertDeliverySchema>;
export type UserGroup = typeof userGroups.$inferSelect;
export type InsertUserGroup = z.infer<typeof insertUserGroupSchema>;
export type Publicity = typeof publicities.$inferSelect;
export type InsertPublicity = z.infer<typeof insertPublicitySchema>;
export type PublicityParticipation = typeof publicityParticipations.$inferSelect;
export type InsertPublicityParticipation = z.infer<typeof insertPublicityParticipationSchema>;
export type CustomerOrder = typeof customerOrders.$inferSelect;
export type InsertCustomerOrder = z.infer<typeof insertCustomerOrderSchema>;
export type DlcProduct = typeof dlcProducts.$inferSelect;
export type InsertDlcProduct = z.infer<typeof insertDlcProductSchema>;
export type DlcProductFrontend = Omit<DlcProduct, 'expiryDate'> & { dlcDate: Date };
export type InsertDlcProductFrontend = z.infer<typeof insertDlcProductFrontendSchema>;
export type Task = typeof tasks.$inferSelect;
export type InsertTask = z.infer<typeof insertTaskSchema>;
export type Avoir = typeof avoirs.$inferSelect;
export type InsertAvoir = z.infer<typeof insertAvoirSchema>;
export type DashboardMessage = typeof dashboardMessages.$inferSelect;
export type InsertDashboardMessage = z.infer<typeof insertDashboardMessageSchema>;
export type ReconciliationComment = typeof reconciliationComments.$inferSelect;
export type InsertReconciliationComment = z.infer<typeof insertReconciliationCommentSchema>;
// Alias pour compatibilité
export type Announcement = DashboardMessage;
export type InsertAnnouncement = InsertDashboardMessage;
// Complex types with relations
export type OrderWithRelations = Order & {
supplier: Supplier;
group: Group;
creator: User;
deliveries?: DeliveryWithRelations[];
};
export type DeliveryWithRelations = Delivery & {
supplier: Supplier;
group: Group;
creator: User;
order?: OrderWithRelations;
reconciliationCommentsCount?: number;
};
export type PublicityWithRelations = Publicity & {
creator: User;
participations: (PublicityParticipation & { group: Group })[];
};
export type CustomerOrderWithRelations = CustomerOrder & {
supplier: Supplier;
group: Group;
creator: User;
};
export type DlcProductWithRelations = DlcProduct & {
supplier: Supplier;
group: Group;
creator: User;
validator?: User;
};
export type UserWithGroups = User & {
userGroups: (UserGroup & { group: Group })[];
};
export type NocodbConfig = typeof nocodbConfig.$inferSelect;
export type InvoiceVerificationCache = typeof invoiceVerificationCache.$inferSelect;
// Task with relations type
export type TaskWithRelations = Task & {
creator?: User;
group?: Group;
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;
group?: Group;
};
// Alias pour compatibilité
export type AnnouncementWithRelations = DashboardMessageWithRelations;
// Reconciliation Comment with relations type
export type ReconciliationCommentWithRelations = ReconciliationComment & {
delivery: DeliveryWithRelations;
group: Group;
author: User;
};
// SAV (Service Après-Vente) Tables
export const savTickets = pgTable("sav_tickets", {
id: serial("id").primaryKey(),
ticketNumber: varchar("ticket_number", { length: 50 }).notNull(),
supplierId: integer("supplier_id").notNull(),
groupId: integer("group_id").notNull(),
productGencode: varchar("product_gencode", { length: 255 }).notNull(),
productReference: varchar("product_reference", { length: 255 }),
productDesignation: varchar("product_designation", { length: 500 }).notNull(),
problemType: varchar("problem_type", { length: 100 }).notNull(),
problemDescription: text("problem_description").notNull(),
resolutionDescription: text("resolution_description"),
status: varchar("status", { length: 50 }).notNull().default("nouveau"),
priority: varchar("priority", { length: 50 }).notNull().default("normale"), // normale, haute, critique
clientName: varchar("client_name", { length: 255 }),
clientPhone: varchar("client_phone", { length: 255 }),
createdBy: varchar("created_by", { length: 255 }).notNull(),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
resolvedAt: timestamp("resolved_at"),
closedAt: timestamp("closed_at"),
});
// SAV Relations
export const savTicketRelations = relations(savTickets, ({ one, many }) => ({
supplier: one(suppliers, {
fields: [savTickets.supplierId],
references: [suppliers.id],
}),
group: one(groups, {
fields: [savTickets.groupId],
references: [groups.id],
}),
creator: one(users, {
fields: [savTickets.createdBy],
references: [users.id],
}),
}));
// SAV Zod Schemas
export const insertSavTicketSchema = createInsertSchema(savTickets).omit({
id: true,
ticketNumber: true, // Auto-generated server-side
createdAt: true,
updatedAt: true,
resolvedAt: true,
closedAt: true,
}).extend({
priority: z.enum(["faible", "normale", "haute", "critique"]).default("normale"),
status: z.enum(["nouveau", "en_cours", "attente_pieces", "attente_echange", "resolu", "ferme"]).default("nouveau"),
problemType: z.enum(["defectueux", "pieces_manquantes", "non_conforme", "autre"]).default("defectueux"),
});
// export const insertSavTicketHistorySchema = createInsertSchema(savTicketHistory).omit({
// id: true,
// createdAt: true,
// });
// SAV Types
export type SavTicket = typeof savTickets.$inferSelect;
export type InsertSavTicket = z.infer<typeof insertSavTicketSchema>;
// export type SavTicketHistory = typeof savTicketHistory.$inferSelect;
// export type InsertSavTicketHistory = z.infer<typeof insertSavTicketHistorySchema>;
export type InsertSavTicketHistory = any;
export type SavTicketHistory = any;
export type SavTicketWithRelations = SavTicket & {
supplier: Supplier;
group: Group;
creator: User;
history: any[];
};
export type SavTicketHistoryWithCreator = any;
// Weather Tables
export const weatherData = pgTable("weather_data", {
id: serial("id").primaryKey(),
date: date("date").notNull(),
location: text("location").notNull(),
tempMax: text("temp_max").notNull(),
tempMin: text("temp_min").notNull(),
icon: text("icon").notNull(),
conditions: text("conditions").notNull(),
isCurrentYear: boolean("is_current_year").notNull().default(true),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
export const weatherSettings = pgTable("weather_settings", {
id: serial("id").primaryKey(),
apiKey: text("api_key").notNull(),
location: text("location").notNull().default("Paris, France"),
isActive: boolean("is_active").notNull().default(true),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Weather Schemas
export const insertWeatherDataSchema = createInsertSchema(weatherData).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export const insertWeatherSettingsSchema = createInsertSchema(weatherSettings).omit({
id: true,
createdAt: true,
updatedAt: true,
});
// Weather Types
export type WeatherData = typeof weatherData.$inferSelect;
export type InsertWeatherData = z.infer<typeof insertWeatherDataSchema>;
export type WeatherSettings = typeof weatherSettings.$inferSelect;
export type InsertWeatherSettings = z.infer<typeof insertWeatherSettingsSchema>;
// Configuration Webhook BAP
export const webhookBapConfig = pgTable("webhook_bap_config", {
id: serial("id").primaryKey(),
name: varchar("name", { length: 100 }).notNull().default("Configuration BAP"),
webhookUrl: text("webhook_url").notNull(),
description: text("description"),
isActive: boolean("is_active").notNull().default(true),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Webhook BAP Schemas
export const insertWebhookBapConfigSchema = createInsertSchema(webhookBapConfig).omit({
id: true,
createdAt: true,
updatedAt: true,
});
// Webhook BAP Types
export type WebhookBapConfig = typeof webhookBapConfig.$inferSelect;
export type InsertWebhookBapConfig = z.infer<typeof insertWebhookBapConfigSchema>;