import { sql } from "drizzle-orm"; import { pgTable, text, varchar, primaryKey, serial, integer, timestamp, numeric, jsonb, boolean, date } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { z } from "zod"; // Re-exportar schemas modulares criados pelo Dev Center export * from "./schemas/index"; export const users = pgTable("users", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), username: text("username").notNull().unique(), password: text("password").notNull(), name: text("name"), email: text("email"), phone: text("phone"), role: text("role").default("user"), profileId: integer("profile_id"), partnerId: integer("partner_id"), collaboratorType: text("collaborator_type"), hourlyRate: numeric("hourly_rate", { precision: 10, scale: 2 }).default("0"), skills: text("skills").array(), status: text("status").default("active"), lastLoginAt: timestamp("last_login_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`), }); export const profiles = pgTable("profiles", { id: serial("id").primaryKey(), name: text("name").notNull().unique(), description: text("description"), type: text("type").notNull().default("custom"), allowedModules: text("allowed_modules").array(), isSystem: integer("is_system").default(0), status: text("status").default("active"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`), }); export const insertProfileSchema = createInsertSchema(profiles).omit({ id: true, createdAt: true, updatedAt: true }); export type Profile = typeof profiles.$inferSelect; export type InsertProfile = z.infer; export const insertUserSchema = createInsertSchema(users).pick({ username: true, password: true, name: true, email: true, role: true, profileId: true, partnerId: true, status: true, }); export type InsertUser = z.infer; export type User = typeof users.$inferSelect; export const applications = pgTable("applications", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), name: text("name").notNull(), category: text("category").notNull(), icon: text("icon").notNull(), status: text("status").notNull(), url: text("url"), description: text("description"), }); export const insertApplicationSchema = createInsertSchema(applications).omit({ id: true, }); export type InsertApplication = z.infer; export type Application = typeof applications.$inferSelect; export const userApplications = pgTable("user_applications", { userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), applicationId: varchar("application_id").notNull().references(() => applications.id, { onDelete: "cascade" }), }, (table) => ({ pk: primaryKey({ columns: [table.userId, table.applicationId] }), })); export const insertUserApplicationSchema = createInsertSchema(userApplications); export type InsertUserApplication = z.infer; export type UserApplication = typeof userApplications.$inferSelect; // ========== RBAC - Roles & Permissions ========== export const roles = pgTable("roles", { id: serial("id").primaryKey(), name: text("name").notNull().unique(), description: text("description"), isSystem: integer("is_system").default(0), // 1 = system role (admin, user), can't be deleted createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const permissions = pgTable("permissions", { id: serial("id").primaryKey(), code: text("code").notNull().unique(), // e.g., "compass.clients.read", "agent.chat.write" name: text("name").notNull(), description: text("description"), module: text("module").notNull(), // agent, compass, insights, manus, whatsapp, admin action: text("action").notNull(), // read, write, delete, admin }); export const rolePermissions = pgTable("role_permissions", { roleId: integer("role_id").notNull().references(() => roles.id, { onDelete: "cascade" }), permissionId: integer("permission_id").notNull().references(() => permissions.id, { onDelete: "cascade" }), }, (table) => ({ pk: primaryKey({ columns: [table.roleId, table.permissionId] }), })); export const userRoles = pgTable("user_roles", { userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), roleId: integer("role_id").notNull().references(() => roles.id, { onDelete: "cascade" }), assignedAt: timestamp("assigned_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }, (table) => ({ pk: primaryKey({ columns: [table.userId, table.roleId] }), })); // Module access control - which modules each user can access export const moduleAccess = pgTable("module_access", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), module: text("module").notNull(), // home, agent, compass, insights, manus, whatsapp, admin canAccess: integer("can_access").default(1), // 1 = yes, 0 = no createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // External application permissions export const externalAppPermissions = pgTable("external_app_permissions", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), appName: text("app_name").notNull(), // e.g., "erp_arcadia", "erp_totvs", "whatsapp" appUrl: text("app_url"), canAccess: integer("can_access").default(1), apiKeyId: text("api_key_id"), // reference to stored API key createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertRoleSchema = createInsertSchema(roles).omit({ id: true, createdAt: true }); export const insertPermissionSchema = createInsertSchema(permissions).omit({ id: true }); export const insertRolePermissionSchema = createInsertSchema(rolePermissions); export const insertUserRoleSchema = createInsertSchema(userRoles).omit({ assignedAt: true }); export const insertModuleAccessSchema = createInsertSchema(moduleAccess).omit({ id: true, createdAt: true }); export const insertExternalAppPermissionSchema = createInsertSchema(externalAppPermissions).omit({ id: true, createdAt: true }); export type Role = typeof roles.$inferSelect; export type InsertRole = z.infer; export type Permission = typeof permissions.$inferSelect; export type InsertPermission = z.infer; export type RolePermission = typeof rolePermissions.$inferSelect; export type InsertRolePermission = z.infer; export type UserRole = typeof userRoles.$inferSelect; export type InsertUserRole = z.infer; export type ModuleAccess = typeof moduleAccess.$inferSelect; export type InsertModuleAccess = z.infer; export type ExternalAppPermission = typeof externalAppPermissions.$inferSelect; export type InsertExternalAppPermission = z.infer; // ========== PRODUCTIVITY HUB - Pages & Blocks (Notion-style) ========== export const workspacePages = pgTable("workspace_pages", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), parentId: integer("parent_id"), // for nested pages title: text("title").notNull().default("Sem título"), icon: text("icon"), // emoji or icon name coverImage: text("cover_image"), isPublic: integer("is_public").default(0), isFavorite: integer("is_favorite").default(0), isArchived: integer("is_archived").default(0), orderIndex: integer("order_index").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const pageBlocks = pgTable("page_blocks", { id: serial("id").primaryKey(), pageId: integer("page_id").notNull().references(() => workspacePages.id, { onDelete: "cascade" }), parentBlockId: integer("parent_block_id"), // for nested blocks type: text("type").notNull(), // text, heading1, heading2, heading3, bullet, numbered, todo, toggle, quote, callout, divider, code, image, table, embed, link_preview content: text("content"), // JSON content based on type properties: text("properties"), // JSON for additional properties (checked, collapsed, etc.) orderIndex: integer("order_index").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Bidirectional links between pages/blocks export const pageLinks = pgTable("page_links", { id: serial("id").primaryKey(), sourcePageId: integer("source_page_id").notNull().references(() => workspacePages.id, { onDelete: "cascade" }), targetPageId: integer("target_page_id").notNull().references(() => workspacePages.id, { onDelete: "cascade" }), blockId: integer("block_id").references(() => pageBlocks.id, { onDelete: "cascade" }), // optional: which block contains the link createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== WIDGETS & DASHBOARD ========== export const dashboardWidgets = pgTable("dashboard_widgets", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), type: text("type").notNull(), // tasks, calendar, recent_activity, quick_notes, weather, clock, pomodoro, favorites, inbox title: text("title"), config: text("config"), // JSON configuration position: text("position"), // JSON {x, y, w, h} for grid layout isVisible: integer("is_visible").default(1), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== QUICK NOTES (Scratch Pad) ========== export const quickNotes = pgTable("quick_notes", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), content: text("content").notNull(), isPinned: integer("is_pinned").default(0), color: text("color"), // for visual distinction createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== UNIFIED INBOX / ACTIVITY FEED ========== export const activityFeed = pgTable("activity_feed", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), actorId: varchar("actor_id").references(() => users.id), // who performed the action type: text("type").notNull(), // created, updated, deleted, mentioned, assigned, completed, commented module: text("module").notNull(), // compass, agent, insights, manus, whatsapp, workspace entityType: text("entity_type").notNull(), // client, project, task, page, conversation, etc. entityId: text("entity_id").notNull(), entityTitle: text("entity_title"), description: text("description"), metadata: text("metadata"), // JSON for additional context isRead: integer("is_read").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== FAVORITES / BOOKMARKS ========== export const userFavorites = pgTable("user_favorites", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), module: text("module").notNull(), entityType: text("entity_type").notNull(), entityId: text("entity_id").notNull(), entityTitle: text("entity_title"), entityIcon: text("entity_icon"), orderIndex: integer("order_index").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== COMMAND HISTORY (for command palette) ========== export const commandHistory = pgTable("command_history", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), command: text("command").notNull(), frequency: integer("frequency").default(1), lastUsedAt: timestamp("last_used_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertWorkspacePageSchema = createInsertSchema(workspacePages).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPageBlockSchema = createInsertSchema(pageBlocks).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPageLinkSchema = createInsertSchema(pageLinks).omit({ id: true, createdAt: true }); export const insertDashboardWidgetSchema = createInsertSchema(dashboardWidgets).omit({ id: true, createdAt: true }); export const insertQuickNoteSchema = createInsertSchema(quickNotes).omit({ id: true, createdAt: true, updatedAt: true }); export const insertActivityFeedSchema = createInsertSchema(activityFeed).omit({ id: true, createdAt: true }); export const insertUserFavoriteSchema = createInsertSchema(userFavorites).omit({ id: true, createdAt: true }); export const insertCommandHistorySchema = createInsertSchema(commandHistory).omit({ id: true, lastUsedAt: true }); export type WorkspacePage = typeof workspacePages.$inferSelect; export type InsertWorkspacePage = z.infer; export type PageBlock = typeof pageBlocks.$inferSelect; export type InsertPageBlock = z.infer; export type PageLink = typeof pageLinks.$inferSelect; export type InsertPageLink = z.infer; export type DashboardWidget = typeof dashboardWidgets.$inferSelect; export type InsertDashboardWidget = z.infer; export type QuickNote = typeof quickNotes.$inferSelect; export type InsertQuickNote = z.infer; export type ActivityFeedEntry = typeof activityFeed.$inferSelect; export type InsertActivityFeedEntry = z.infer; export type UserFavorite = typeof userFavorites.$inferSelect; export type InsertUserFavorite = z.infer; export type CommandHistoryEntry = typeof commandHistory.$inferSelect; export type InsertCommandHistoryEntry = z.infer; export const conversations = pgTable("conversations", { id: serial("id").primaryKey(), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }), title: text("title").notNull(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const messages = pgTable("messages", { id: serial("id").primaryKey(), conversationId: integer("conversation_id").notNull().references(() => conversations.id, { onDelete: "cascade" }), role: text("role").notNull(), content: text("content").notNull(), interactionId: integer("interaction_id"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertConversationSchema = createInsertSchema(conversations).omit({ id: true, createdAt: true, }); export const insertMessageSchema = createInsertSchema(messages).omit({ id: true, createdAt: true, }); export type Conversation = typeof conversations.$inferSelect; export type InsertConversation = z.infer; export type Message = typeof messages.$inferSelect; export type InsertMessage = z.infer; export const knowledgeBase = pgTable("knowledge_base", { id: serial("id").primaryKey(), title: text("title").notNull(), content: text("content").notNull(), author: text("author").notNull(), category: text("category").notNull(), source: text("source"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertKnowledgeBaseSchema = createInsertSchema(knowledgeBase).omit({ id: true, createdAt: true, }); export type KnowledgeBaseEntry = typeof knowledgeBase.$inferSelect; export type InsertKnowledgeBaseEntry = z.infer; export const chatAttachments = pgTable("chat_attachments", { id: serial("id").primaryKey(), messageId: integer("message_id").references(() => messages.id, { onDelete: "cascade" }), fileName: text("file_name").notNull(), fileType: text("file_type").notNull(), fileContent: text("file_content").notNull(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertChatAttachmentSchema = createInsertSchema(chatAttachments).omit({ id: true, createdAt: true, }); export type ChatAttachment = typeof chatAttachments.$inferSelect; export type InsertChatAttachment = z.infer; export const erpConnections = pgTable("erp_connections", { id: serial("id").primaryKey(), name: text("name").notNull(), type: text("type").notNull(), baseUrl: text("base_url").notNull(), apiKey: text("api_key"), apiSecret: text("api_secret"), username: text("username"), password: text("password"), isActive: text("is_active").default("true"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertErpConnectionSchema = createInsertSchema(erpConnections).omit({ id: true, createdAt: true, }); export type ErpConnection = typeof erpConnections.$inferSelect; export type InsertErpConnection = z.infer; export const agentTasks = pgTable("agent_tasks", { id: serial("id").primaryKey(), name: text("name").notNull(), type: text("type").notNull(), schedule: text("schedule"), erpConnectionId: integer("erp_connection_id").references(() => erpConnections.id, { onDelete: "cascade" }), config: text("config"), status: text("status").default("active"), lastRun: timestamp("last_run"), nextRun: timestamp("next_run"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertAgentTaskSchema = createInsertSchema(agentTasks).omit({ id: true, createdAt: true, lastRun: true, nextRun: true, }); export type AgentTask = typeof agentTasks.$inferSelect; export type InsertAgentTask = z.infer; export const taskExecutions = pgTable("task_executions", { id: serial("id").primaryKey(), taskId: integer("task_id").references(() => agentTasks.id, { onDelete: "cascade" }), status: text("status").notNull(), result: text("result"), error: text("error"), startedAt: timestamp("started_at").default(sql`CURRENT_TIMESTAMP`).notNull(), completedAt: timestamp("completed_at"), }); export const insertTaskExecutionSchema = createInsertSchema(taskExecutions).omit({ id: true, startedAt: true, }); export type TaskExecution = typeof taskExecutions.$inferSelect; export type InsertTaskExecution = z.infer; export const chatThreads = pgTable("chat_threads", { id: serial("id").primaryKey(), type: text("type").notNull().default("direct"), name: text("name"), createdBy: varchar("created_by").references(() => users.id, { onDelete: "set null" }), latestMessageAt: timestamp("latest_message_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const chatParticipants = pgTable("chat_participants", { id: serial("id").primaryKey(), threadId: integer("thread_id").notNull().references(() => chatThreads.id, { onDelete: "cascade" }), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), role: text("role").default("member"), joinedAt: timestamp("joined_at").default(sql`CURRENT_TIMESTAMP`).notNull(), lastReadAt: timestamp("last_read_at"), }); export const chatMessages = pgTable("chat_messages", { id: serial("id").primaryKey(), threadId: integer("thread_id").notNull().references(() => chatThreads.id, { onDelete: "cascade" }), senderId: varchar("sender_id").references(() => users.id, { onDelete: "set null" }), body: text("body").notNull(), messageType: text("message_type").default("text"), status: text("status").default("sent"), sentAt: timestamp("sent_at").default(sql`CURRENT_TIMESTAMP`).notNull(), editedAt: timestamp("edited_at"), }); export const whatsappSessions = pgTable("whatsapp_sessions", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), sessionData: text("session_data"), status: text("status").default("disconnected"), phoneNumber: text("phone_number"), lastSync: timestamp("last_sync"), autoReplyConfig: jsonb("auto_reply_config").$type>(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const whatsappContacts = pgTable("whatsapp_contacts", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), whatsappId: text("whatsapp_id").notNull(), name: text("name"), pushName: text("push_name"), phoneNumber: text("phone_number"), profilePicUrl: text("profile_pic_url"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const whatsappMessages = pgTable("whatsapp_messages", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), whatsappContactId: integer("whatsapp_contact_id").references(() => whatsappContacts.id, { onDelete: "cascade" }), remoteJid: text("remote_jid").notNull(), messageId: text("message_id").notNull(), fromMe: text("from_me").default("false"), body: text("body"), messageType: text("message_type").default("text"), timestamp: timestamp("timestamp").notNull(), status: text("status").default("received"), quotedMessageId: text("quoted_message_id"), quotedBody: text("quoted_body"), isDeleted: integer("is_deleted").default(0), isEdited: integer("is_edited").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertChatThreadSchema = createInsertSchema(chatThreads).omit({ id: true, createdAt: true }); export const insertChatParticipantSchema = createInsertSchema(chatParticipants).omit({ id: true, joinedAt: true }); export const insertChatMessageSchema = createInsertSchema(chatMessages).omit({ id: true, sentAt: true }); // WhatsApp Queues (Filas de Atendimento) export const whatsappQueues = pgTable("whatsapp_queues", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), name: text("name").notNull(), color: text("color").default("#10B981"), greeting: text("greeting"), isActive: integer("is_active").default(1), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // WhatsApp Tickets (Atendimentos) export const whatsappTickets = pgTable("whatsapp_tickets", { id: serial("id").primaryKey(), ownerId: varchar("owner_id").notNull().references(() => users.id, { onDelete: "cascade" }), contactId: integer("contact_id").notNull().references(() => whatsappContacts.id, { onDelete: "cascade" }), queueId: integer("queue_id").references(() => whatsappQueues.id, { onDelete: "set null" }), assignedToId: varchar("assigned_to_id").references(() => users.id, { onDelete: "set null" }), status: text("status").default("open"), lastMessage: text("last_message"), unreadCount: integer("unread_count").default(0), protocol: text("protocol"), closedAt: timestamp("closed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertWhatsappSessionSchema = createInsertSchema(whatsappSessions).omit({ id: true, createdAt: true }); export const insertWhatsappContactSchema = createInsertSchema(whatsappContacts).omit({ id: true, createdAt: true }); export const insertWhatsappMessageSchema = createInsertSchema(whatsappMessages).omit({ id: true, createdAt: true }); export const insertWhatsappQueueSchema = createInsertSchema(whatsappQueues).omit({ id: true, createdAt: true }); export const insertWhatsappTicketSchema = createInsertSchema(whatsappTickets).omit({ id: true, createdAt: true, updatedAt: true }); export type ChatThread = typeof chatThreads.$inferSelect; export type InsertChatThread = z.infer; export type ChatParticipant = typeof chatParticipants.$inferSelect; export type InsertChatParticipant = z.infer; export type ChatMessage = typeof chatMessages.$inferSelect; export type InsertChatMessage = z.infer; export type WhatsappSession = typeof whatsappSessions.$inferSelect; export type InsertWhatsappSession = z.infer; export type WhatsappContact = typeof whatsappContacts.$inferSelect; export type InsertWhatsappContact = z.infer; export type WhatsappMessage = typeof whatsappMessages.$inferSelect; export type InsertWhatsappMessage = z.infer; export type WhatsappQueue = typeof whatsappQueues.$inferSelect; export type InsertWhatsappQueue = z.infer; export type WhatsappTicket = typeof whatsappTickets.$inferSelect; export type InsertWhatsappTicket = z.infer; export const manusRuns = pgTable("manus_runs", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), prompt: text("prompt").notNull(), status: text("status").default("running"), result: text("result"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), completedAt: timestamp("completed_at"), }); export const manusSteps = pgTable("manus_steps", { id: serial("id").primaryKey(), runId: integer("run_id").notNull().references(() => manusRuns.id, { onDelete: "cascade" }), stepNumber: integer("step_number").notNull(), thought: text("thought"), tool: text("tool"), toolInput: text("tool_input"), toolOutput: text("tool_output"), status: text("status").default("pending"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertManusRunSchema = createInsertSchema(manusRuns).omit({ id: true, createdAt: true }); export const insertManusStepSchema = createInsertSchema(manusSteps).omit({ id: true, createdAt: true }); export type ManusRun = typeof manusRuns.$inferSelect; export type InsertManusRun = z.infer; export type ManusStep = typeof manusSteps.$inferSelect; export type InsertManusStep = z.infer; // ========== Custom MCP Servers ========== export const customMcpServers = pgTable("custom_mcp_servers", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), name: text("name").notNull(), transportType: text("transport_type").notNull().default("http"), // http, stdio serverUrl: text("server_url"), // For HTTP transport command: text("command"), // For STDIO transport args: text("args").array(), // For STDIO transport iconUrl: text("icon_url"), description: text("description"), customHeaders: jsonb("custom_headers"), // {key: value} for HTTP headers isActive: integer("is_active").default(1), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertCustomMcpServerSchema = createInsertSchema(customMcpServers).omit({ id: true, createdAt: true, updatedAt: true }); export type CustomMcpServer = typeof customMcpServers.$inferSelect; export type InsertCustomMcpServer = z.infer; export const automations = pgTable("automations", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), triggerType: text("trigger_type").notNull(), triggerConfig: text("trigger_config"), isActive: text("is_active").default("true"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const automationActions = pgTable("automation_actions", { id: serial("id").primaryKey(), automationId: integer("automation_id").notNull().references(() => automations.id, { onDelete: "cascade" }), orderIndex: integer("order_index").notNull().default(0), actionType: text("action_type").notNull(), actionConfig: text("action_config"), conditionConfig: text("condition_config"), }); export const automationLogs = pgTable("automation_logs", { id: serial("id").primaryKey(), automationId: integer("automation_id").notNull().references(() => automations.id, { onDelete: "cascade" }), status: text("status").notNull(), triggerData: text("trigger_data"), result: text("result"), error: text("error"), startedAt: timestamp("started_at").default(sql`CURRENT_TIMESTAMP`).notNull(), completedAt: timestamp("completed_at"), }); export const scheduledTasks = pgTable("scheduled_tasks", { id: serial("id").primaryKey(), automationId: integer("automation_id").references(() => automations.id, { onDelete: "cascade" }), cronExpression: text("cron_expression"), intervalMinutes: integer("interval_minutes"), nextRunAt: timestamp("next_run_at"), lastRunAt: timestamp("last_run_at"), isActive: text("is_active").default("true"), }); export const insertAutomationSchema = createInsertSchema(automations).omit({ id: true, createdAt: true, updatedAt: true }); export const insertAutomationActionSchema = createInsertSchema(automationActions).omit({ id: true }); export const insertAutomationLogSchema = createInsertSchema(automationLogs).omit({ id: true, startedAt: true }); export const insertScheduledTaskSchema = createInsertSchema(scheduledTasks).omit({ id: true }); export type Automation = typeof automations.$inferSelect; export type InsertAutomation = z.infer; export type AutomationAction = typeof automationActions.$inferSelect; export type InsertAutomationAction = z.infer; export type AutomationLog = typeof automationLogs.$inferSelect; export type InsertAutomationLog = z.infer; export type ScheduledTask = typeof scheduledTasks.$inferSelect; export type InsertScheduledTask = z.infer; export const dataSources = pgTable("data_sources", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), name: text("name").notNull(), type: text("type").notNull(), host: text("host"), port: integer("port"), database: text("database"), username: text("username"), password: text("password"), connectionString: text("connection_string"), isActive: text("is_active").default("true"), lastTestedAt: timestamp("last_tested_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const biDatasets = pgTable("bi_datasets", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), dataSourceId: integer("data_source_id").references(() => dataSources.id, { onDelete: "set null" }), name: text("name").notNull(), description: text("description"), queryType: text("query_type").default("table"), tableName: text("table_name"), sqlQuery: text("sql_query"), columns: text("columns"), filters: text("filters"), isPublic: text("is_public").default("false"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const biCharts = pgTable("bi_charts", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), datasetId: integer("dataset_id").references(() => biDatasets.id, { onDelete: "cascade" }), name: text("name").notNull(), chartType: text("chart_type").notNull(), config: text("config"), xAxis: text("x_axis"), yAxis: text("y_axis"), groupBy: text("group_by"), aggregation: text("aggregation"), colors: text("colors"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const biDashboards = pgTable("bi_dashboards", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), layout: text("layout"), isPublic: text("is_public").default("false"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const biDashboardCharts = pgTable("bi_dashboard_charts", { id: serial("id").primaryKey(), dashboardId: integer("dashboard_id").notNull().references(() => biDashboards.id, { onDelete: "cascade" }), chartId: integer("chart_id").notNull().references(() => biCharts.id, { onDelete: "cascade" }), positionX: integer("position_x").default(0), positionY: integer("position_y").default(0), width: integer("width").default(6), height: integer("height").default(4), }); export const backupJobs = pgTable("backup_jobs", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), dataSourceId: integer("data_source_id").references(() => dataSources.id, { onDelete: "cascade" }), name: text("name").notNull(), backupType: text("backup_type").notNull(), includeSchema: text("include_schema").default("true"), includeTables: text("include_tables"), excludeTables: text("exclude_tables"), compressionType: text("compression_type").default("gzip"), retentionDays: integer("retention_days").default(30), storageLocation: text("storage_location"), isActive: text("is_active").default("true"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const backupArtifacts = pgTable("backup_artifacts", { id: serial("id").primaryKey(), backupJobId: integer("backup_job_id").references(() => backupJobs.id, { onDelete: "cascade" }), automationLogId: integer("automation_log_id").references(() => automationLogs.id, { onDelete: "set null" }), filename: text("filename").notNull(), filePath: text("file_path").notNull(), fileSize: integer("file_size"), checksum: text("checksum"), status: text("status").default("pending"), startedAt: timestamp("started_at").default(sql`CURRENT_TIMESTAMP`).notNull(), completedAt: timestamp("completed_at"), expiresAt: timestamp("expires_at"), }); // Data Staging for ERP Migration export const stagedTables = pgTable("staged_tables", { id: serial("id").primaryKey(), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }), name: text("name").notNull(), sourceType: text("source_type").notNull(), // sql, mongodb, csv, json, zip sourceFile: text("source_file"), tableName: text("table_name").notNull(), // actual DB table name (staged_*) columns: text("columns"), // JSON array of column definitions rowCount: integer("row_count").default(0), status: text("status").default("ready"), // ready, mapped, migrating, migrated, error targetErp: text("target_erp"), // plus, next, totvs, sap description: text("description"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const stagingMappings = pgTable("staging_mappings", { id: serial("id").primaryKey(), stagedTableId: integer("staged_table_id").references(() => stagedTables.id, { onDelete: "cascade" }), name: text("name").notNull(), targetErp: text("target_erp").notNull(), // plus, next, totvs, sap targetEntity: text("target_entity").notNull(), // customers, products, orders, etc. fieldMappings: text("field_mappings").notNull(), // JSON: {sourceField: targetField, transform?} filters: text("filters"), // JSON: conditions to filter data transformations: text("transformations"), // JSON: data transformations isActive: text("is_active").default("true"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const etlMigrationJobs = pgTable("etl_migration_jobs", { id: serial("id").primaryKey(), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }), stagedTableId: integer("staged_table_id").references(() => stagedTables.id, { onDelete: "cascade" }), mappingId: integer("mapping_id").references(() => stagingMappings.id, { onDelete: "cascade" }), erpConnectionId: integer("erp_connection_id").references(() => erpConnections.id, { onDelete: "set null" }), status: text("status").default("pending"), // pending, running, completed, failed, cancelled totalRecords: integer("total_records").default(0), processedRecords: integer("processed_records").default(0), successRecords: integer("success_records").default(0), errorRecords: integer("error_records").default(0), errorLog: text("error_log"), // JSON array of errors startedAt: timestamp("started_at"), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertDataSourceSchema = createInsertSchema(dataSources).omit({ id: true, createdAt: true }); export const insertBiDatasetSchema = createInsertSchema(biDatasets).omit({ id: true, createdAt: true, updatedAt: true }); export const insertBiChartSchema = createInsertSchema(biCharts).omit({ id: true, createdAt: true, updatedAt: true }); export const insertBiDashboardSchema = createInsertSchema(biDashboards).omit({ id: true, createdAt: true, updatedAt: true }); export const insertBiDashboardChartSchema = createInsertSchema(biDashboardCharts).omit({ id: true }); export const insertBackupJobSchema = createInsertSchema(backupJobs).omit({ id: true, createdAt: true }); export const insertBackupArtifactSchema = createInsertSchema(backupArtifacts).omit({ id: true, startedAt: true }); export const insertStagedTableSchema = createInsertSchema(stagedTables).omit({ id: true, createdAt: true, updatedAt: true }); export const insertStagingMappingSchema = createInsertSchema(stagingMappings).omit({ id: true, createdAt: true }); export const insertEtlMigrationJobSchema = createInsertSchema(etlMigrationJobs).omit({ id: true, createdAt: true }); export type DataSource = typeof dataSources.$inferSelect; export type InsertDataSource = z.infer; export type BiDataset = typeof biDatasets.$inferSelect; export type InsertBiDataset = z.infer; export type BiChart = typeof biCharts.$inferSelect; export type InsertBiChart = z.infer; export type BiDashboard = typeof biDashboards.$inferSelect; export type InsertBiDashboard = z.infer; export type BiDashboardChart = typeof biDashboardCharts.$inferSelect; export type InsertBiDashboardChart = z.infer; export type BackupJob = typeof backupJobs.$inferSelect; export type InsertBackupJob = z.infer; export type BackupArtifact = typeof backupArtifacts.$inferSelect; export type InsertBackupArtifact = z.infer; export type StagedTable = typeof stagedTables.$inferSelect; export type InsertStagedTable = z.infer; export type StagingMapping = typeof stagingMappings.$inferSelect; export type InsertStagingMapping = z.infer; export type EtlMigrationJob = typeof etlMigrationJobs.$inferSelect; export type InsertEtlMigrationJob = z.infer; // ========================================== // MULTI-TENANCY (SaaS) - Hierarquia Master/Partner/Client // ========================================== // Tipo de features disponíveis por tenant/plano export type TenantFeatures = { ide: boolean; ideMode: 'none' | 'no-code' | 'low-code' | 'pro-code'; whatsapp: boolean; whatsappSessions: number; crm: boolean; erp: boolean; bi: boolean; manus: boolean; manusTools: string[]; centralApis: boolean; centralApisManage: boolean; comunidades: boolean; maxChannels: number; biblioteca: boolean; bibliotecaPublish: boolean; suporteN3: boolean; retail: boolean; plus: boolean; fisco: boolean; cockpit: boolean; compass: boolean; production: boolean; support: boolean; xosCrm: boolean; }; // Tenants (Master/Parceiros/Clientes) export const tenants = pgTable("tenants", { id: serial("id").primaryKey(), name: text("name").notNull(), slug: text("slug").unique(), email: text("email"), phone: text("phone"), logoUrl: text("logo_url"), plan: text("plan").default("free"), // free, starter, pro, enterprise, partner_starter, partner_pro status: text("status").default("active"), // active, suspended, cancelled, trial settings: text("settings"), // JSON for tenant-specific settings createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), // Hierarquia de tenants tenantType: text("tenant_type").default("client"), // master, partner, client parentTenantId: integer("parent_tenant_id"), // Self-reference para hierarquia (parceiro ou master) // Informações de parceiro partnerCode: text("partner_code"), // Código único do parceiro commissionRate: numeric("commission_rate", { precision: 5, scale: 2 }), // % de comissão (para parceiros) // Limites do plano maxUsers: integer("max_users").default(5), maxStorageMb: integer("max_storage_mb").default(1000), features: jsonb("features").$type(), // Billing billingEmail: text("billing_email"), trialEndsAt: timestamp("trial_ends_at"), // Contato comercial commercialContact: text("commercial_contact"), commercialPhone: text("commercial_phone"), // Dados empresariais (unificado com CRM) cnpj: text("cnpj"), tradeName: text("trade_name"), address: text("address"), city: text("city"), state: text("state"), segment: text("segment"), notes: text("notes"), source: text("source"), }); // Empresas do Tenant (Matriz + Filiais) export const tenantEmpresas = pgTable("tenant_empresas", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), razaoSocial: text("razao_social").notNull(), nomeFantasia: text("nome_fantasia"), cnpj: text("cnpj").notNull(), ie: text("ie"), im: text("im"), email: text("email"), phone: text("phone"), tipo: text("tipo").default("filial"), // matriz, filial status: text("status").default("active"), // active, inactive cep: text("cep"), logradouro: text("logradouro"), numero: text("numero"), complemento: text("complemento"), bairro: text("bairro"), cidade: text("cidade"), uf: text("uf"), codigoIbge: text("codigo_ibge"), // Fiscal regimeTributario: text("regime_tributario"), // simples, presumido, real certificadoDigitalId: integer("certificado_digital_id"), ambienteFiscal: text("ambiente_fiscal").default("homologacao"), // producao, homologacao serieNfe: integer("serie_nfe").default(1), serieNfce: integer("serie_nfce").default(1), // Plus ERP link plusEmpresaId: integer("plus_empresa_id"), // Metadata createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Tenant Users (Membership) export const tenantUsers = pgTable("tenant_users", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), role: text("role").default("member"), // owner, admin, member isOwner: text("is_owner").default("false"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Planos disponíveis export const tenantPlans = pgTable("tenant_plans", { id: serial("id").primaryKey(), code: text("code").notNull().unique(), // free, starter, pro, enterprise, partner_starter, partner_pro name: text("name").notNull(), description: text("description"), tenantType: text("tenant_type").notNull(), // master, partner, client maxUsers: integer("max_users").default(5), maxStorageMb: integer("max_storage_mb").default(1000), features: jsonb("features").$type(), monthlyPrice: integer("monthly_price").default(0), // em centavos yearlyPrice: integer("yearly_price").default(0), // em centavos trialDays: integer("trial_days").default(14), isActive: text("is_active").default("true"), sortOrder: integer("sort_order").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Relacionamento Parceiro-Cliente export const partnerClients = pgTable("partner_clients", { id: serial("id").primaryKey(), partnerId: integer("partner_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), clientId: integer("client_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), commissionRate: numeric("commission_rate", { precision: 5, scale: 2 }), // Override do rate padrão do parceiro status: text("status").default("active"), // active, suspended, ended notes: text("notes"), startedAt: timestamp("started_at").default(sql`CURRENT_TIMESTAMP`).notNull(), endedAt: timestamp("ended_at"), }); // Comissões dos Parceiros export const partnerCommissions = pgTable("partner_commissions", { id: serial("id").primaryKey(), partnerId: integer("partner_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), clientId: integer("client_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), referenceMonth: text("reference_month").notNull(), // "2026-01" clientPlanCode: text("client_plan_code"), clientPlanValue: integer("client_plan_value").notNull(), // Valor do plano em centavos commissionRate: numeric("commission_rate", { precision: 5, scale: 2 }).notNull(), commissionValue: integer("commission_value").notNull(), // Valor da comissão em centavos status: text("status").default("pending"), // pending, approved, paid, cancelled approvedAt: timestamp("approved_at"), paidAt: timestamp("paid_at"), paymentReference: text("payment_reference"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertTenantSchema = createInsertSchema(tenants).omit({ id: true, createdAt: true, updatedAt: true }); export const insertTenantUserSchema = createInsertSchema(tenantUsers).omit({ id: true, createdAt: true }); export const insertTenantPlanSchema = createInsertSchema(tenantPlans).omit({ id: true, createdAt: true }); export const insertPartnerClientSchema = createInsertSchema(partnerClients).omit({ id: true, startedAt: true }); export const insertPartnerCommissionSchema = createInsertSchema(partnerCommissions).omit({ id: true, createdAt: true }); export const insertTenantEmpresaSchema = createInsertSchema(tenantEmpresas).omit({ id: true, createdAt: true, updatedAt: true }); export type Tenant = typeof tenants.$inferSelect; export type InsertTenant = z.infer; export type TenantUser = typeof tenantUsers.$inferSelect; export type InsertTenantUser = z.infer; export type TenantPlan = typeof tenantPlans.$inferSelect; export type InsertTenantPlan = z.infer; export type PartnerClient = typeof partnerClients.$inferSelect; export type InsertPartnerClient = z.infer; export type PartnerCommission = typeof partnerCommissions.$inferSelect; export type InsertPartnerCommission = z.infer; export type TenantEmpresa = typeof tenantEmpresas.$inferSelect; export type InsertTenantEmpresa = z.infer; // ========================================== // PROCESS COMPASS - CONSULTING BACK-OFFICE // ========================================== // Consulting Clients export const pcClients = pgTable("pc_clients", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), name: text("name").notNull(), email: text("email"), phone: text("phone"), company: text("company"), industry: text("industry"), website: text("website"), address: text("address"), notes: text("notes"), logoUrl: text("logo_url"), status: text("status").default("active"), // active, inactive, prospect createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Client Contacts export const pcClientContacts = pgTable("pc_client_contacts", { id: serial("id").primaryKey(), clientId: integer("client_id").notNull().references(() => pcClients.id, { onDelete: "cascade" }), name: text("name").notNull(), role: text("role"), email: text("email"), phone: text("phone"), isPrimary: text("is_primary").default("false"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Consulting Projects export const pcProjects = pgTable("pc_projects", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), clientId: integer("client_id").references(() => crmClients.id, { onDelete: "set null" }), name: text("name").notNull(), description: text("description"), projectType: text("project_type").default("consultoria").notNull(), prodType: text("prod_type").default("internal"), clientName: text("client_name"), compassProjectId: integer("compass_project_id"), history: text("history"), status: text("status").default("backlog").notNull(), managerId: varchar("manager_id").references(() => users.id), startDate: timestamp("start_date"), dueDate: timestamp("due_date"), priority: integer("priority").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Project Team Members (Squad por Projeto) export const pcProjectMembers = pgTable("pc_project_members", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => pcProjects.id, { onDelete: "cascade" }), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }), collaboratorId: integer("collaborator_id"), role: text("role").default("member"), // product_owner, tech_lead, member isExternal: integer("is_external").default(0), // 1 = colaborador externo assignedAt: timestamp("assigned_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Project Activity History export const pcProjectActivities = pgTable("pc_project_activities", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => pcProjects.id, { onDelete: "cascade" }), userId: varchar("user_id").references(() => users.id, { onDelete: "set null" }), activityType: text("activity_type").notNull(), // note, milestone, status_change, meeting, discovery, decision title: text("title").notNull(), description: text("description"), metadata: text("metadata"), // JSON for additional data createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Canvas Blocks (9 BMC blocks with 4 evolutionary levels) export const pcCanvasBlocks = pgTable("pc_canvas_blocks", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => pcProjects.id, { onDelete: "cascade" }), blockType: text("block_type").notNull(), // key_partners, key_activities, key_resources, value_propositions, customer_relationships, channels, customer_segments, cost_structure, revenue_streams level: text("level").default("intencao").notNull(), // intencao, evidencias, sistemico, transformacao title: text("title"), content: text("content"), notes: text("notes"), synthesis: text("synthesis"), // Síntese do bloco score: integer("score").default(0), // 0-100 completion score status: text("status").default("pending"), // pending, in_progress, completed pdcaStatus: text("pdca_status").default("plan"), // plan, do, check, act, done pdcaActionPlan: text("pdca_action_plan"), pdcaResult: text("pdca_result"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Canvas Diagnostic Questions (perguntas de diagnóstico) export const pcCanvasQuestions = pgTable("pc_canvas_questions", { id: serial("id").primaryKey(), blockId: integer("block_id").notNull().references(() => pcCanvasBlocks.id, { onDelete: "cascade" }), question: text("question").notNull(), answer: text("answer"), score: integer("score").default(0), // 0-10 score orderIndex: integer("order_index").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Canvas Expected Outputs (Saídas Esperadas) export const pcCanvasExpectedOutputs = pgTable("pc_canvas_expected_outputs", { id: serial("id").primaryKey(), blockId: integer("block_id").notNull().references(() => pcCanvasBlocks.id, { onDelete: "cascade" }), label: text("label").notNull(), description: text("description"), orderIndex: integer("order_index").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Canvas PDCA Links (items PDCA vinculados ao canvas) export const pcCanvasPdcaLinks = pgTable("pc_canvas_pdca_links", { id: serial("id").primaryKey(), blockId: integer("block_id").notNull().references(() => pcCanvasBlocks.id, { onDelete: "cascade" }), title: text("title").notNull(), pdcaStatus: text("pdca_status").default("plan"), // plan, do, check, act, done createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Canvas SWOT Links (items SWOT vinculados ao canvas) export const pcCanvasSwotLinks = pgTable("pc_canvas_swot_links", { id: serial("id").primaryKey(), blockId: integer("block_id").notNull().references(() => pcCanvasBlocks.id, { onDelete: "cascade" }), swotItemId: integer("swot_item_id").references(() => pcSwotItems.id, { onDelete: "cascade" }), title: text("title"), type: text("type"), // strength, weakness, opportunity, threat createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Business Processes export const pcProcesses = pgTable("pc_processes", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => pcProjects.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), category: text("category"), // operacional, comercial, administrativo, financeiro owner: text("owner"), status: text("status").default("draft"), // draft, mapped, optimizing, implemented priority: integer("priority").default(0), orderIndex: integer("order_index").default(0), diagramNodes: jsonb("diagram_nodes").$type().default([]), diagramEdges: jsonb("diagram_edges").$type().default([]), diagramViewport: jsonb("diagram_viewport").$type<{ x: number; y: number; zoom: number }>(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Project Team Members (for production projects) export const pcProjectTeamMembers = pgTable("pc_project_team_members", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => pcProjects.id, { onDelete: "cascade" }), name: text("name").notNull(), email: text("email"), role: text("role").notNull(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Project Tasks (production project tasks) export const pcProjectTasks = pgTable("pc_project_tasks", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => pcProjects.id, { onDelete: "cascade" }), title: text("title").notNull(), description: text("description"), status: text("status").default("pending").notNull(), // pending, in_progress, completed priority: text("priority").default("medium").notNull(), // low, medium, high assignedTo: text("assigned_to"), dueDate: timestamp("due_date"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Project Files export const pcProjectFiles = pgTable("pc_project_files", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => pcProjects.id, { onDelete: "cascade" }), name: text("name").notNull(), originalName: text("original_name").notNull(), mimeType: text("mime_type").notNull(), size: integer("size").notNull(), url: text("url").notNull(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Project History (rich text content) export const pcProjectHistory = pgTable("pc_project_history", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => pcProjects.id, { onDelete: "cascade" }).unique(), content: text("content").notNull().default(""), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Process Steps export const pcProcessSteps = pgTable("pc_process_steps", { id: serial("id").primaryKey(), processId: integer("process_id").notNull().references(() => pcProcesses.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), responsible: text("responsible"), inputs: text("inputs"), outputs: text("outputs"), systems: text("systems"), // JSON array of systems used duration: text("duration"), painPoints: text("pain_points"), improvements: text("improvements"), orderIndex: integer("order_index").default(0), status: text("status").default("active"), // active, bottleneck, optimized pdcaStatus: text("pdca_status").default("plan"), // plan, do, check, act, done pdcaActionPlan: text("pdca_action_plan"), pdcaResult: text("pdca_result"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // SWOT Analyses export const pcSwotAnalyses = pgTable("pc_swot_analyses", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => pcProjects.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), sector: text("sector"), // general, comercial, operacional, financeiro, rh analysisDate: timestamp("analysis_date").default(sql`CURRENT_TIMESTAMP`).notNull(), status: text("status").default("draft"), // draft, completed, archived createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // SWOT Items export const pcSwotItems = pgTable("pc_swot_items", { id: serial("id").primaryKey(), swotAnalysisId: integer("swot_analysis_id").notNull().references(() => pcSwotAnalyses.id, { onDelete: "cascade" }), type: text("type").notNull(), // strength, weakness, opportunity, threat title: text("title"), // título do item description: text("description").notNull(), impact: text("impact").default("medium"), // low, medium, high impactScore: integer("impact_score").default(3), // 1-5 numeric scale priorityLevel: text("priority_level").default("medium"), // baixa, média, alta, crítica priority: integer("priority").default(0), // ordering priority actionPlan: text("action_plan"), // plano de ação PDCA result: text("result"), // resultado/verificação do PDCA pdcaStatus: text("pdca_status").default("plan"), // plan, do, check, act, done responsible: text("responsible"), dueDate: timestamp("due_date"), status: text("status").default("identified"), // identified, analyzing, action_planned, resolved createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // CRM Pipeline Stages export const pcCrmStages = pgTable("pc_crm_stages", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), color: text("color").default("#3b82f6"), orderIndex: integer("order_index").default(0), isDefault: text("is_default").default("false"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // CRM Leads export const pcCrmLeads = pgTable("pc_crm_leads", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), name: text("name").notNull(), email: text("email"), phone: text("phone"), company: text("company"), source: text("source"), // website, referral, cold_call, event, social status: text("status").default("new"), // new, contacted, qualified, converted, lost notes: text("notes"), assignedToId: varchar("assigned_to_id").references(() => users.id), convertedToClientId: integer("converted_to_client_id").references(() => pcClients.id), convertedAt: timestamp("converted_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // CRM Opportunities export const pcCrmOpportunities = pgTable("pc_crm_opportunities", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), clientId: integer("client_id").references(() => pcClients.id), leadId: integer("lead_id").references(() => pcCrmLeads.id), stageId: integer("stage_id").references(() => pcCrmStages.id), name: text("name").notNull(), description: text("description"), value: integer("value").default(0), // Estimated value in cents probability: integer("probability").default(50), // 0-100 expectedCloseDate: timestamp("expected_close_date"), actualCloseDate: timestamp("actual_close_date"), status: text("status").default("open"), // open, won, lost lostReason: text("lost_reason"), assignedToId: varchar("assigned_to_id").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // CRM Activities export const pcCrmActivities = pgTable("pc_crm_activities", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), opportunityId: integer("opportunity_id").references(() => pcCrmOpportunities.id, { onDelete: "cascade" }), leadId: integer("lead_id").references(() => pcCrmLeads.id, { onDelete: "cascade" }), clientId: integer("client_id").references(() => pcClients.id), type: text("type").notNull(), // call, email, meeting, note, task title: text("title").notNull(), description: text("description"), dueDate: timestamp("due_date"), completedAt: timestamp("completed_at"), isCompleted: text("is_completed").default("false"), assignedToId: varchar("assigned_to_id").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Deliverables export const pcDeliverables = pgTable("pc_deliverables", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => pcProjects.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), type: text("type"), // document, presentation, report, model, template dueDate: timestamp("due_date"), status: text("status").default("pending"), // pending, in_progress, review, completed fileUrl: text("file_url"), assignedToId: varchar("assigned_to_id").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), completedAt: timestamp("completed_at"), }); // Tasks export const pcTasks = pgTable("pc_tasks", { id: serial("id").primaryKey(), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }), deliverableId: integer("deliverable_id").references(() => pcDeliverables.id, { onDelete: "set null" }), title: text("title").notNull(), description: text("description"), status: text("status").default("todo"), // todo, in_progress, review, done priority: text("priority").default("medium"), // low, medium, high, urgent dueDate: timestamp("due_date"), assignedToId: varchar("assigned_to_id").references(() => users.id), createdById: varchar("created_by_id").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), completedAt: timestamp("completed_at"), }); // PDCA Cycles export const pcPdcaCycles = pgTable("pc_pdca_cycles", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }), title: text("title").notNull(), description: text("description"), status: text("status").default("plan"), // plan, do, check, act, completed priority: text("priority").default("medium"), // low, medium, high dueDate: timestamp("due_date"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), completedAt: timestamp("completed_at"), }); // PDCA Actions (items within each phase) export const pcPdcaActions = pgTable("pc_pdca_actions", { id: serial("id").primaryKey(), cycleId: integer("cycle_id").notNull().references(() => pcPdcaCycles.id, { onDelete: "cascade" }), phase: text("phase").notNull(), // plan, do, check, act title: text("title").notNull(), description: text("description"), responsible: text("responsible"), status: text("status").default("pending"), // pending, in_progress, completed dueDate: timestamp("due_date"), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Requirements export const pcRequirements = pgTable("pc_requirements", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }), code: text("code"), // REQ-001, etc title: text("title").notNull(), description: text("description"), type: text("type").default("functional"), // functional, non_functional, business, technical priority: text("priority").default("medium"), // low, medium, high, critical status: text("status").default("draft"), // draft, approved, implemented, verified, rejected source: text("source"), // stakeholder, regulation, contract category: text("category"), // usability, performance, security, integration acceptanceCriteria: text("acceptance_criteria"), pdcaStatus: text("pdca_status").default("plan"), // plan, do, check, act, done pdcaActionPlan: text("pdca_action_plan"), pdcaResult: text("pdca_result"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========================================== // CRM EXPANDIDO - ARCÁDIA CRM // ========================================== // Partners (Parceiros de Canal) export const crmPartners = pgTable("crm_partners", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: text("name").notNull(), tradeName: text("trade_name"), cnpj: text("cnpj"), email: text("email"), phone: text("phone"), website: text("website"), type: text("type").notNull(), // referral, solution, technology, service tier: text("tier").default("partner"), // partner, certified, premier status: text("status").default("pending"), // pending, active, suspended, inactive contractStartDate: timestamp("contract_start_date"), contractEndDate: timestamp("contract_end_date"), primaryContactName: text("primary_contact_name"), primaryContactEmail: text("primary_contact_email"), primaryContactPhone: text("primary_contact_phone"), address: text("address"), city: text("city"), state: text("state"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Partner Certifications (Certificações de Parceiros) export const crmPartnerCertifications = pgTable("crm_partner_certifications", { id: serial("id").primaryKey(), partnerId: integer("partner_id").notNull().references(() => crmPartners.id, { onDelete: "cascade" }), userId: varchar("user_id").references(() => users.id), // Profissional certificado certificationName: text("certification_name").notNull(), // sales_professional, technical_consultant certificationDate: timestamp("certification_date").notNull(), expirationDate: timestamp("expiration_date"), score: integer("score"), status: text("status").default("active"), // active, expired, revoked createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Partner Performance (Métricas de Performance) export const crmPartnerPerformance = pgTable("crm_partner_performance", { id: serial("id").primaryKey(), partnerId: integer("partner_id").notNull().references(() => crmPartners.id, { onDelete: "cascade" }), period: text("period").notNull(), // 2026-01, 2026-Q1, 2026 periodType: text("period_type").notNull(), // monthly, quarterly, yearly arrGenerated: integer("arr_generated").default(0), newClients: integer("new_clients").default(0), certifiedProfessionals: integer("certified_professionals").default(0), npsAverage: integer("nps_average"), casesPublished: integer("cases_published").default(0), portalUsageRate: integer("portal_usage_rate").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Contracts (Contratos SaaS e Serviços) export const crmContracts = pgTable("crm_contracts", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), clientId: integer("client_id").references(() => pcClients.id), partnerId: integer("partner_id").references(() => crmPartners.id), opportunityId: integer("opportunity_id").references(() => pcCrmOpportunities.id), contractNumber: text("contract_number"), type: text("type").notNull(), // saas_subscription, implementation, customization, consulting status: text("status").default("draft"), // draft, pending_approval, active, completed, cancelled startDate: timestamp("start_date").notNull(), endDate: timestamp("end_date"), monthlyValue: integer("monthly_value").default(0), totalValue: integer("total_value").default(0), paymentTerms: text("payment_terms"), billingCycle: text("billing_cycle").default("monthly"), // monthly, quarterly, yearly autoRenew: text("auto_renew").default("true"), signedAt: timestamp("signed_at"), signedBy: text("signed_by"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Revenue Schedule (Cronograma de Receita Recorrente) export const crmRevenueSchedule = pgTable("crm_revenue_schedule", { id: serial("id").primaryKey(), contractId: integer("contract_id").notNull().references(() => crmContracts.id, { onDelete: "cascade" }), month: integer("month").notNull(), // 1, 2, 3... dueDate: timestamp("due_date").notNull(), value: integer("value").notNull(), status: text("status").default("pending"), // pending, invoiced, paid, overdue invoiceNumber: text("invoice_number"), paidAt: timestamp("paid_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Commission Rules (Regras de Comissionamento) export const crmCommissionRules = pgTable("crm_commission_rules", { id: serial("id").primaryKey(), name: text("name").notNull(), description: text("description"), revenueType: text("revenue_type").notNull(), // recurring, services saleScenario: text("sale_scenario").notNull(), // direct, referral role: text("role"), // sdr, ae, sales_leader, partner monthRangeStart: integer("month_range_start"), // 1, 6 monthRangeEnd: integer("month_range_end"), // 5, null (perpetuo) percentage: integer("percentage").notNull(), // 500 = 5.00% isActive: text("is_active").default("true"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Commission Calculations (Cálculos de Comissão) export const crmCommissions = pgTable("crm_commissions", { id: serial("id").primaryKey(), contractId: integer("contract_id").notNull().references(() => crmContracts.id, { onDelete: "cascade" }), revenueScheduleId: integer("revenue_schedule_id").references(() => crmRevenueSchedule.id), ruleId: integer("rule_id").references(() => crmCommissionRules.id), partnerId: integer("partner_id").references(() => crmPartners.id), userId: varchar("user_id").references(() => users.id), role: text("role"), // partner, sdr, ae, sales_leader baseValue: integer("base_value").notNull(), percentage: integer("percentage").notNull(), commissionValue: integer("commission_value").notNull(), period: text("period").notNull(), // 2026-01 status: text("status").default("pending"), // pending, approved, paid paidAt: timestamp("paid_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Communication Channels (Canais de Comunicação - WhatsApp, Email) export const crmChannels = pgTable("crm_channels", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), type: text("type").notNull(), // whatsapp, email, sms name: text("name").notNull(), identifier: text("identifier"), // phone number, email address status: text("status").default("disconnected"), // connected, disconnected, connecting sessionData: text("session_data"), qrCode: text("qr_code"), lastConnectedAt: timestamp("last_connected_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Communication Threads (Conversas) export const crmThreads = pgTable("crm_threads", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), channelId: integer("channel_id").references(() => crmChannels.id), clientId: integer("client_id").references(() => pcClients.id), leadId: integer("lead_id").references(() => pcCrmLeads.id), contactPhone: text("contact_phone"), contactEmail: text("contact_email"), contactName: text("contact_name"), status: text("status").default("open"), // open, pending, resolved, closed priority: text("priority").default("normal"), // low, normal, high, urgent assignedToId: varchar("assigned_to_id").references(() => users.id), queueId: integer("queue_id"), lastMessageAt: timestamp("last_message_at"), unreadCount: integer("unread_count").default(0), tags: text("tags").array(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Messages (Mensagens) export const crmMessages = pgTable("crm_messages", { id: serial("id").primaryKey(), threadId: integer("thread_id").notNull().references(() => crmThreads.id, { onDelete: "cascade" }), channelId: integer("channel_id").references(() => crmChannels.id), direction: text("direction").notNull(), // inbound, outbound type: text("type").default("text"), // text, image, audio, video, document content: text("content"), mediaUrl: text("media_url"), mediaType: text("media_type"), externalId: text("external_id"), // ID da mensagem no WhatsApp status: text("status").default("sent"), // pending, sent, delivered, read, failed sentById: varchar("sent_by_id").references(() => users.id), isFromAgent: text("is_from_agent").default("false"), // Enviada pelo Agente IA metadata: text("metadata"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Quick Messages (Mensagens Rápidas/Templates) export const crmQuickMessages = pgTable("crm_quick_messages", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").references(() => users.id), shortcut: text("shortcut").notNull(), // /ola, /preco title: text("title").notNull(), content: text("content").notNull(), mediaUrl: text("media_url"), category: text("category"), isGlobal: text("is_global").default("false"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Campaigns (Campanhas de Mensagens) export const crmCampaigns = pgTable("crm_campaigns", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), channelId: integer("channel_id").references(() => crmChannels.id), name: text("name").notNull(), description: text("description"), messageContent: text("message_content").notNull(), mediaUrl: text("media_url"), status: text("status").default("draft"), // draft, scheduled, running, paused, completed scheduledAt: timestamp("scheduled_at"), startedAt: timestamp("started_at"), completedAt: timestamp("completed_at"), totalContacts: integer("total_contacts").default(0), sentCount: integer("sent_count").default(0), deliveredCount: integer("delivered_count").default(0), readCount: integer("read_count").default(0), failedCount: integer("failed_count").default(0), createdById: varchar("created_by_id").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Campaign Contacts (Contatos da Campanha) export const crmCampaignContacts = pgTable("crm_campaign_contacts", { id: serial("id").primaryKey(), campaignId: integer("campaign_id").notNull().references(() => crmCampaigns.id, { onDelete: "cascade" }), phone: text("phone"), email: text("email"), name: text("name"), status: text("status").default("pending"), // pending, sent, delivered, read, failed sentAt: timestamp("sent_at"), deliveredAt: timestamp("delivered_at"), readAt: timestamp("read_at"), errorMessage: text("error_message"), }); // Calendar Events (Eventos de Calendário) export const crmEvents = pgTable("crm_events", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").notNull().references(() => users.id), opportunityId: integer("opportunity_id").references(() => pcCrmOpportunities.id), leadId: integer("lead_id").references(() => pcCrmLeads.id), clientId: integer("client_id").references(() => pcClients.id), title: text("title").notNull(), description: text("description"), type: text("type").default("meeting"), // meeting, call, task, reminder startAt: timestamp("start_at").notNull(), endAt: timestamp("end_at"), allDay: text("all_day").default("false"), location: text("location"), meetingLink: text("meeting_link"), googleEventId: text("google_event_id"), attendees: text("attendees").array(), reminders: text("reminders"), status: text("status").default("scheduled"), // scheduled, completed, cancelled completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Google Calendar Tokens (Tokens OAuth do Google) export const crmGoogleTokens = pgTable("crm_google_tokens", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id).unique(), accessToken: text("access_token").notNull(), refreshToken: text("refresh_token"), expiresAt: timestamp("expires_at"), scope: text("scope"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // CRM Products/Services (Produtos e Serviços) export const crmProducts = pgTable("crm_products", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), type: text("type").default("service"), // product, service category: text("category"), price: integer("price").default(0), // Price in cents currency: text("currency").default("BRL"), unit: text("unit").default("unit"), // unit, hour, month, project isActive: text("is_active").default("true"), sku: text("sku"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // CRM Clients (Clientes do CRM) export const crmClients = pgTable("crm_clients", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").references(() => users.id), name: text("name").notNull(), tradeName: text("trade_name"), cnpj: text("cnpj"), email: text("email"), phone: text("phone"), website: text("website"), address: text("address"), city: text("city"), state: text("state"), segment: text("segment"), primaryContactName: text("primary_contact_name"), primaryContactEmail: text("primary_contact_email"), primaryContactPhone: text("primary_contact_phone"), notes: text("notes"), status: text("status").default("active"), // active, inactive, churned source: text("source"), // lead, partner, direct, referral convertedFromLeadId: integer("converted_from_lead_id"), convertedFromPartnerId: integer("converted_from_partner_id"), partnerId: integer("partner_id"), // Parceiro associado ao cliente createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // CRM Sales Pipeline Stages (Estágios do Funil de Vendas) export const crmPipelineStages = pgTable("crm_pipeline_stages", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), color: text("color").default("#3b82f6"), orderIndex: integer("order_index").default(0), probability: integer("probability").default(50), // Default win probability 0-100 createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // CRM Leads (Leads do CRM Arcádia) export const crmLeads = pgTable("crm_leads", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").references(() => users.id), name: text("name").notNull(), email: text("email"), phone: text("phone"), company: text("company"), position: text("position"), source: text("source"), // website, referral, linkedin, event, other status: text("status").default("new"), // new, contacted, qualified, unqualified, converted notes: text("notes"), tags: text("tags").array(), assignedTo: varchar("assigned_to").references(() => users.id), convertedAt: timestamp("converted_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // CRM Opportunities (Oportunidades de Venda) export const crmOpportunities = pgTable("crm_opportunities", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), userId: varchar("user_id").references(() => users.id), leadId: integer("lead_id").references(() => crmLeads.id), partnerId: integer("partner_id").references(() => crmPartners.id), stageId: integer("stage_id").references(() => crmPipelineStages.id), name: text("name").notNull(), description: text("description"), value: integer("value").default(0), // Value in cents currency: text("currency").default("BRL"), probability: integer("probability").default(50), // 0-100 expectedCloseDate: timestamp("expected_close_date"), actualCloseDate: timestamp("actual_close_date"), status: text("status").default("open"), // open, won, lost lossReason: text("loss_reason"), assignedTo: varchar("assigned_to").references(() => users.id), approvalStatus: text("approval_status").default("pending"), // pending, approved, rejected approvedAt: timestamp("approved_at"), approvedBy: varchar("approved_by").references(() => users.id), processCompassProjectId: integer("process_compass_project_id"), billingStatus: text("billing_status").default("none"), // none, pending, invoiced, paid createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // CRM Opportunity Products (Produtos vinculados a oportunidades) export const crmOpportunityProducts = pgTable("crm_opportunity_products", { id: serial("id").primaryKey(), opportunityId: integer("opportunity_id").notNull().references(() => crmOpportunities.id, { onDelete: "cascade" }), productId: integer("product_id").notNull().references(() => crmProducts.id), quantity: integer("quantity").default(1), unitPrice: integer("unit_price").default(0), // Price at time of sale discount: integer("discount").default(0), // Discount percentage total: integer("total").default(0), // Total in cents }); // CRM Proposals (Propostas Comerciais) export const crmProposals = pgTable("crm_proposals", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), opportunityId: integer("opportunity_id").references(() => crmOpportunities.id, { onDelete: "set null" }), clientId: integer("client_id").references(() => crmClients.id, { onDelete: "set null" }), code: text("code"), title: text("title").notNull(), description: text("description"), version: integer("version").default(1), status: text("status").default("draft"), // draft, sent, viewed, accepted, rejected, expired validUntil: timestamp("valid_until"), totalValue: integer("total_value").default(0), // Total in cents currency: text("currency").default("BRL"), paymentTerms: text("payment_terms"), deliveryTerms: text("delivery_terms"), notes: text("notes"), internalNotes: text("internal_notes"), sentAt: timestamp("sent_at"), viewedAt: timestamp("viewed_at"), acceptedAt: timestamp("accepted_at"), rejectedAt: timestamp("rejected_at"), rejectionReason: text("rejection_reason"), createdById: varchar("created_by_id").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // CRM Proposal Items (Itens da Proposta) export const crmProposalItems = pgTable("crm_proposal_items", { id: serial("id").primaryKey(), proposalId: integer("proposal_id").notNull().references(() => crmProposals.id, { onDelete: "cascade" }), productId: integer("product_id").references(() => crmProducts.id), itemType: text("item_type").default("product"), // product, service, custom name: text("name").notNull(), description: text("description"), quantity: integer("quantity").default(1), unitPrice: integer("unit_price").default(0), discount: integer("discount").default(0), total: integer("total").default(0), orderIndex: integer("order_index").default(0), }); // CRM Contract Milestones (Marcos de Entrega do Contrato) export const crmContractMilestones = pgTable("crm_contract_milestones", { id: serial("id").primaryKey(), contractId: integer("contract_id").notNull().references(() => crmContracts.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), dueDate: timestamp("due_date"), completedDate: timestamp("completed_date"), status: text("status").default("pending"), // pending, in_progress, completed, delayed deliverables: text("deliverables"), billingAmount: integer("billing_amount").default(0), // Amount in cents to bill on completion billingStatus: text("billing_status").default("pending"), // pending, invoiced, paid orderIndex: integer("order_index").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Opportunity Registration (Registro de Oportunidades - Proteção 90 dias) export const crmOpportunityRegistrations = pgTable("crm_opportunity_registrations", { id: serial("id").primaryKey(), partnerId: integer("partner_id").notNull().references(() => crmPartners.id), opportunityId: integer("opportunity_id").notNull().references(() => pcCrmOpportunities.id), registeredAt: timestamp("registered_at").default(sql`CURRENT_TIMESTAMP`).notNull(), expiresAt: timestamp("expires_at").notNull(), // registeredAt + 90 dias status: text("status").default("active"), // active, expired, converted notes: text("notes"), }); // Insert Schemas for Process Compass export const insertPcClientSchema = createInsertSchema(pcClients).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPcClientContactSchema = createInsertSchema(pcClientContacts).omit({ id: true, createdAt: true }); export const insertPcProjectSchema = createInsertSchema(pcProjects).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPcProjectMemberSchema = createInsertSchema(pcProjectMembers).omit({ id: true, assignedAt: true }); export const insertPcProjectActivitySchema = createInsertSchema(pcProjectActivities).omit({ id: true, createdAt: true }); export const insertPcCanvasBlockSchema = createInsertSchema(pcCanvasBlocks).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPcCanvasQuestionSchema = createInsertSchema(pcCanvasQuestions).omit({ id: true, createdAt: true }); export const insertPcCanvasExpectedOutputSchema = createInsertSchema(pcCanvasExpectedOutputs).omit({ id: true, createdAt: true }); export const insertPcCanvasPdcaLinkSchema = createInsertSchema(pcCanvasPdcaLinks).omit({ id: true, createdAt: true }); export const insertPcCanvasSwotLinkSchema = createInsertSchema(pcCanvasSwotLinks).omit({ id: true, createdAt: true }); export const insertPcProcessSchema = createInsertSchema(pcProcesses).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPcProcessStepSchema = createInsertSchema(pcProcessSteps).omit({ id: true, createdAt: true }); export const insertPcSwotAnalysisSchema = createInsertSchema(pcSwotAnalyses).omit({ id: true, createdAt: true, updatedAt: true, analysisDate: true }); export const insertPcSwotItemSchema = createInsertSchema(pcSwotItems).omit({ id: true, createdAt: true }); export const updatePcSwotItemSchema = insertPcSwotItemSchema.partial(); export const insertPcCrmStageSchema = createInsertSchema(pcCrmStages).omit({ id: true, createdAt: true }); export const insertPcCrmLeadSchema = createInsertSchema(pcCrmLeads).omit({ id: true, createdAt: true, updatedAt: true, convertedAt: true }); export const insertPcCrmOpportunitySchema = createInsertSchema(pcCrmOpportunities).omit({ id: true, createdAt: true, updatedAt: true, actualCloseDate: true }); export const insertPcCrmActivitySchema = createInsertSchema(pcCrmActivities).omit({ id: true, createdAt: true, completedAt: true }); export const insertPcDeliverableSchema = createInsertSchema(pcDeliverables).omit({ id: true, createdAt: true, completedAt: true }); export const insertPcTaskSchema = createInsertSchema(pcTasks).omit({ id: true, createdAt: true, completedAt: true }); export const insertPcPdcaCycleSchema = createInsertSchema(pcPdcaCycles).omit({ id: true, createdAt: true, updatedAt: true, completedAt: true }); export const insertPcPdcaActionSchema = createInsertSchema(pcPdcaActions).omit({ id: true, createdAt: true, completedAt: true }); export const insertPcRequirementSchema = createInsertSchema(pcRequirements).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPcProjectTeamMemberSchema = createInsertSchema(pcProjectTeamMembers).omit({ id: true, createdAt: true }); export const insertPcProjectTaskSchema = createInsertSchema(pcProjectTasks).omit({ id: true, createdAt: true }); export const insertPcProjectFileSchema = createInsertSchema(pcProjectFiles).omit({ id: true, createdAt: true }); export const insertPcProjectHistorySchema = createInsertSchema(pcProjectHistory).omit({ id: true, updatedAt: true }); // Types for Process Compass export type PcClient = typeof pcClients.$inferSelect; export type InsertPcClient = z.infer; export type PcClientContact = typeof pcClientContacts.$inferSelect; export type InsertPcClientContact = z.infer; export type PcProject = typeof pcProjects.$inferSelect; export type InsertPcProject = z.infer; export type PcProjectMember = typeof pcProjectMembers.$inferSelect; export type InsertPcProjectMember = z.infer; export type PcProjectActivity = typeof pcProjectActivities.$inferSelect; export type InsertPcProjectActivity = z.infer; export type PcCanvasBlock = typeof pcCanvasBlocks.$inferSelect; export type InsertPcCanvasBlock = z.infer; export type PcCanvasQuestion = typeof pcCanvasQuestions.$inferSelect; export type InsertPcCanvasQuestion = z.infer; export type PcCanvasExpectedOutput = typeof pcCanvasExpectedOutputs.$inferSelect; export type InsertPcCanvasExpectedOutput = z.infer; export type PcCanvasPdcaLink = typeof pcCanvasPdcaLinks.$inferSelect; export type InsertPcCanvasPdcaLink = z.infer; export type PcCanvasSwotLink = typeof pcCanvasSwotLinks.$inferSelect; export type InsertPcCanvasSwotLink = z.infer; export type PcProcess = typeof pcProcesses.$inferSelect; export type InsertPcProcess = z.infer; export type PcProcessStep = typeof pcProcessSteps.$inferSelect; export type InsertPcProcessStep = z.infer; export type PcSwotAnalysis = typeof pcSwotAnalyses.$inferSelect; export type InsertPcSwotAnalysis = z.infer; export type PcSwotItem = typeof pcSwotItems.$inferSelect; export type InsertPcSwotItem = z.infer; export type PcCrmStage = typeof pcCrmStages.$inferSelect; export type InsertPcCrmStage = z.infer; export type PcCrmLead = typeof pcCrmLeads.$inferSelect; export type InsertPcCrmLead = z.infer; export type PcCrmOpportunity = typeof pcCrmOpportunities.$inferSelect; export type InsertPcCrmOpportunity = z.infer; export type PcCrmActivity = typeof pcCrmActivities.$inferSelect; export type InsertPcCrmActivity = z.infer; export type PcDeliverable = typeof pcDeliverables.$inferSelect; export type InsertPcDeliverable = z.infer; export type PcTask = typeof pcTasks.$inferSelect; export type InsertPcTask = z.infer; export type PcPdcaCycle = typeof pcPdcaCycles.$inferSelect; export type InsertPcPdcaCycle = z.infer; export type PcPdcaAction = typeof pcPdcaActions.$inferSelect; export type InsertPcPdcaAction = z.infer; export type PcRequirement = typeof pcRequirements.$inferSelect; export type InsertPcRequirement = z.infer; export type PcProjectTeamMember = typeof pcProjectTeamMembers.$inferSelect; export type InsertPcProjectTeamMember = z.infer; export type PcProjectTask = typeof pcProjectTasks.$inferSelect; export type InsertPcProjectTask = z.infer; export type PcProjectFile = typeof pcProjectFiles.$inferSelect; export type InsertPcProjectFile = z.infer; export type PcProjectHistory = typeof pcProjectHistory.$inferSelect; export type InsertPcProjectHistory = z.infer; // ========================================== // Insert Schemas for Arcádia CRM // ========================================== export const insertCrmPartnerSchema = createInsertSchema(crmPartners).omit({ id: true, createdAt: true, updatedAt: true }); export const insertCrmPartnerCertificationSchema = createInsertSchema(crmPartnerCertifications).omit({ id: true, createdAt: true }); export const insertCrmPartnerPerformanceSchema = createInsertSchema(crmPartnerPerformance).omit({ id: true, createdAt: true }); export const insertCrmContractSchema = createInsertSchema(crmContracts).omit({ id: true, createdAt: true, updatedAt: true, signedAt: true }); export const insertCrmRevenueScheduleSchema = createInsertSchema(crmRevenueSchedule).omit({ id: true, createdAt: true, paidAt: true }); export const insertCrmCommissionRuleSchema = createInsertSchema(crmCommissionRules).omit({ id: true, createdAt: true }); export const insertCrmCommissionSchema = createInsertSchema(crmCommissions).omit({ id: true, createdAt: true, paidAt: true }); export const insertCrmChannelSchema = createInsertSchema(crmChannels).omit({ id: true, createdAt: true, updatedAt: true, lastConnectedAt: true }); export const insertCrmThreadSchema = createInsertSchema(crmThreads).omit({ id: true, createdAt: true, updatedAt: true, lastMessageAt: true }); export const insertCrmMessageSchema = createInsertSchema(crmMessages).omit({ id: true, createdAt: true }); export const insertCrmQuickMessageSchema = createInsertSchema(crmQuickMessages).omit({ id: true, createdAt: true }); export const insertCrmCampaignSchema = createInsertSchema(crmCampaigns).omit({ id: true, createdAt: true, startedAt: true, completedAt: true }); export const insertCrmCampaignContactSchema = createInsertSchema(crmCampaignContacts).omit({ id: true, sentAt: true, deliveredAt: true, readAt: true }); export const insertCrmEventSchema = createInsertSchema(crmEvents).omit({ id: true, createdAt: true, updatedAt: true, completedAt: true }); export const insertCrmGoogleTokenSchema = createInsertSchema(crmGoogleTokens).omit({ id: true, createdAt: true, updatedAt: true }); export const insertCrmOpportunityRegistrationSchema = createInsertSchema(crmOpportunityRegistrations).omit({ id: true, registeredAt: true }); export const insertCrmProductSchema = createInsertSchema(crmProducts).omit({ id: true, createdAt: true, updatedAt: true }); export const insertCrmClientSchema = createInsertSchema(crmClients).omit({ id: true, createdAt: true, updatedAt: true }); export const insertCrmPipelineStageSchema = createInsertSchema(crmPipelineStages).omit({ id: true, createdAt: true }); export const insertCrmLeadSchema = createInsertSchema(crmLeads).omit({ id: true, createdAt: true, updatedAt: true, convertedAt: true }); export const insertCrmOpportunitySchema = createInsertSchema(crmOpportunities).omit({ id: true, createdAt: true, updatedAt: true, actualCloseDate: true }); export const insertCrmOpportunityProductSchema = createInsertSchema(crmOpportunityProducts).omit({ id: true }); export const insertCrmProposalSchema = createInsertSchema(crmProposals).omit({ id: true, createdAt: true, updatedAt: true, sentAt: true, viewedAt: true, acceptedAt: true, rejectedAt: true }); export const insertCrmProposalItemSchema = createInsertSchema(crmProposalItems).omit({ id: true }); export const insertCrmContractMilestoneSchema = createInsertSchema(crmContractMilestones).omit({ id: true, createdAt: true, completedDate: true }); // Types for Arcádia CRM export type CrmPartner = typeof crmPartners.$inferSelect; export type InsertCrmPartner = z.infer; export type CrmPartnerCertification = typeof crmPartnerCertifications.$inferSelect; export type InsertCrmPartnerCertification = z.infer; export type CrmPartnerPerformance = typeof crmPartnerPerformance.$inferSelect; export type InsertCrmPartnerPerformance = z.infer; export type CrmContract = typeof crmContracts.$inferSelect; export type InsertCrmContract = z.infer; export type CrmRevenueSchedule = typeof crmRevenueSchedule.$inferSelect; export type InsertCrmRevenueSchedule = z.infer; export type CrmCommissionRule = typeof crmCommissionRules.$inferSelect; export type InsertCrmCommissionRule = z.infer; export type CrmCommission = typeof crmCommissions.$inferSelect; export type InsertCrmCommission = z.infer; export type CrmChannel = typeof crmChannels.$inferSelect; export type InsertCrmChannel = z.infer; export type CrmThread = typeof crmThreads.$inferSelect; export type InsertCrmThread = z.infer; export type CrmMessage = typeof crmMessages.$inferSelect; export type InsertCrmMessage = z.infer; export type CrmQuickMessage = typeof crmQuickMessages.$inferSelect; export type InsertCrmQuickMessage = z.infer; export type CrmCampaign = typeof crmCampaigns.$inferSelect; export type InsertCrmCampaign = z.infer; export type CrmCampaignContact = typeof crmCampaignContacts.$inferSelect; export type InsertCrmCampaignContact = z.infer; export type CrmEvent = typeof crmEvents.$inferSelect; export type InsertCrmEvent = z.infer; export type CrmGoogleToken = typeof crmGoogleTokens.$inferSelect; export type InsertCrmGoogleToken = z.infer; export type CrmOpportunityRegistration = typeof crmOpportunityRegistrations.$inferSelect; export type InsertCrmOpportunityRegistration = z.infer; export type CrmProduct = typeof crmProducts.$inferSelect; export type InsertCrmProduct = z.infer; export type CrmClient = typeof crmClients.$inferSelect; export type InsertCrmClient = z.infer; export type CrmPipelineStage = typeof crmPipelineStages.$inferSelect; export type InsertCrmPipelineStage = z.infer; export type CrmLead = typeof crmLeads.$inferSelect; export type InsertCrmLead = z.infer; export type CrmOpportunity = typeof crmOpportunities.$inferSelect; export type InsertCrmOpportunity = z.infer; export type CrmOpportunityProduct = typeof crmOpportunityProducts.$inferSelect; export type InsertCrmOpportunityProduct = z.infer; export type CrmProposal = typeof crmProposals.$inferSelect; export type InsertCrmProposal = z.infer; export type CrmProposalItem = typeof crmProposalItems.$inferSelect; export type InsertCrmProposalItem = z.infer; export type CrmContractMilestone = typeof crmContractMilestones.$inferSelect; export type InsertCrmContractMilestone = z.infer; // ========== CRM - Frappe ERPNext/CRM Connector ========== export const crmFrappeConnectors = pgTable("crm_frappe_connectors", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), name: text("name").notNull(), baseUrl: text("base_url").notNull(), apiKey: text("api_key").notNull(), apiSecret: text("api_secret").notNull(), frappeUser: text("frappe_user"), defaultCompany: text("default_company"), targetSystem: text("target_system").default("erpnext"), syncMode: text("sync_mode").default("manual"), syncEntities: text("sync_entities").array(), lastSyncAt: timestamp("last_sync_at"), status: text("status").default("inactive"), errorMessage: text("error_message"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const crmFrappeMappings = pgTable("crm_frappe_mappings", { id: serial("id").primaryKey(), connectorId: integer("connector_id").notNull().references(() => crmFrappeConnectors.id, { onDelete: "cascade" }), localEntity: text("local_entity").notNull(), frappeDoctype: text("frappe_doctype").notNull(), fieldMappings: text("field_mappings"), statusMappings: text("status_mappings"), syncDirection: text("sync_direction").default("push"), isEnabled: integer("is_enabled").default(1), }); export const crmSyncLogs = pgTable("crm_sync_logs", { id: serial("id").primaryKey(), connectorId: integer("connector_id").notNull().references(() => crmFrappeConnectors.id, { onDelete: "cascade" }), syncType: text("sync_type").notNull(), entity: text("entity"), recordsProcessed: integer("records_processed").default(0), recordsSuccess: integer("records_success").default(0), recordsFailed: integer("records_failed").default(0), status: text("status").notNull(), errorDetails: text("error_details"), startedAt: timestamp("started_at").default(sql`CURRENT_TIMESTAMP`).notNull(), completedAt: timestamp("completed_at"), }); export const insertCrmFrappeConnectorSchema = createInsertSchema(crmFrappeConnectors).omit({ id: true, createdAt: true, updatedAt: true }); export const insertCrmFrappeMappingSchema = createInsertSchema(crmFrappeMappings).omit({ id: true }); export const insertCrmSyncLogSchema = createInsertSchema(crmSyncLogs).omit({ id: true, startedAt: true }); export type CrmFrappeConnector = typeof crmFrappeConnectors.$inferSelect; export type InsertCrmFrappeConnector = z.infer; export type CrmFrappeMapping = typeof crmFrappeMappings.$inferSelect; export type InsertCrmFrappeMapping = z.infer; export type CrmSyncLog = typeof crmSyncLogs.$inferSelect; export type InsertCrmSyncLog = z.infer; // ========================================== // MÓDULO DE PRODUÇÃO - CENTRAL DE PRODUÇÃO // ========================================== // Squads (Equipes de Produção) export const pcSquads = pgTable("pc_squads", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), leaderId: varchar("leader_id").references(() => users.id), productOwnerId: varchar("product_owner_id"), techLeadId: varchar("tech_lead_id"), color: text("color").default("#3b82f6"), status: text("status").default("active"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Squad Members (supports users and external collaborators) export const pcSquadMembers = pgTable("pc_squad_members", { id: serial("id").primaryKey(), squadId: integer("squad_id").notNull().references(() => pcSquads.id, { onDelete: "cascade" }), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }), collaboratorId: varchar("collaborator_id"), memberRole: text("member_role").default("member"), joinedAt: timestamp("joined_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Sprints export const pcSprints = pgTable("pc_sprints", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }), squadId: integer("squad_id").references(() => pcSquads.id, { onDelete: "set null" }), name: text("name").notNull(), goal: text("goal"), startDate: timestamp("start_date"), endDate: timestamp("end_date"), status: text("status").default("planning"), velocity: integer("velocity"), completedPoints: integer("completed_points").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Work Items (Backlog Unificado) export const pcWorkItems = pgTable("pc_work_items", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }), sprintId: integer("sprint_id").references(() => pcSprints.id, { onDelete: "set null" }), parentId: integer("parent_id"), code: text("code"), title: text("title").notNull(), description: text("description"), type: text("type").notNull().default("task"), origin: text("origin").default("direct"), originId: integer("origin_id"), originType: text("origin_type"), status: text("status").default("backlog"), priority: text("priority").default("medium"), storyPoints: integer("story_points"), effortScore: integer("effort_score"), estimatedHours: numeric("estimated_hours", { precision: 10, scale: 2 }), actualHours: numeric("actual_hours", { precision: 10, scale: 2 }), hourlyRate: numeric("hourly_rate", { precision: 10, scale: 2 }), totalCost: numeric("total_cost", { precision: 12, scale: 2 }), dueDate: timestamp("due_date"), assigneeId: varchar("assignee_id").references(() => users.id), createdById: varchar("created_by_id").references(() => users.id), tags: text("tags").array(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), completedAt: timestamp("completed_at"), }); // Work Item Comments export const pcWorkItemComments = pgTable("pc_work_item_comments", { id: serial("id").primaryKey(), workItemId: integer("work_item_id").notNull().references(() => pcWorkItems.id, { onDelete: "cascade" }), userId: varchar("user_id").notNull().references(() => users.id), content: text("content").notNull(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Timesheet Entries export const pcTimesheetEntries = pgTable("pc_timesheet_entries", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), workItemId: integer("work_item_id").references(() => pcWorkItems.id, { onDelete: "cascade" }), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }), sprintId: integer("sprint_id").references(() => pcSprints.id, { onDelete: "set null" }), userId: varchar("user_id").references(() => users.id), collaboratorId: integer("collaborator_id"), date: timestamp("date").notNull(), hours: numeric("hours", { precision: 6, scale: 2 }).notNull(), description: text("description"), billable: integer("billable").default(1), hourlyRate: numeric("hourly_rate", { precision: 10, scale: 2 }), totalCost: numeric("total_cost", { precision: 12, scale: 2 }), status: text("status").default("draft"), // draft, pending, approved, rejected timerStartedAt: timestamp("timer_started_at"), approvedById: varchar("approved_by_id").references(() => users.id), approvedAt: timestamp("approved_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Tenant Production Settings (configurações comerciais) export const tenantProductionSettings = pgTable("tenant_production_settings", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }).unique(), timesheetRequiresApproval: integer("timesheet_requires_approval").default(0), timesheetAllowTimer: integer("timesheet_allow_timer").default(1), defaultHourlyRate: numeric("default_hourly_rate", { precision: 10, scale: 2 }).default("0"), workHoursPerDay: numeric("work_hours_per_day", { precision: 4, scale: 2 }).default("8"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertTenantProductionSettingsSchema = createInsertSchema(tenantProductionSettings).omit({ id: true, createdAt: true, updatedAt: true }); export type TenantProductionSettings = typeof tenantProductionSettings.$inferSelect; export type InsertTenantProductionSettings = z.infer; // ========================================== // MÓDULO DE SUPORTE - TICKETS E ATENDIMENTO // ========================================== // Support Tickets export const supportTickets = pgTable("support_tickets", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), clientId: integer("client_id").references(() => crmClients.id, { onDelete: "set null" }), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "set null" }), code: text("code"), title: text("title").notNull(), description: text("description"), category: text("category").default("general"), priority: text("priority").default("medium"), status: text("status").default("open"), channel: text("channel").default("portal"), assigneeId: varchar("assignee_id").references(() => users.id), createdById: varchar("created_by_id").references(() => users.id), workItemId: integer("work_item_id").references(() => pcWorkItems.id, { onDelete: "set null" }), resolvedAt: timestamp("resolved_at"), firstResponseAt: timestamp("first_response_at"), closedAt: timestamp("closed_at"), slaDeadline: timestamp("sla_deadline"), satisfaction: integer("satisfaction"), tags: text("tags").array(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Support Conversations export const supportConversations = pgTable("support_conversations", { id: serial("id").primaryKey(), ticketId: integer("ticket_id").notNull().references(() => supportTickets.id, { onDelete: "cascade" }), userId: varchar("user_id").references(() => users.id), senderType: text("sender_type").notNull(), content: text("content").notNull(), isAiGenerated: integer("is_ai_generated").default(0), aiModel: text("ai_model"), attachments: text("attachments").array(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Knowledge Base Articles export const supportKnowledgeBase = pgTable("support_knowledge_base", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), title: text("title").notNull(), content: text("content").notNull(), category: text("category"), tags: text("tags").array(), status: text("status").default("published"), viewCount: integer("view_count").default(0), helpfulCount: integer("helpful_count").default(0), authorId: varchar("author_id").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Collaborators (Freelancers, Programmers, Consultants) export const pcCollaborators = pgTable("pc_collaborators", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: text("name").notNull(), email: text("email"), phone: text("phone"), type: text("type").notNull().default("programador"), hourlyRate: numeric("hourly_rate", { precision: 10, scale: 2 }).default("0"), skills: text("skills").array(), status: text("status").default("active"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert Schemas export const insertPcSquadSchema = createInsertSchema(pcSquads).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPcSquadMemberSchema = createInsertSchema(pcSquadMembers).omit({ id: true, joinedAt: true }); export const insertPcSprintSchema = createInsertSchema(pcSprints).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPcWorkItemSchema = createInsertSchema(pcWorkItems).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPcWorkItemCommentSchema = createInsertSchema(pcWorkItemComments).omit({ id: true, createdAt: true }); export const insertPcTimesheetEntrySchema = createInsertSchema(pcTimesheetEntries).omit({ id: true, createdAt: true }); export const insertPcCollaboratorSchema = createInsertSchema(pcCollaborators).omit({ id: true, createdAt: true, updatedAt: true }); export const insertSupportTicketSchema = createInsertSchema(supportTickets).omit({ id: true, createdAt: true, updatedAt: true }); export const insertSupportConversationSchema = createInsertSchema(supportConversations).omit({ id: true, createdAt: true }); export const insertSupportKnowledgeBaseSchema = createInsertSchema(supportKnowledgeBase).omit({ id: true, createdAt: true, updatedAt: true }); // Types export type PcSquad = typeof pcSquads.$inferSelect; export type InsertPcSquad = z.infer; export type PcSquadMember = typeof pcSquadMembers.$inferSelect; export type InsertPcSquadMember = z.infer; export type PcSprint = typeof pcSprints.$inferSelect; export type InsertPcSprint = z.infer; export type PcWorkItem = typeof pcWorkItems.$inferSelect; export type InsertPcWorkItem = z.infer; export type PcWorkItemComment = typeof pcWorkItemComments.$inferSelect; export type InsertPcWorkItemComment = z.infer; export type PcTimesheetEntry = typeof pcTimesheetEntries.$inferSelect; export type InsertPcTimesheetEntry = z.infer; export type PcCollaborator = typeof pcCollaborators.$inferSelect; export type InsertPcCollaborator = z.infer; export type SupportTicket = typeof supportTickets.$inferSelect; export type InsertSupportTicket = z.infer; export type SupportConversation = typeof supportConversations.$inferSelect; export type InsertSupportConversation = z.infer; export type SupportKnowledgeBase = typeof supportKnowledgeBase.$inferSelect; export type InsertSupportKnowledgeBase = z.infer; // ========================================== // MÓDULO DE RELATÓRIOS - COMPASS REPORTS // ========================================== // Report Templates (tipos de relatórios disponíveis) export const pcReportTemplates = pgTable("pc_report_templates", { id: serial("id").primaryKey(), name: text("name").notNull(), description: text("description"), templateType: text("template_type").notNull(), // executive_summary, full_diagnostic, swot_report, process_analysis, canvas_report, erp_adherence, custom sections: jsonb("sections").$type().default([]), isDefault: integer("is_default").default(0), isActive: integer("is_active").default(1), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Report Configurations (configurações salvas por projeto) export const pcReportConfigurations = pgTable("pc_report_configurations", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }), templateId: integer("template_id").references(() => pcReportTemplates.id, { onDelete: "set null" }), name: text("name").notNull(), description: text("description"), sections: jsonb("sections").$type().default([]), sectionOptions: jsonb("section_options").$type>().default({}), layoutOptions: jsonb("layout_options").$type<{ showCoverPage?: boolean; showTableOfContents?: boolean; showPageNumbers?: boolean; orientation?: 'portrait' | 'landscape'; logoUrl?: string; primaryColor?: string; }>().default({}), filters: jsonb("filters").$type<{ dateRange?: { start?: string; end?: string }; canvasLevels?: string[]; swotTypes?: string[]; processVariants?: string[]; }>().default({}), lastGeneratedAt: timestamp("last_generated_at"), createdById: varchar("created_by_id").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Generated Reports (histórico de relatórios gerados) export const pcGeneratedReports = pgTable("pc_generated_reports", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), configurationId: integer("configuration_id").references(() => pcReportConfigurations.id, { onDelete: "set null" }), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }), name: text("name").notNull(), reportType: text("report_type"), content: text("content"), // HTML content for editing format: text("format").notNull().default("pdf"), // pdf, docx, html filePath: text("file_path"), fileSize: integer("file_size"), status: text("status").default("pending"), // pending, generating, completed, failed generatedBy: varchar("generated_by").references(() => users.id), generatedAt: timestamp("generated_at"), updatedAt: timestamp("updated_at"), metadata: jsonb("metadata").$type>().default({}), }); // Insert Schemas for Reports export const insertPcReportTemplateSchema = createInsertSchema(pcReportTemplates).omit({ id: true, createdAt: true }); export const insertPcReportConfigurationSchema = createInsertSchema(pcReportConfigurations).omit({ id: true, createdAt: true, updatedAt: true, lastGeneratedAt: true }); export const insertPcGeneratedReportSchema = createInsertSchema(pcGeneratedReports).omit({ id: true, generatedAt: true }); // Types for Reports export type PcReportTemplate = typeof pcReportTemplates.$inferSelect; export type InsertPcReportTemplate = z.infer; export type PcReportConfiguration = typeof pcReportConfigurations.$inferSelect; export type InsertPcReportConfiguration = z.infer; export type PcGeneratedReport = typeof pcGeneratedReports.$inferSelect; export type InsertPcGeneratedReport = z.infer; // ========================================== // ERP ADHERENCE MODULE - ERP Implementation Assessment // ========================================== // ERP Modules catalog (for organizing requirements by ERP module) export const pcErpModules = pgTable("pc_erp_modules", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: text("name").notNull(), description: text("description"), category: text("category"), // Operacional, Financeiro, RH, etc. isActive: integer("is_active").default(1), orderIndex: integer("order_index").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ERP Requirements (for ERP implementation assessment) export const pcErpRequirements = pgTable("pc_erp_requirements", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }).notNull(), processId: integer("process_id").references(() => pcProcesses.id, { onDelete: "set null" }), erpModuleId: integer("erp_module_id").references(() => pcErpModules.id, { onDelete: "set null" }), requirement: text("requirement").notNull(), description: text("description"), erpModule: text("erp_module"), // Financeiro, Contabil, Faturamento, Compras, Estoque, Producao, RH, CRM adherenceStatus: text("adherence_status").default("nao_atendido"), // nativo, configuravel, customizavel, nao_atendido priority: text("priority").default("media"), // alta, media, baixa customizationNotes: text("customization_notes"), estimatedEffort: text("estimated_effort"), // hours/days processRedesignRequired: integer("process_redesign_required").default(0), // 0 = no, 1 = yes pdcaStatus: text("pdca_status").default("plan"), // plan, do, check, act, done recommendation: text("recommendation"), actionDueDate: timestamp("action_due_date"), actionAssigneeId: varchar("action_assignee_id").references(() => users.id), actionResult: text("action_result"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ERP Parameterization Topics (checklist groups for ERP configuration) export const pcErpParameterizationTopics = pgTable("pc_erp_parameterization_topics", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }).notNull(), name: text("name").notNull(), description: text("description"), erpModule: text("erp_module"), orderIndex: integer("order_index").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ERP Parameterization Items (checklist items within topics) export const pcErpParameterizationItems = pgTable("pc_erp_parameterization_items", { id: serial("id").primaryKey(), topicId: integer("topic_id").references(() => pcErpParameterizationTopics.id, { onDelete: "cascade" }).notNull(), name: text("name").notNull(), description: text("description"), isCompleted: integer("is_completed").default(0), // 0 = no, 1 = yes completedAt: timestamp("completed_at"), completedById: varchar("completed_by_id").references(() => users.id), notes: text("notes"), orderIndex: integer("order_index").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert Schemas for ERP Adherence export const insertPcErpModuleSchema = createInsertSchema(pcErpModules).omit({ id: true, createdAt: true }); export const insertPcErpRequirementSchema = createInsertSchema(pcErpRequirements).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPcErpParameterizationTopicSchema = createInsertSchema(pcErpParameterizationTopics).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPcErpParameterizationItemSchema = createInsertSchema(pcErpParameterizationItems).omit({ id: true, createdAt: true, updatedAt: true }); // Types for ERP Adherence export type PcErpModule = typeof pcErpModules.$inferSelect; export type InsertPcErpModule = z.infer; export type PcErpRequirement = typeof pcErpRequirements.$inferSelect; export type InsertPcErpRequirement = z.infer; export type PcErpParameterizationTopic = typeof pcErpParameterizationTopics.$inferSelect; export type InsertPcErpParameterizationTopic = z.infer; export type PcErpParameterizationItem = typeof pcErpParameterizationItems.$inferSelect; export type InsertPcErpParameterizationItem = z.infer; // ========================================== // VALUATION MODULE - Business Valuation // ========================================== // Valuation Projects export const valuationProjects = pgTable("valuation_projects", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }), companyName: text("company_name").notNull(), cnpj: text("cnpj"), sector: text("sector").notNull(), businessModel: text("business_model"), stage: text("stage").notNull(), size: text("size").notNull(), projectType: text("project_type").default("simple"), status: text("status").default("draft"), consultantId: varchar("consultant_id").references(() => users.id), clientUserId: varchar("client_user_id").references(() => users.id), clientId: integer("client_id").references(() => crmClients.id, { onDelete: "set null" }), valuationRangeMin: numeric("valuation_range_min"), valuationRangeMax: numeric("valuation_range_max"), finalValue: numeric("final_value"), currentValuation: numeric("current_valuation"), projectedValuation: numeric("projected_valuation"), governanceScore: numeric("governance_score"), checklistProgress: numeric("checklist_progress"), currency: text("currency").default("BRL"), baseDate: timestamp("base_date"), valuationObjective: text("valuation_objective"), foundingYear: integer("founding_year"), city: text("city"), state: text("state"), legalNature: text("legal_nature"), reportUrl: text("report_url"), notes: text("notes"), metadata: jsonb("metadata"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Valuation Financial Inputs (historical and projected data) export const valuationInputs = pgTable("valuation_inputs", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), year: integer("year").notNull(), isProjection: integer("is_projection").default(0), periodType: text("period_type").default("annual"), grossRevenue: numeric("gross_revenue"), revenue: numeric("revenue"), cogs: numeric("cogs"), grossProfit: numeric("gross_profit"), operatingExpenses: numeric("operating_expenses"), ebitda: numeric("ebitda"), ebit: numeric("ebit"), netIncome: numeric("net_income"), totalAssets: numeric("total_assets"), totalLiabilities: numeric("total_liabilities"), totalEquity: numeric("total_equity"), cash: numeric("cash"), debt: numeric("debt"), workingCapital: numeric("working_capital"), capex: numeric("capex"), depreciation: numeric("depreciation"), cashFlowOperations: numeric("cash_flow_operations"), freeCashFlow: numeric("free_cash_flow"), headcount: integer("headcount"), source: text("source").default("manual"), arr: numeric("arr"), mrr: numeric("mrr"), churnRate: numeric("churn_rate"), ltv: numeric("ltv"), cac: numeric("cac"), gmv: numeric("gmv"), tpv: numeric("tpv"), takeRate: numeric("take_rate"), growthRate: numeric("growth_rate"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Valuation Assumptions (macro and company-specific) export const valuationAssumptions = pgTable("valuation_assumptions", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), name: text("name").notNull(), category: text("category").notNull(), value: numeric("value"), unit: text("unit"), source: text("source"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Valuation Calculations (results from each methodology) export const valuationCalculations = pgTable("valuation_calculations", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), method: text("method").notNull(), weight: numeric("weight"), enterpriseValue: numeric("enterprise_value"), equityValue: numeric("equity_value"), assumptions: jsonb("assumptions"), sensitivityMatrix: jsonb("sensitivity_matrix"), details: jsonb("details"), version: integer("version").default(1), status: text("status").default("draft"), calculatedAt: timestamp("calculated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), calculatedBy: varchar("calculated_by").references(() => users.id), }); // Valuation Maturity Scores (qualitative analysis) export const valuationMaturityScores = pgTable("valuation_maturity_scores", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), dimension: text("dimension").notNull(), score: integer("score"), maxScore: integer("max_score").default(100), benchmark: integer("benchmark"), responses: jsonb("responses"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Valuation Cap Table (shareholder structure) export const valuationCapTable = pgTable("valuation_cap_table", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), shareholderName: text("shareholder_name").notNull(), shareholderType: text("shareholder_type"), shareClass: text("share_class").default("common"), sharesOwned: integer("shares_owned"), percentageOwned: numeric("percentage_owned"), investmentAmount: numeric("investment_amount"), liquidationPreference: numeric("liquidation_preference"), vestingSchedule: text("vesting_schedule"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Valuation M&A Transactions export const valuationTransactions = pgTable("valuation_transactions", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), type: text("type").notNull(), phase: text("phase").notNull(), targetCloseDate: timestamp("target_close_date"), actualCloseDate: timestamp("actual_close_date"), dealValue: numeric("deal_value"), status: text("status").default("active"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Valuation Data Room Documents export const valuationDocuments = pgTable("valuation_documents", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), transactionId: integer("transaction_id").references(() => valuationTransactions.id, { onDelete: "set null" }), name: text("name").notNull(), folder: text("folder"), fileUrl: text("file_url"), fileType: text("file_type"), fileSize: integer("file_size"), accessLevel: text("access_level").default("view_only"), watermark: integer("watermark").default(0), uploadedBy: varchar("uploaded_by").references(() => users.id), viewCount: integer("view_count").default(0), downloadCount: integer("download_count").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Valuation Document Access Log export const valuationDocumentLogs = pgTable("valuation_document_logs", { id: serial("id").primaryKey(), documentId: integer("document_id").notNull().references(() => valuationDocuments.id, { onDelete: "cascade" }), userId: varchar("user_id").references(() => users.id), action: text("action").notNull(), ipAddress: text("ip_address"), userAgent: text("user_agent"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Valuation Business Model Canvas export const valuationCanvas = pgTable("valuation_canvas", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), block: text("block").notNull(), content: text("content"), orderIndex: integer("order_index").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Valuation Agent Insights (AI suggestions) export const valuationAgentInsights = pgTable("valuation_agent_insights", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), type: text("type").notNull(), title: text("title").notNull(), content: text("content").notNull(), confidence: numeric("confidence"), source: text("source"), status: text("status").default("pending"), appliedAt: timestamp("applied_at"), appliedBy: varchar("applied_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== VALUATION GOVERNANCE ========== export const valuationGovernance = pgTable("valuation_governance", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), criterionCode: text("criterion_code").notNull(), criterionName: text("criterion_name").notNull(), category: text("category").notNull(), currentScore: integer("current_score").default(0), targetScore: integer("target_score").default(10), weight: numeric("weight"), valuationImpactPct: numeric("valuation_impact_pct"), equityImpactPct: numeric("equity_impact_pct"), roeImpactPct: numeric("roe_impact_pct"), priority: text("priority").default("medium"), implementationQuarter: text("implementation_quarter"), implementationCost: numeric("implementation_cost"), status: text("status").default("not_started"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== VALUATION PDCA ========== export const valuationPdca = pgTable("valuation_pdca", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), governanceCriterionId: integer("governance_criterion_id").references(() => valuationGovernance.id), title: text("title").notNull(), originArea: text("origin_area").notNull(), phase: text("phase").notNull().default("plan"), status: text("status").default("not_started"), priority: text("priority").default("medium"), description: text("description"), expectedResult: text("expected_result"), actualResult: text("actual_result"), improvementScore: integer("improvement_score"), heatMapValue: numeric("heat_map_value"), responsible: text("responsible"), startDate: timestamp("start_date"), endDate: timestamp("end_date"), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== VALUATION SWOT ========== export const valuationSwot = pgTable("valuation_swot", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), quadrant: text("quadrant").notNull(), item: text("item").notNull(), impact: text("impact").default("medium"), valuationRelevance: integer("valuation_relevance").default(0), governanceRelevance: integer("governance_relevance").default(0), linkedPdcaId: integer("linked_pdca_id").references(() => valuationPdca.id), valuationImpactPct: numeric("valuation_impact_pct"), orderIndex: integer("order_index").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== VALUATION RESULTS ========== export const valuationResults = pgTable("valuation_results", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), scenario: text("scenario").notNull(), method: text("method").notNull(), enterpriseValue: numeric("enterprise_value"), equityValue: numeric("equity_value"), terminalValue: numeric("terminal_value"), netDebt: numeric("net_debt"), roe: numeric("roe"), roa: numeric("roa"), weight: numeric("weight"), calculationDetails: jsonb("calculation_details"), calculatedAt: timestamp("calculated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== VALUATION ASSETS ========== export const valuationAssets = pgTable("valuation_assets", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), assetType: text("asset_type").notNull(), name: text("name").notNull(), description: text("description"), bookValue: numeric("book_value"), marketValue: numeric("market_value"), appraisedValue: numeric("appraised_value"), depreciationRate: numeric("depreciation_rate"), acquisitionDate: timestamp("acquisition_date"), status: text("status").default("active"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== VALUATION REPORTS ========== export const valuationReports = pgTable("valuation_reports", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), reportType: text("report_type").notNull(), format: text("format").notNull(), fileUrl: text("file_url"), generatedAt: timestamp("generated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), generatedBy: varchar("generated_by").references(() => users.id), version: integer("version").default(1), isCurrent: integer("is_current").default(1), }); // ========== VALUATION AI LOG ========== export const valuationAiLog = pgTable("valuation_ai_log", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), eventType: text("event_type").notNull(), triggerSource: text("trigger_source").notNull(), inputSummary: text("input_summary"), outputSummary: text("output_summary"), fullResponse: jsonb("full_response"), confidence: numeric("confidence"), tokensUsed: integer("tokens_used"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert Schemas - Valuation (new tables) export const insertValuationGovernanceSchema = createInsertSchema(valuationGovernance).omit({ id: true, createdAt: true }); export const insertValuationPdcaSchema = createInsertSchema(valuationPdca).omit({ id: true, createdAt: true, updatedAt: true }); export const insertValuationSwotSchema = createInsertSchema(valuationSwot).omit({ id: true, createdAt: true }); export const insertValuationResultSchema = createInsertSchema(valuationResults).omit({ id: true, calculatedAt: true }); export const insertValuationAssetSchema = createInsertSchema(valuationAssets).omit({ id: true, createdAt: true }); export const insertValuationReportSchema = createInsertSchema(valuationReports).omit({ id: true, generatedAt: true }); export const insertValuationAiLogSchema = createInsertSchema(valuationAiLog).omit({ id: true, createdAt: true }); // Types - Valuation (new tables) export type ValuationGovernanceEntry = typeof valuationGovernance.$inferSelect; export type InsertValuationGovernance = z.infer; export type ValuationPdcaEntry = typeof valuationPdca.$inferSelect; export type InsertValuationPdca = z.infer; export type ValuationSwotEntry = typeof valuationSwot.$inferSelect; export type InsertValuationSwot = z.infer; export type ValuationResultEntry = typeof valuationResults.$inferSelect; export type InsertValuationResult = z.infer; export type ValuationAssetEntry = typeof valuationAssets.$inferSelect; export type InsertValuationAsset = z.infer; export type ValuationReportEntry = typeof valuationReports.$inferSelect; export type InsertValuationReport = z.infer; export type ValuationAiLogEntry = typeof valuationAiLog.$inferSelect; export type InsertValuationAiLog = z.infer; // Insert Schemas - Valuation (original tables) export const insertValuationProjectSchema = createInsertSchema(valuationProjects).omit({ id: true, createdAt: true, updatedAt: true }); export const insertValuationInputSchema = createInsertSchema(valuationInputs).omit({ id: true, createdAt: true, updatedAt: true }); export const insertValuationAssumptionSchema = createInsertSchema(valuationAssumptions).omit({ id: true, createdAt: true }); export const insertValuationCalculationSchema = createInsertSchema(valuationCalculations).omit({ id: true, calculatedAt: true }); export const insertValuationMaturityScoreSchema = createInsertSchema(valuationMaturityScores).omit({ id: true, createdAt: true }); export const insertValuationCapTableSchema = createInsertSchema(valuationCapTable).omit({ id: true, createdAt: true }); export const insertValuationTransactionSchema = createInsertSchema(valuationTransactions).omit({ id: true, createdAt: true, updatedAt: true }); export const insertValuationDocumentSchema = createInsertSchema(valuationDocuments).omit({ id: true, createdAt: true }); export const insertValuationDocumentLogSchema = createInsertSchema(valuationDocumentLogs).omit({ id: true, createdAt: true }); export const insertValuationCanvasSchema = createInsertSchema(valuationCanvas).omit({ id: true, createdAt: true, updatedAt: true }); export const insertValuationAgentInsightSchema = createInsertSchema(valuationAgentInsights).omit({ id: true, createdAt: true }); // Types - Valuation export type ValuationProject = typeof valuationProjects.$inferSelect; export type InsertValuationProject = z.infer; export type ValuationInput = typeof valuationInputs.$inferSelect; export type InsertValuationInput = z.infer; export type ValuationAssumption = typeof valuationAssumptions.$inferSelect; export type InsertValuationAssumption = z.infer; export type ValuationCalculation = typeof valuationCalculations.$inferSelect; export type InsertValuationCalculation = z.infer; export type ValuationMaturityScore = typeof valuationMaturityScores.$inferSelect; export type InsertValuationMaturityScore = z.infer; export type ValuationCapTableEntry = typeof valuationCapTable.$inferSelect; export type InsertValuationCapTableEntry = z.infer; export type ValuationTransaction = typeof valuationTransactions.$inferSelect; export type InsertValuationTransaction = z.infer; export type ValuationDocument = typeof valuationDocuments.$inferSelect; export type InsertValuationDocument = z.infer; export type ValuationDocumentLog = typeof valuationDocumentLogs.$inferSelect; export type InsertValuationDocumentLog = z.infer; export type ValuationCanvas = typeof valuationCanvas.$inferSelect; export type InsertValuationCanvas = z.infer; export type ValuationAgentInsight = typeof valuationAgentInsights.$inferSelect; export type InsertValuationAgentInsight = z.infer; // ========== VALUATION CHECKLIST ========== // Checklist categories for valuation data collection export const valuationChecklistCategories = pgTable("valuation_checklist_categories", { id: serial("id").primaryKey(), code: text("code").notNull().unique(), name: text("name").notNull(), description: text("description"), orderIndex: integer("order_index").default(0), icon: text("icon"), segmentFilter: text("segment_filter"), // null = all, or comma-separated: "technology,fintech,ecommerce,industry,agro" }); // Checklist items template export const valuationChecklistItems = pgTable("valuation_checklist_items", { id: serial("id").primaryKey(), categoryId: integer("category_id").notNull().references(() => valuationChecklistCategories.id, { onDelete: "cascade" }), code: text("code").notNull().unique(), title: text("title").notNull(), description: text("description"), format: text("format"), // PDF, XLSX, Texto, Formulário Online isRequired: integer("is_required").default(1), orderIndex: integer("order_index").default(0), segmentFilter: text("segment_filter"), // null = all, or specific segments agentPrompt: text("agent_prompt"), // AI prompt to assist this item }); // Project checklist progress export const valuationChecklistProgress = pgTable("valuation_checklist_progress", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), itemId: integer("item_id").notNull().references(() => valuationChecklistItems.id, { onDelete: "cascade" }), status: text("status").default("pending"), // pending, in_progress, completed, not_applicable notes: text("notes"), documentId: integer("document_id").references(() => valuationDocuments.id), dataJson: text("data_json"), // structured data for form items agentAnalysis: text("agent_analysis"), // AI interpretation of submitted data completedAt: timestamp("completed_at"), completedBy: varchar("completed_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Checklist attachments export const valuationChecklistAttachments = pgTable("valuation_checklist_attachments", { id: serial("id").primaryKey(), progressId: integer("progress_id").notNull().references(() => valuationChecklistProgress.id, { onDelete: "cascade" }), filename: text("filename").notNull(), originalName: text("original_name").notNull(), mimeType: text("mime_type").notNull(), size: integer("size").notNull(), storagePath: text("storage_path").notNull(), uploadedBy: varchar("uploaded_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert schemas - Valuation Checklist export const insertValuationChecklistCategorySchema = createInsertSchema(valuationChecklistCategories).omit({ id: true }); export const insertValuationChecklistItemSchema = createInsertSchema(valuationChecklistItems).omit({ id: true }); export const insertValuationChecklistProgressSchema = createInsertSchema(valuationChecklistProgress).omit({ id: true, createdAt: true, updatedAt: true }); export const insertValuationChecklistAttachmentSchema = createInsertSchema(valuationChecklistAttachments).omit({ id: true, createdAt: true }); // Types - Valuation Checklist export type ValuationChecklistCategory = typeof valuationChecklistCategories.$inferSelect; export type InsertValuationChecklistCategory = z.infer; export type ValuationChecklistItem = typeof valuationChecklistItems.$inferSelect; export type InsertValuationChecklistItem = z.infer; export type ValuationChecklistProgress = typeof valuationChecklistProgress.$inferSelect; export type InsertValuationChecklistProgress = z.infer; export type ValuationChecklistAttachment = typeof valuationChecklistAttachments.$inferSelect; export type InsertValuationChecklistAttachment = z.infer; // ========== SECTOR ANALYSIS ========== // Category weights for sector analysis export const valuationCategoryWeights = pgTable("valuation_category_weights", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), segment: text("segment").notNull(), // industry, services, retail, technology, etc. categoryCode: text("category_code").notNull(), // general, financial, operational, etc. weight: integer("weight").notNull().default(10), // percentage weight (0-100) description: text("description"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Sector benchmarks for comparison export const valuationSectorBenchmarks = pgTable("valuation_sector_benchmarks", { id: serial("id").primaryKey(), segment: text("segment").notNull(), indicatorCode: text("indicator_code").notNull(), // ebitda_margin, revenue_growth, etc. indicatorName: text("indicator_name").notNull(), minValue: numeric("min_value"), maxValue: numeric("max_value"), avgValue: numeric("avg_value"), topQuartile: numeric("top_quartile"), unit: text("unit"), // %, x, days, etc. source: text("source"), year: integer("year"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Sector analysis results export const valuationSectorScores = pgTable("valuation_sector_scores", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), overallScore: integer("overall_score"), // 0-100 categoryScores: jsonb("category_scores"), // {categoryCode: score} indicatorScores: jsonb("indicator_scores"), // detailed indicator results strengths: text("strengths").array(), weaknesses: text("weaknesses").array(), recommendations: text("recommendations").array(), analysisNotes: text("analysis_notes"), calculatedBy: varchar("calculated_by").references(() => users.id), calculatedAt: timestamp("calculated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), version: integer("version").default(1), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert schemas - Sector Analysis export const insertValuationCategoryWeightSchema = createInsertSchema(valuationCategoryWeights).omit({ id: true, createdAt: true, updatedAt: true }); export const insertValuationSectorBenchmarkSchema = createInsertSchema(valuationSectorBenchmarks).omit({ id: true, createdAt: true }); export const insertValuationSectorScoreSchema = createInsertSchema(valuationSectorScores).omit({ id: true, createdAt: true }); // Types - Sector Analysis export type ValuationCategoryWeight = typeof valuationCategoryWeights.$inferSelect; export type InsertValuationCategoryWeight = z.infer; export type ValuationSectorBenchmark = typeof valuationSectorBenchmarks.$inferSelect; export type InsertValuationSectorBenchmark = z.infer; export type ValuationSectorScore = typeof valuationSectorScores.$inferSelect; export type InsertValuationSectorScore = z.infer; // ========== BUSINESS MODEL CANVAS ========== // Business Model Canvas blocks (expanded structure) export const valuationCanvasBlocks = pgTable("valuation_canvas_blocks", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), blockType: text("block_type").notNull(), // value_proposition, customer_segments, channels, etc. title: text("title"), items: text("items").array(), // list of items in this block notes: text("notes"), orderIndex: integer("order_index").default(0), metadata: jsonb("metadata"), // additional data like colors, icons createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Canvas snapshots for versioning export const valuationCanvasSnapshots = pgTable("valuation_canvas_snapshots", { id: serial("id").primaryKey(), projectId: integer("project_id").notNull().references(() => valuationProjects.id, { onDelete: "cascade" }), name: text("name"), canvasData: jsonb("canvas_data").notNull(), // full canvas state consistencyScore: integer("consistency_score"), // 0-100 consistencyNotes: text("consistency_notes").array(), createdBy: varchar("created_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert schemas - Canvas export const insertValuationCanvasBlockSchema = createInsertSchema(valuationCanvasBlocks).omit({ id: true, createdAt: true, updatedAt: true }); export const insertValuationCanvasSnapshotSchema = createInsertSchema(valuationCanvasSnapshots).omit({ id: true, createdAt: true }); // Types - Canvas export type ValuationCanvasBlock = typeof valuationCanvasBlocks.$inferSelect; export type InsertValuationCanvasBlock = z.infer; export type ValuationCanvasSnapshot = typeof valuationCanvasSnapshots.$inferSelect; export type InsertValuationCanvasSnapshot = z.infer; // ========== GRAFO DE CONHECIMENTO ========== // Nodes do grafo de conhecimento export const graphNodes = pgTable("graph_nodes", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), type: varchar("type", { length: 256 }).notNull(), // contact, message, project, email, document, etc. externalId: varchar("external_id", { length: 512 }), // ID do sistema externo data: jsonb("data").notNull(), // dados do nó embedding: text("embedding"), // embedding vector serializado (opcional, ChromaDB usa externamente) createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Edges (relacionamentos) do grafo export const graphEdges = pgTable("graph_edges", { id: serial("id").primaryKey(), sourceId: integer("source_id").references(() => graphNodes.id, { onDelete: "cascade" }).notNull(), targetId: integer("target_id").references(() => graphNodes.id, { onDelete: "cascade" }).notNull(), relation: varchar("relation", { length: 256 }).notNull(), // has_message, mentions, belongs_to, sent_by, etc. weight: numeric("weight").default("1.0"), // força do relacionamento metadata: jsonb("metadata"), // dados adicionais do relacionamento createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert schemas - Grafo export const insertGraphNodeSchema = createInsertSchema(graphNodes).omit({ id: true, createdAt: true, updatedAt: true }); export const insertGraphEdgeSchema = createInsertSchema(graphEdges).omit({ id: true, createdAt: true }); // Types - Grafo export type GraphNode = typeof graphNodes.$inferSelect; export type InsertGraphNode = z.infer; export type GraphEdge = typeof graphEdges.$inferSelect; export type InsertGraphEdge = z.infer; // ========== EVENTOS DE CONHECIMENTO (COLLECTOR) ========== // Eventos brutos coletados pelo KnowledgeCollector export const learningEvents = pgTable("learning_events", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), userId: varchar("user_id").references(() => users.id), sessionId: varchar("session_id", { length: 256 }), eventType: varchar("event_type", { length: 50 }).notNull(), module: varchar("module", { length: 100 }).notNull(), data: jsonb("data"), url: text("url"), timeSpent: integer("time_spent"), isProcessed: integer("is_processed").default(0), processedAt: timestamp("processed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertLearningEventSchema = createInsertSchema(learningEvents).omit({ id: true, createdAt: true }); export type LearningEvent = typeof learningEvents.$inferSelect; export type InsertLearningEvent = z.infer; // ========== REPOSITÓRIO DE APRENDIZADO (INTERAÇÕES) ========== // Armazena todas as interações do chat e agente para aprendizado export const learnedInteractions = pgTable("learned_interactions", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), userId: varchar("user_id").references(() => users.id), source: varchar("source", { length: 50 }).notNull(), // 'agent_chat', 'manus_agent', 'support_chat', 'whatsapp' sessionId: varchar("session_id", { length: 256 }), // para agrupar conversas question: text("question").notNull(), answer: text("answer").notNull(), context: jsonb("context"), // contexto adicional (ferramentas usadas, dados consultados) toolsUsed: text("tools_used").array(), // ferramentas usadas para responder dataSourcesAccessed: text("data_sources_accessed").array(), // fontes de dados consultadas confidence: numeric("confidence", { precision: 5, scale: 2 }), // confiança da resposta (0-100) feedback: varchar("feedback", { length: 20 }), // 'positive', 'negative', 'neutral' category: varchar("category", { length: 100 }), // categoria inferida da pergunta tags: text("tags").array(), // tags para busca isIndexed: integer("is_indexed").default(0), // se já foi indexado no ChromaDB createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Padrões aprendidos pelo Cientista export const learnedPatterns = pgTable("learned_patterns", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 256 }).notNull(), description: text("description"), patternType: varchar("pattern_type", { length: 50 }).notNull(), // 'correlation', 'trend', 'anomaly', 'rule', 'template' sourceDataset: varchar("source_dataset", { length: 256 }), // dataset que originou o padrão sourceTable: varchar("source_table", { length: 256 }), // tabela que originou pattern: jsonb("pattern").notNull(), // definição do padrão confidence: numeric("confidence", { precision: 5, scale: 2 }), // confiança (0-100) usageCount: integer("usage_count").default(0), // quantas vezes foi aplicado lastUsedAt: timestamp("last_used_at"), isActive: integer("is_active").default(1), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Código gerado pelo Cientista export const generatedCode = pgTable("generated_code", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 256 }).notNull(), description: text("description"), language: varchar("language", { length: 20 }).notNull().default("python"), // 'python', 'sql', 'javascript' codeType: varchar("code_type", { length: 50 }).notNull(), // 'analysis', 'automation', 'transformation', 'report' code: text("code").notNull(), parameters: jsonb("parameters"), // parâmetros esperados generatedFrom: varchar("generated_from", { length: 256 }), // prompt ou dataset que originou usageCount: integer("usage_count").default(0), lastUsedAt: timestamp("last_used_at"), isActive: integer("is_active").default(1), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert schemas - Aprendizado export const insertLearnedInteractionSchema = createInsertSchema(learnedInteractions).omit({ id: true, createdAt: true }); export const insertLearnedPatternSchema = createInsertSchema(learnedPatterns).omit({ id: true, createdAt: true, updatedAt: true }); export const insertGeneratedCodeSchema = createInsertSchema(generatedCode).omit({ id: true, createdAt: true }); // Types - Aprendizado export type LearnedInteraction = typeof learnedInteractions.$inferSelect; export type InsertLearnedInteraction = z.infer; export type LearnedPattern = typeof learnedPatterns.$inferSelect; export type InsertLearnedPattern = z.infer; export type GeneratedCode = typeof generatedCode.$inferSelect; export type InsertGeneratedCode = z.infer; // ========================================== // EMAIL MODULE - Unified Email Communication // ========================================== export const emailAccounts = pgTable("email_accounts", { id: serial("id").primaryKey(), userId: varchar("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), tenantId: integer("tenant_id").references(() => tenants.id), email: varchar("email", { length: 256 }).notNull(), password: text("password"), displayName: varchar("display_name", { length: 256 }), provider: varchar("provider", { length: 50 }).default("gmail"), imapHost: varchar("imap_host", { length: 256 }), imapPort: integer("imap_port").default(993), smtpHost: varchar("smtp_host", { length: 256 }), smtpPort: integer("smtp_port").default(587), status: varchar("status", { length: 50 }).default("disconnected"), lastSyncAt: timestamp("last_sync_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const emailFolders = pgTable("email_folders", { id: serial("id").primaryKey(), accountId: integer("account_id").notNull().references(() => emailAccounts.id, { onDelete: "cascade" }), name: varchar("name", { length: 256 }).notNull(), type: varchar("type", { length: 50 }).default("custom"), unreadCount: integer("unread_count").default(0), totalCount: integer("total_count").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const emailMessages = pgTable("email_messages", { id: serial("id").primaryKey(), accountId: integer("account_id").notNull().references(() => emailAccounts.id, { onDelete: "cascade" }), folderId: integer("folder_id").references(() => emailFolders.id), messageId: varchar("message_id", { length: 512 }), threadId: varchar("thread_id", { length: 512 }), fromAddress: varchar("from_address", { length: 256 }).notNull(), fromName: varchar("from_name", { length: 256 }), toAddresses: text("to_addresses").array(), ccAddresses: text("cc_addresses").array(), bccAddresses: text("bcc_addresses").array(), subject: text("subject"), bodyText: text("body_text"), bodyHtml: text("body_html"), snippet: text("snippet"), isRead: integer("is_read").default(0), isStarred: integer("is_starred").default(0), hasAttachments: integer("has_attachments").default(0), labels: text("labels").array(), replyToId: integer("reply_to_id"), receivedAt: timestamp("received_at"), sentAt: timestamp("sent_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const emailAttachments = pgTable("email_attachments", { id: serial("id").primaryKey(), messageId: integer("message_id").notNull().references(() => emailMessages.id, { onDelete: "cascade" }), filename: varchar("filename", { length: 512 }).notNull(), mimeType: varchar("mime_type", { length: 256 }), size: integer("size"), contentId: varchar("content_id", { length: 256 }), storagePath: text("storage_path"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert Schemas - Email export const insertEmailAccountSchema = createInsertSchema(emailAccounts).omit({ id: true, createdAt: true, updatedAt: true }); export const insertEmailFolderSchema = createInsertSchema(emailFolders).omit({ id: true, createdAt: true }); export const insertEmailMessageSchema = createInsertSchema(emailMessages).omit({ id: true, createdAt: true, updatedAt: true }); // Types - Email export type EmailAccount = typeof emailAccounts.$inferSelect; export type InsertEmailAccount = z.infer; export type EmailFolder = typeof emailFolders.$inferSelect; export type InsertEmailFolder = z.infer; export type EmailMessage = typeof emailMessages.$inferSelect; export type InsertEmailMessage = z.infer; // ========== ARCÁDIA ERP - DocTypes Nativos ========== // Meta-tabela que define a estrutura dos DocTypes export const doctypes = pgTable("doctypes", { id: serial("id").primaryKey(), name: varchar("name", { length: 100 }).notNull().unique(), module: varchar("module", { length: 100 }).default("Core"), label: varchar("label", { length: 200 }).notNull(), description: text("description"), isSingle: integer("is_single").default(0), // 0 = list, 1 = single record isSubmittable: integer("is_submittable").default(0), // 0 = no, 1 = yes (has workflow) isChild: integer("is_child").default(0), // 0 = standalone, 1 = child table parentDoctype: varchar("parent_doctype", { length: 100 }), icon: varchar("icon", { length: 50 }), color: varchar("color", { length: 20 }), trackChanges: integer("track_changes").default(1), permissions: jsonb("permissions").$type>(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Campos de cada DocType export const doctypeFields = pgTable("doctype_fields", { id: serial("id").primaryKey(), doctypeId: integer("doctype_id").notNull().references(() => doctypes.id, { onDelete: "cascade" }), fieldname: varchar("fieldname", { length: 100 }).notNull(), label: varchar("label", { length: 200 }).notNull(), fieldtype: varchar("fieldtype", { length: 50 }).notNull(), // Data, Int, Currency, Link, Select, Table, Text, etc options: text("options"), // For Link: target doctype, For Select: options list defaultValue: text("default_value"), description: text("description"), reqd: integer("reqd").default(0), // required unique: integer("unique").default(0), inListView: integer("in_list_view").default(0), inStandardFilter: integer("in_standard_filter").default(0), hidden: integer("hidden").default(0), readOnly: integer("read_only").default(0), idx: integer("idx").default(0), // order createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Registros dinâmicos (dados reais de cada DocType) export const doctypeRecords = pgTable("doctype_records", { id: serial("id").primaryKey(), doctypeName: varchar("doctype_name", { length: 100 }).notNull(), name: varchar("name", { length: 256 }).notNull(), // unique identifier within doctype tenantId: integer("tenant_id").references(() => tenants.id), ownerId: varchar("owner_id").references(() => users.id), data: jsonb("data").$type>().notNull(), docstatus: integer("docstatus").default(0), // 0=Draft, 1=Submitted, 2=Cancelled createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== Configuração de Segmentos ERP ========== // Segmentos de Negócio export const erpSegments = pgTable("erp_segments", { id: serial("id").primaryKey(), code: varchar("code", { length: 50 }).notNull().unique(), name: varchar("name", { length: 100 }).notNull(), category: varchar("category", { length: 50 }).notNull(), // comercial, industria, distribuidor, ecommerce, foodservice description: text("description"), modules: text("modules").array(), // módulos habilitados para este segmento features: jsonb("features").$type>(), // funcionalidades específicas isActive: integer("is_active").default(1), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Configuração do Tenant (empresa) export const erpConfig = pgTable("erp_config", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id).unique(), segmentId: integer("segment_id").references(() => erpSegments.id), companyName: varchar("company_name", { length: 256 }), tradeName: varchar("trade_name", { length: 256 }), taxId: varchar("tax_id", { length: 20 }), // CNPJ stateRegistration: varchar("state_registration", { length: 50 }), // Inscrição Estadual cityRegistration: varchar("city_registration", { length: 50 }), // Inscrição Municipal taxRegime: varchar("tax_regime", { length: 50 }), // simples, lucro_presumido, lucro_real address: text("address"), city: varchar("city", { length: 100 }), state: varchar("state", { length: 2 }), zipCode: varchar("zip_code", { length: 10 }), phone: varchar("phone", { length: 20 }), email: varchar("email", { length: 256 }), website: varchar("website", { length: 256 }), logoUrl: text("logo_url"), // Integrações erpnextUrl: varchar("erpnext_url", { length: 512 }), erpnextEnabled: integer("erpnext_enabled").default(0), // Módulos ativos modulesCrm: integer("modules_crm").default(1), modulesSales: integer("modules_sales").default(1), modulesPurchases: integer("modules_purchases").default(1), modulesStock: integer("modules_stock").default(1), modulesFinance: integer("modules_finance").default(1), modulesAccounting: integer("modules_accounting").default(0), modulesProduction: integer("modules_production").default(0), modulesProjects: integer("modules_projects").default(0), modulesHr: integer("modules_hr").default(0), modulesServiceOrder: integer("modules_service_order").default(0), // Parametrizações defaultCurrency: varchar("default_currency", { length: 10 }).default("BRL"), decimalPlaces: integer("decimal_places").default(2), fiscalDocumentSeries: varchar("fiscal_document_series", { length: 10 }).default("1"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertErpSegmentSchema = createInsertSchema(erpSegments).omit({ id: true, createdAt: true }); export type ErpSegment = typeof erpSegments.$inferSelect; export type InsertErpSegment = z.infer; export const insertErpConfigSchema = createInsertSchema(erpConfig).omit({ id: true, createdAt: true, updatedAt: true }); export type ErpConfig = typeof erpConfig.$inferSelect; export type InsertErpConfig = z.infer; // ========== DocTypes de Negócio Pré-definidos ========== // Clientes export const customers = pgTable("customers", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), code: varchar("code", { length: 50 }).notNull(), name: varchar("name", { length: 256 }).notNull(), type: varchar("type", { length: 50 }).default("company"), // company, individual taxId: varchar("tax_id", { length: 50 }), // CPF/CNPJ email: varchar("email", { length: 256 }), phone: varchar("phone", { length: 50 }), address: text("address"), city: varchar("city", { length: 100 }), state: varchar("state", { length: 50 }), country: varchar("country", { length: 100 }).default("Brasil"), creditLimit: numeric("credit_limit", { precision: 15, scale: 2 }).default("0"), paymentTerms: integer("payment_terms").default(30), // days status: varchar("status", { length: 50 }).default("active"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Fornecedores export const suppliers = pgTable("suppliers", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), code: varchar("code", { length: 50 }).notNull(), name: varchar("name", { length: 256 }).notNull(), taxId: varchar("tax_id", { length: 50 }), email: varchar("email", { length: 256 }), phone: varchar("phone", { length: 50 }), address: text("address"), city: varchar("city", { length: 100 }), state: varchar("state", { length: 50 }), country: varchar("country", { length: 100 }).default("Brasil"), paymentTerms: integer("payment_terms").default(30), status: varchar("status", { length: 50 }).default("active"), notes: text("notes"), // Campos de Homologação ISO 17025 isHomologated: integer("is_homologated").default(0), homologationDate: timestamp("homologation_date"), homologationExpiry: timestamp("homologation_expiry"), homologationStatus: varchar("homologation_status", { length: 50 }), // pending, approved, expired, blocked certifications: text("certifications").array(), // ISO 17025, ISO 9001, etc. qualityScore: integer("quality_score"), // 0-100 lastAuditDate: timestamp("last_audit_date"), nextAuditDate: timestamp("next_audit_date"), blockedForPurchase: integer("blocked_for_purchase").default(0), blockReason: text("block_reason"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Produtos export const products = pgTable("products", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), code: varchar("code", { length: 50 }).notNull(), name: varchar("name", { length: 256 }).notNull(), description: text("description"), category: varchar("category", { length: 100 }), unit: varchar("unit", { length: 20 }).default("UN"), costPrice: numeric("cost_price", { precision: 15, scale: 2 }).default("0"), salePrice: numeric("sale_price", { precision: 15, scale: 2 }).default("0"), stockQty: numeric("stock_qty", { precision: 15, scale: 3 }).default("0"), minStock: numeric("min_stock", { precision: 15, scale: 3 }).default("0"), barcode: varchar("barcode", { length: 50 }), ncm: varchar("ncm", { length: 20 }), // Brazilian tax code taxGroupId: integer("tax_group_id").references(() => fiscalGruposTributacao.id), // Grupo tributário status: varchar("status", { length: 50 }).default("active"), imageUrl: text("image_url"), requiresSerialTracking: boolean("requires_serial_tracking").default(false), trackingType: varchar("tracking_type", { length: 20 }).default("none"), // none, imei, serial defaultBrand: varchar("default_brand", { length: 50 }), defaultModel: varchar("default_model", { length: 100 }), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Pedidos de Venda export const salesOrders = pgTable("sales_orders", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), orderNumber: varchar("order_number", { length: 50 }).notNull(), customerId: integer("customer_id").references(() => customers.id), orderDate: timestamp("order_date").default(sql`CURRENT_TIMESTAMP`).notNull(), deliveryDate: timestamp("delivery_date"), status: varchar("status", { length: 50 }).default("draft"), // draft, confirmed, delivered, cancelled subtotal: numeric("subtotal", { precision: 15, scale: 2 }).default("0"), discount: numeric("discount", { precision: 15, scale: 2 }).default("0"), tax: numeric("tax", { precision: 15, scale: 2 }).default("0"), total: numeric("total", { precision: 15, scale: 2 }).default("0"), paymentMethod: varchar("payment_method", { length: 50 }), notes: text("notes"), createdBy: varchar("created_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Itens do Pedido export const salesOrderItems = pgTable("sales_order_items", { id: serial("id").primaryKey(), orderId: integer("order_id").notNull().references(() => salesOrders.id, { onDelete: "cascade" }), productId: integer("product_id").references(() => products.id), productName: varchar("product_name", { length: 256 }).notNull(), quantity: numeric("quantity", { precision: 15, scale: 3 }).notNull(), unitPrice: numeric("unit_price", { precision: 15, scale: 2 }).notNull(), discount: numeric("discount", { precision: 15, scale: 2 }).default("0"), total: numeric("total", { precision: 15, scale: 2 }).notNull(), }); // Pedidos de Compra export const purchaseOrders = pgTable("purchase_orders", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), orderNumber: varchar("order_number", { length: 50 }).notNull(), supplierId: integer("supplier_id").references(() => suppliers.id), orderDate: timestamp("order_date").default(sql`CURRENT_TIMESTAMP`).notNull(), expectedDate: timestamp("expected_date"), status: varchar("status", { length: 50 }).default("draft"), subtotal: numeric("subtotal", { precision: 15, scale: 2 }).default("0"), discount: numeric("discount", { precision: 15, scale: 2 }).default("0"), tax: numeric("tax", { precision: 15, scale: 2 }).default("0"), total: numeric("total", { precision: 15, scale: 2 }).default("0"), notes: text("notes"), createdBy: varchar("created_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Itens do Pedido de Compra export const purchaseOrderItems = pgTable("purchase_order_items", { id: serial("id").primaryKey(), orderId: integer("order_id").notNull().references(() => purchaseOrders.id, { onDelete: "cascade" }), productId: integer("product_id").references(() => products.id), productName: varchar("product_name", { length: 256 }).notNull(), quantity: numeric("quantity", { precision: 15, scale: 3 }).notNull(), unitPrice: numeric("unit_price", { precision: 15, scale: 2 }).notNull(), total: numeric("total", { precision: 15, scale: 2 }).notNull(), }); // ========== Insert Schemas - ERP ========== export const insertDoctypeSchema = createInsertSchema(doctypes).omit({ id: true, createdAt: true, updatedAt: true }); export const insertDoctypeFieldSchema = createInsertSchema(doctypeFields).omit({ id: true, createdAt: true }); export const insertDoctypeRecordSchema = createInsertSchema(doctypeRecords).omit({ id: true, createdAt: true, updatedAt: true }); export const insertCustomerSchema = createInsertSchema(customers).omit({ id: true, createdAt: true, updatedAt: true }); export const insertSupplierSchema = createInsertSchema(suppliers).omit({ id: true, createdAt: true, updatedAt: true }); export const insertProductSchema = createInsertSchema(products).omit({ id: true, createdAt: true, updatedAt: true }); export const insertSalesOrderSchema = createInsertSchema(salesOrders).omit({ id: true, createdAt: true, updatedAt: true }); export const insertSalesOrderItemSchema = createInsertSchema(salesOrderItems).omit({ id: true }); export const insertPurchaseOrderSchema = createInsertSchema(purchaseOrders).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPurchaseOrderItemSchema = createInsertSchema(purchaseOrderItems).omit({ id: true }); // ========== Types - ERP ========== export type Doctype = typeof doctypes.$inferSelect; export type InsertDoctype = z.infer; export type DoctypeField = typeof doctypeFields.$inferSelect; export type InsertDoctypeField = z.infer; export type DoctypeRecord = typeof doctypeRecords.$inferSelect; export type InsertDoctypeRecord = z.infer; export type Customer = typeof customers.$inferSelect; export type InsertCustomer = z.infer; export type Supplier = typeof suppliers.$inferSelect; export type InsertSupplier = z.infer; export type Product = typeof products.$inferSelect; export type InsertProduct = z.infer; export type SalesOrder = typeof salesOrders.$inferSelect; export type InsertSalesOrder = z.infer; export type SalesOrderItem = typeof salesOrderItems.$inferSelect; export type InsertSalesOrderItem = z.infer; export type PurchaseOrder = typeof purchaseOrders.$inferSelect; export type InsertPurchaseOrder = z.infer; export type PurchaseOrderItem = typeof purchaseOrderItems.$inferSelect; export type InsertPurchaseOrderItem = z.infer; // ========== ARCÁDIA FISCO - Motor Fiscal ========== // Tabela NCM (Nomenclatura Comum do Mercosul) export const fiscalNcms = pgTable("fiscal_ncms", { id: serial("id").primaryKey(), codigo: varchar("codigo", { length: 10 }).notNull().unique(), descricao: text("descricao").notNull(), aliqIpi: numeric("aliq_ipi", { precision: 5, scale: 2 }).default("0"), aliqImport: numeric("aliq_import", { precision: 5, scale: 2 }).default("0"), unidadeTributavel: varchar("unidade_tributavel", { length: 10 }), exTipi: varchar("ex_tipi", { length: 3 }), status: varchar("status", { length: 20 }).default("active"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Tabela CEST (Código Especificador da Substituição Tributária) export const fiscalCests = pgTable("fiscal_cests", { id: serial("id").primaryKey(), codigo: varchar("codigo", { length: 9 }).notNull().unique(), descricao: text("descricao").notNull(), ncmInicio: varchar("ncm_inicio", { length: 10 }), ncmFim: varchar("ncm_fim", { length: 10 }), segmento: varchar("segmento", { length: 100 }), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Tabela CFOP (Código Fiscal de Operações e Prestações) export const fiscalCfops = pgTable("fiscal_cfops", { id: serial("id").primaryKey(), codigo: varchar("codigo", { length: 4 }).notNull().unique(), descricao: text("descricao").notNull(), tipo: varchar("tipo", { length: 20 }).notNull(), // entrada, saida natureza: varchar("natureza", { length: 50 }), // venda, compra, devolucao, transferencia, etc geraCredito: integer("gera_credito").default(0), geraDebito: integer("gera_debito").default(0), movimentaEstoque: integer("movimenta_estoque").default(1), aplicacao: text("aplicacao"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Grupos de Tributação (padrão de impostos vinculado a NCM) export const fiscalGruposTributacao = pgTable("fiscal_grupos_tributacao", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), nome: varchar("nome", { length: 100 }).notNull(), descricao: text("descricao"), ncm: varchar("ncm", { length: 10 }), cest: varchar("cest", { length: 9 }), // ICMS cstCsosn: varchar("cst_csosn", { length: 5 }), percIcms: numeric("perc_icms", { precision: 5, scale: 2 }).default("0"), percRedBc: numeric("perc_red_bc", { precision: 5, scale: 2 }).default("0"), modBcSt: varchar("mod_bc_st", { length: 5 }), percMvaSt: numeric("perc_mva_st", { precision: 5, scale: 2 }).default("0"), percIcmsSt: numeric("perc_icms_st", { precision: 5, scale: 2 }).default("0"), percRedBcSt: numeric("perc_red_bc_st", { precision: 5, scale: 2 }).default("0"), // PIS/COFINS cstPis: varchar("cst_pis", { length: 3 }), percPis: numeric("perc_pis", { precision: 5, scale: 2 }).default("0"), cstCofins: varchar("cst_cofins", { length: 3 }), percCofins: numeric("perc_cofins", { precision: 5, scale: 2 }).default("0"), // IPI cstIpi: varchar("cst_ipi", { length: 3 }), percIpi: numeric("perc_ipi", { precision: 5, scale: 2 }).default("0"), cEnq: varchar("c_enq", { length: 3 }), // CFOPs cfopEstadual: varchar("cfop_estadual", { length: 4 }), cfopOutroEstado: varchar("cfop_outro_estado", { length: 4 }), cfopEntradaEstadual: varchar("cfop_entrada_estadual", { length: 4 }), cfopEntradaOutroEstado: varchar("cfop_entrada_outro_estado", { length: 4 }), // Benefício Fiscal codigoBeneficioFiscal: varchar("codigo_beneficio_fiscal", { length: 20 }), // Reforma Tributária (IBS/CBS) - Lei Complementar 214/2025 cstIbsCbs: varchar("cst_ibs_cbs", { length: 5 }), classeTribIbsCbs: varchar("classe_trib_ibs_cbs", { length: 10 }), percIbsUf: numeric("perc_ibs_uf", { precision: 5, scale: 2 }).default("0"), percIbsMun: numeric("perc_ibs_mun", { precision: 5, scale: 2 }).default("0"), percCbs: numeric("perc_cbs", { precision: 5, scale: 2 }).default("0"), percDif: numeric("perc_dif", { precision: 5, scale: 2 }).default("0"), padrao: integer("padrao").default(0), status: varchar("status", { length: 20 }).default("active"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Natureza de Operação export const fiscalNaturezaOperacao = pgTable("fiscal_natureza_operacao", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), codigo: varchar("codigo", { length: 20 }), descricao: varchar("descricao", { length: 200 }).notNull(), // CSTs cstCsosn: varchar("cst_csosn", { length: 5 }), cstPis: varchar("cst_pis", { length: 3 }), cstCofins: varchar("cst_cofins", { length: 3 }), cstIpi: varchar("cst_ipi", { length: 3 }), // CFOPs cfopEstadual: varchar("cfop_estadual", { length: 4 }), cfopOutroEstado: varchar("cfop_outro_estado", { length: 4 }), cfopEntradaEstadual: varchar("cfop_entrada_estadual", { length: 4 }), cfopEntradaOutroEstado: varchar("cfop_entrada_outro_estado", { length: 4 }), // Alíquotas percIcms: numeric("perc_icms", { precision: 5, scale: 2 }).default("0"), percPis: numeric("perc_pis", { precision: 5, scale: 2 }).default("0"), percCofins: numeric("perc_cofins", { precision: 5, scale: 2 }).default("0"), percIpi: numeric("perc_ipi", { precision: 5, scale: 2 }).default("0"), sobrescreverCfop: integer("sobrescrever_cfop").default(0), movimentarEstoque: integer("movimentar_estoque").default(1), padrao: integer("padrao").default(0), status: varchar("status", { length: 20 }).default("active"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Certificados Digitais export const fiscalCertificados = pgTable("fiscal_certificados", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), nome: varchar("nome", { length: 100 }).notNull(), tipo: varchar("tipo", { length: 5 }).notNull(), // A1, A3 cnpj: varchar("cnpj", { length: 20 }).notNull(), razaoSocial: varchar("razao_social", { length: 200 }), serialNumber: varchar("serial_number", { length: 100 }), validoAte: timestamp("valido_ate"), arquivo: text("arquivo"), // path ou base64 criptografado senha: text("senha"), // criptografada ambiente: varchar("ambiente", { length: 20 }).default("homologacao"), // homologacao, producao status: varchar("status", { length: 20 }).default("active"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Configurações Fiscais por Tenant export const fiscalConfiguracoes = pgTable("fiscal_configuracoes", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id).unique(), // Emitente cnpj: varchar("cnpj", { length: 20 }), ie: varchar("ie", { length: 20 }), im: varchar("im", { length: 20 }), cnae: varchar("cnae", { length: 10 }), crt: varchar("crt", { length: 2 }), // 1-Simples, 2-Simples Excesso, 3-Normal // Série NF-e/NFC-e serieNfe: integer("serie_nfe").default(1), serieNfce: integer("serie_nfce").default(1), proximoNumNfe: integer("proximo_num_nfe").default(1), proximoNumNfce: integer("proximo_num_nfce").default(1), // NFC-e cscId: varchar("csc_id", { length: 10 }), cscToken: varchar("csc_token", { length: 50 }), // Ambiente ambiente: varchar("ambiente", { length: 20 }).default("homologacao"), certificadoId: integer("certificado_id").references(() => fiscalCertificados.id), // Horários de funcionamento (para auto-resposta fora do horário) horarioInicio: varchar("horario_inicio", { length: 5 }), horarioFim: varchar("horario_fim", { length: 5 }), // Configurações gerais enviarEmailAutomatico: integer("enviar_email_automatico").default(0), imprimirDanfeAutomatico: integer("imprimir_danfe_automatico").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Notas Fiscais (NF-e / NFC-e) export const fiscalNotas = pgTable("fiscal_notas", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), // Identificação modelo: varchar("modelo", { length: 3 }).notNull(), // 55 = NF-e, 65 = NFC-e serie: integer("serie").notNull(), numero: integer("numero").notNull(), chave: varchar("chave", { length: 44 }), naturezaOperacao: varchar("natureza_operacao", { length: 100 }), // Datas dataEmissao: timestamp("data_emissao").notNull(), dataSaida: timestamp("data_saida"), // Tipo tipoOperacao: varchar("tipo_operacao", { length: 5 }), // 0=Entrada, 1=Saída tipoEmissao: varchar("tipo_emissao", { length: 5 }).default("1"), // 1=Normal finalidade: varchar("finalidade", { length: 5 }).default("1"), // 1=Normal, 2=Complementar, 3=Ajuste, 4=Devolução consumidorFinal: varchar("consumidor_final", { length: 5 }).default("0"), presencaComprador: varchar("presenca_comprador", { length: 5 }).default("1"), // Destinatário destinatarioId: integer("destinatario_id"), destinatarioTipo: varchar("destinatario_tipo", { length: 20 }), // customer, supplier destinatarioDoc: varchar("destinatario_doc", { length: 20 }), destinatarioNome: varchar("destinatario_nome", { length: 200 }), // Totais valorProdutos: numeric("valor_produtos", { precision: 15, scale: 2 }).default("0"), valorFrete: numeric("valor_frete", { precision: 15, scale: 2 }).default("0"), valorSeguro: numeric("valor_seguro", { precision: 15, scale: 2 }).default("0"), valorDesconto: numeric("valor_desconto", { precision: 15, scale: 2 }).default("0"), valorOutros: numeric("valor_outros", { precision: 15, scale: 2 }).default("0"), valorTotal: numeric("valor_total", { precision: 15, scale: 2 }).default("0"), // Impostos totais valorIcms: numeric("valor_icms", { precision: 15, scale: 2 }).default("0"), valorIcmsSt: numeric("valor_icms_st", { precision: 15, scale: 2 }).default("0"), valorPis: numeric("valor_pis", { precision: 15, scale: 2 }).default("0"), valorCofins: numeric("valor_cofins", { precision: 15, scale: 2 }).default("0"), valorIpi: numeric("valor_ipi", { precision: 15, scale: 2 }).default("0"), // Reforma Tributária valorIbsUf: numeric("valor_ibs_uf", { precision: 15, scale: 2 }).default("0"), valorIbsMun: numeric("valor_ibs_mun", { precision: 15, scale: 2 }).default("0"), valorCbs: numeric("valor_cbs", { precision: 15, scale: 2 }).default("0"), // Status SEFAZ status: varchar("status", { length: 30 }).default("rascunho"), // rascunho, pendente, autorizada, rejeitada, cancelada, inutilizada codigoStatus: varchar("codigo_status", { length: 10 }), motivoStatus: text("motivo_status"), protocolo: varchar("protocolo", { length: 50 }), dataAutorizacao: timestamp("data_autorizacao"), // XMLs xmlEnvio: text("xml_envio"), xmlRetorno: text("xml_retorno"), xmlAutorizado: text("xml_autorizado"), // Pedido origem pedidoOrigemId: integer("pedido_origem_id"), pedidoOrigemTipo: varchar("pedido_origem_tipo", { length: 20 }), // salesOrder, purchaseOrder informacoesAdicionais: text("informacoes_adicionais"), createdBy: varchar("created_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Itens da Nota Fiscal export const fiscalNotaItens = pgTable("fiscal_nota_itens", { id: serial("id").primaryKey(), notaId: integer("nota_id").notNull().references(() => fiscalNotas.id, { onDelete: "cascade" }), // Produto produtoId: integer("produto_id"), codigo: varchar("codigo", { length: 60 }), descricao: varchar("descricao", { length: 256 }).notNull(), ncm: varchar("ncm", { length: 10 }), cest: varchar("cest", { length: 9 }), cfop: varchar("cfop", { length: 4 }), unidade: varchar("unidade", { length: 10 }), // Quantidades e valores quantidade: numeric("quantidade", { precision: 15, scale: 4 }).notNull(), valorUnitario: numeric("valor_unitario", { precision: 15, scale: 4 }).notNull(), valorDesconto: numeric("valor_desconto", { precision: 15, scale: 2 }).default("0"), valorTotal: numeric("valor_total", { precision: 15, scale: 2 }).notNull(), // ICMS origem: varchar("origem", { length: 2 }), cstCsosn: varchar("cst_csosn", { length: 5 }), bcIcms: numeric("bc_icms", { precision: 15, scale: 2 }).default("0"), percIcms: numeric("perc_icms", { precision: 5, scale: 2 }).default("0"), valorIcms: numeric("valor_icms", { precision: 15, scale: 2 }).default("0"), // ST bcIcmsSt: numeric("bc_icms_st", { precision: 15, scale: 2 }).default("0"), percIcmsSt: numeric("perc_icms_st", { precision: 5, scale: 2 }).default("0"), valorIcmsSt: numeric("valor_icms_st", { precision: 15, scale: 2 }).default("0"), // PIS cstPis: varchar("cst_pis", { length: 3 }), bcPis: numeric("bc_pis", { precision: 15, scale: 2 }).default("0"), percPis: numeric("perc_pis", { precision: 5, scale: 2 }).default("0"), valorPis: numeric("valor_pis", { precision: 15, scale: 2 }).default("0"), // COFINS cstCofins: varchar("cst_cofins", { length: 3 }), bcCofins: numeric("bc_cofins", { precision: 15, scale: 2 }).default("0"), percCofins: numeric("perc_cofins", { precision: 5, scale: 2 }).default("0"), valorCofins: numeric("valor_cofins", { precision: 15, scale: 2 }).default("0"), // IPI cstIpi: varchar("cst_ipi", { length: 3 }), bcIpi: numeric("bc_ipi", { precision: 15, scale: 2 }).default("0"), percIpi: numeric("perc_ipi", { precision: 5, scale: 2 }).default("0"), valorIpi: numeric("valor_ipi", { precision: 15, scale: 2 }).default("0"), // Reforma Tributária cstIbsCbs: varchar("cst_ibs_cbs", { length: 5 }), valorIbsUf: numeric("valor_ibs_uf", { precision: 15, scale: 2 }).default("0"), valorIbsMun: numeric("valor_ibs_mun", { precision: 15, scale: 2 }).default("0"), valorCbs: numeric("valor_cbs", { precision: 15, scale: 2 }).default("0"), ordem: integer("ordem").default(1), }); // Eventos Fiscais (cancelamento, carta de correção, etc) export const fiscalEventos = pgTable("fiscal_eventos", { id: serial("id").primaryKey(), notaId: integer("nota_id").notNull().references(() => fiscalNotas.id, { onDelete: "cascade" }), tipoEvento: varchar("tipo_evento", { length: 10 }).notNull(), // 110111=Cancelamento, 110110=CCe sequencia: integer("sequencia").default(1), descricao: text("descricao"), justificativa: text("justificativa"), protocolo: varchar("protocolo", { length: 50 }), dataEvento: timestamp("data_evento"), status: varchar("status", { length: 30 }), xmlEvento: text("xml_evento"), xmlRetorno: text("xml_retorno"), createdBy: varchar("created_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // IBPT - Carga Tributária por NCM export const fiscalIbpt = pgTable("fiscal_ibpt", { id: serial("id").primaryKey(), ncm: varchar("ncm", { length: 10 }).notNull(), exTipi: varchar("ex_tipi", { length: 3 }), tabela: varchar("tabela", { length: 3 }), // 0, 1, 2 aliqNac: numeric("aliq_nac", { precision: 5, scale: 2 }).default("0"), aliqImp: numeric("aliq_imp", { precision: 5, scale: 2 }).default("0"), aliqEst: numeric("aliq_est", { precision: 5, scale: 2 }).default("0"), aliqMun: numeric("aliq_mun", { precision: 5, scale: 2 }).default("0"), vigenciaInicio: timestamp("vigencia_inicio"), vigenciaFim: timestamp("vigencia_fim"), versao: varchar("versao", { length: 20 }), }); // ========== Insert Schemas - Arcádia Fisco ========== export const insertFiscalNcmSchema = createInsertSchema(fiscalNcms).omit({ id: true, createdAt: true, updatedAt: true }); export const insertFiscalCestSchema = createInsertSchema(fiscalCests).omit({ id: true, createdAt: true }); export const insertFiscalCfopSchema = createInsertSchema(fiscalCfops).omit({ id: true, createdAt: true }); export const insertFiscalGrupoTributacaoSchema = createInsertSchema(fiscalGruposTributacao).omit({ id: true, createdAt: true, updatedAt: true }); export const insertFiscalNaturezaOperacaoSchema = createInsertSchema(fiscalNaturezaOperacao).omit({ id: true, createdAt: true, updatedAt: true }); export const insertFiscalCertificadoSchema = createInsertSchema(fiscalCertificados).omit({ id: true, createdAt: true, updatedAt: true }); export const insertFiscalConfiguracaoSchema = createInsertSchema(fiscalConfiguracoes).omit({ id: true, createdAt: true, updatedAt: true }); export const insertFiscalNotaSchema = createInsertSchema(fiscalNotas).omit({ id: true, createdAt: true, updatedAt: true }); export const insertFiscalNotaItemSchema = createInsertSchema(fiscalNotaItens).omit({ id: true }); export const insertFiscalEventoSchema = createInsertSchema(fiscalEventos).omit({ id: true, createdAt: true }); export const insertFiscalIbptSchema = createInsertSchema(fiscalIbpt).omit({ id: true }); // ========== Types - Arcádia Fisco ========== export type FiscalNcm = typeof fiscalNcms.$inferSelect; export type InsertFiscalNcm = z.infer; export type FiscalCest = typeof fiscalCests.$inferSelect; export type InsertFiscalCest = z.infer; export type FiscalCfop = typeof fiscalCfops.$inferSelect; export type InsertFiscalCfop = z.infer; export type FiscalGrupoTributacao = typeof fiscalGruposTributacao.$inferSelect; export type InsertFiscalGrupoTributacao = z.infer; export type FiscalNaturezaOperacao = typeof fiscalNaturezaOperacao.$inferSelect; export type InsertFiscalNaturezaOperacao = z.infer; export type FiscalCertificado = typeof fiscalCertificados.$inferSelect; export type InsertFiscalCertificado = z.infer; export type FiscalConfiguracao = typeof fiscalConfiguracoes.$inferSelect; export type InsertFiscalConfiguracao = z.infer; export type FiscalNota = typeof fiscalNotas.$inferSelect; export type InsertFiscalNota = z.infer; export type FiscalNotaItem = typeof fiscalNotaItens.$inferSelect; export type InsertFiscalNotaItem = z.infer; export type FiscalEvento = typeof fiscalEventos.$inferSelect; export type InsertFiscalEvento = z.infer; export type FiscalIbpt = typeof fiscalIbpt.$inferSelect; export type InsertFiscalIbpt = z.infer; // ========== ARCÁDIA CONTÁBIL - Motor de Contabilidade ========== // Plano de Contas export const contabilPlanoContas = pgTable("contabil_plano_contas", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), codigo: varchar("codigo", { length: 20 }).notNull(), descricao: varchar("descricao", { length: 200 }).notNull(), tipo: varchar("tipo", { length: 20 }).notNull(), // ativo, passivo, patrimonio, receita, despesa natureza: varchar("natureza", { length: 10 }).notNull(), // devedora, credora nivel: integer("nivel").default(1), contaPai: integer("conta_pai"), aceitaLancamento: integer("aceita_lancamento").default(1), codigoReduzido: varchar("codigo_reduzido", { length: 10 }), status: varchar("status", { length: 20 }).default("ativo"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Centros de Custo export const contabilCentrosCusto = pgTable("contabil_centros_custo", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), codigo: varchar("codigo", { length: 20 }).notNull(), descricao: varchar("descricao", { length: 200 }).notNull(), tipo: varchar("tipo", { length: 20 }), // custo, lucro, investimento centroPai: integer("centro_pai"), responsavel: varchar("responsavel"), status: varchar("status", { length: 20 }).default("ativo"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Lançamentos Contábeis (Diário) export const contabilLancamentos = pgTable("contabil_lancamentos", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), numero: varchar("numero", { length: 20 }), dataLancamento: timestamp("data_lancamento").notNull(), dataCompetencia: timestamp("data_competencia"), tipoDocumento: varchar("tipo_documento", { length: 50 }), // nfe, nfse, recibo, folha, manual numeroDocumento: varchar("numero_documento", { length: 50 }), historico: text("historico").notNull(), valor: numeric("valor", { precision: 15, scale: 2 }).notNull(), origem: varchar("origem", { length: 50 }), // fisco, people, manual, erp origemId: integer("origem_id"), status: varchar("status", { length: 20 }).default("pendente"), // pendente, conferido, fechado createdBy: varchar("created_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Partidas do Lançamento (Débito/Crédito) export const contabilPartidas = pgTable("contabil_partidas", { id: serial("id").primaryKey(), lancamentoId: integer("lancamento_id").notNull().references(() => contabilLancamentos.id, { onDelete: "cascade" }), contaId: integer("conta_id").notNull().references(() => contabilPlanoContas.id), centroCustoId: integer("centro_custo_id").references(() => contabilCentrosCusto.id), tipo: varchar("tipo", { length: 10 }).notNull(), // debito, credito valor: numeric("valor", { precision: 15, scale: 2 }).notNull(), historico: text("historico"), }); // Períodos Contábeis export const contabilPeriodos = pgTable("contabil_periodos", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), ano: integer("ano").notNull(), mes: integer("mes").notNull(), dataInicio: timestamp("data_inicio").notNull(), dataFim: timestamp("data_fim").notNull(), status: varchar("status", { length: 20 }).default("aberto"), // aberto, fechado fechadoPor: varchar("fechado_por").references(() => users.id), fechadoEm: timestamp("fechado_em"), }); // Configurações de Lançamento Automático export const contabilConfigLancamento = pgTable("contabil_config_lancamento", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), origem: varchar("origem", { length: 50 }).notNull(), // nfe_entrada, nfe_saida, folha, etc descricao: varchar("descricao", { length: 200 }), contaDebito: integer("conta_debito").references(() => contabilPlanoContas.id), contaCredito: integer("conta_credito").references(() => contabilPlanoContas.id), centroCusto: integer("centro_custo").references(() => contabilCentrosCusto.id), historicoTemplate: text("historico_template"), ativo: integer("ativo").default(1), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Saldos de Contas (cache para performance) export const contabilSaldos = pgTable("contabil_saldos", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), contaId: integer("conta_id").notNull().references(() => contabilPlanoContas.id), centroCustoId: integer("centro_custo_id").references(() => contabilCentrosCusto.id), ano: integer("ano").notNull(), mes: integer("mes").notNull(), saldoAnterior: numeric("saldo_anterior", { precision: 15, scale: 2 }).default("0"), debitos: numeric("debitos", { precision: 15, scale: 2 }).default("0"), creditos: numeric("creditos", { precision: 15, scale: 2 }).default("0"), saldoAtual: numeric("saldo_atual", { precision: 15, scale: 2 }).default("0"), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== ARCÁDIA PEOPLE - Motor de RH ========== // Cargos export const peopleCargos = pgTable("people_cargos", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), codigo: varchar("codigo", { length: 20 }), nome: varchar("nome", { length: 100 }).notNull(), descricao: text("descricao"), cbo: varchar("cbo", { length: 10 }), // Classificação Brasileira de Ocupações nivel: varchar("nivel", { length: 50 }), // junior, pleno, senior, gerente, diretor departamento: varchar("departamento", { length: 100 }), salarioBase: numeric("salario_base", { precision: 15, scale: 2 }), status: varchar("status", { length: 20 }).default("ativo"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Departamentos export const peopleDepartamentos = pgTable("people_departamentos", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), codigo: varchar("codigo", { length: 20 }), nome: varchar("nome", { length: 100 }).notNull(), centroCustoId: integer("centro_custo_id").references(() => contabilCentrosCusto.id), gerente: varchar("gerente"), departamentoPai: integer("departamento_pai"), status: varchar("status", { length: 20 }).default("ativo"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Funcionários export const peopleFuncionarios = pgTable("people_funcionarios", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), userId: varchar("user_id").references(() => users.id), matricula: varchar("matricula", { length: 20 }), nome: varchar("nome", { length: 200 }).notNull(), cpf: varchar("cpf", { length: 14 }), rg: varchar("rg", { length: 20 }), dataNascimento: timestamp("data_nascimento"), sexo: varchar("sexo", { length: 1 }), estadoCivil: varchar("estado_civil", { length: 20 }), nacionalidade: varchar("nacionalidade", { length: 50 }), naturalidade: varchar("naturalidade", { length: 100 }), nomeMae: varchar("nome_mae", { length: 200 }), nomePai: varchar("nome_pai", { length: 200 }), // Contato email: varchar("email", { length: 200 }), telefone: varchar("telefone", { length: 20 }), celular: varchar("celular", { length: 20 }), // Endereço cep: varchar("cep", { length: 10 }), logradouro: varchar("logradouro", { length: 200 }), numero: varchar("numero", { length: 20 }), complemento: varchar("complemento", { length: 100 }), bairro: varchar("bairro", { length: 100 }), cidade: varchar("cidade", { length: 100 }), uf: varchar("uf", { length: 2 }), // Documentos Trabalhistas pis: varchar("pis", { length: 20 }), ctps: varchar("ctps", { length: 20 }), ctpsSerie: varchar("ctps_serie", { length: 10 }), ctpsUf: varchar("ctps_uf", { length: 2 }), // Dados Bancários banco: varchar("banco", { length: 10 }), agencia: varchar("agencia", { length: 10 }), conta: varchar("conta", { length: 20 }), tipoConta: varchar("tipo_conta", { length: 20 }), chavePix: varchar("chave_pix", { length: 100 }), // Vínculo cargoId: integer("cargo_id").references(() => peopleCargos.id), departamentoId: integer("departamento_id").references(() => peopleDepartamentos.id), dataAdmissao: timestamp("data_admissao"), dataDemissao: timestamp("data_demissao"), tipoContrato: varchar("tipo_contrato", { length: 20 }), // clt, pj, estagio, temporario jornadaTrabalho: varchar("jornada_trabalho", { length: 20 }), // 44h, 40h, 36h, 30h salario: numeric("salario", { precision: 15, scale: 2 }), // eSocial matriculaEsocial: varchar("matricula_esocial", { length: 30 }), categoriaEsocial: varchar("categoria_esocial", { length: 5 }), status: varchar("status", { length: 20 }).default("ativo"), foto: text("foto"), observacoes: text("observacoes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Dependentes export const peopleDependentes = pgTable("people_dependentes", { id: serial("id").primaryKey(), funcionarioId: integer("funcionario_id").notNull().references(() => peopleFuncionarios.id, { onDelete: "cascade" }), nome: varchar("nome", { length: 200 }).notNull(), cpf: varchar("cpf", { length: 14 }), dataNascimento: timestamp("data_nascimento"), parentesco: varchar("parentesco", { length: 30 }), // filho, conjuge, pai, mae irrf: integer("irrf").default(0), // deduz no IRRF salarioFamilia: integer("salario_familia").default(0), planoSaude: integer("plano_saude").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Eventos de Folha (Proventos e Descontos) export const peopleEventosFolha = pgTable("people_eventos_folha", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), codigo: varchar("codigo", { length: 10 }).notNull(), nome: varchar("nome", { length: 100 }).notNull(), tipo: varchar("tipo", { length: 20 }).notNull(), // provento, desconto natureza: varchar("natureza", { length: 20 }), // fixo, variavel, proporcional incidencias: jsonb("incidencias"), // { inss: true, irrf: true, fgts: true } formula: text("formula"), contaDebito: integer("conta_debito").references(() => contabilPlanoContas.id), contaCredito: integer("conta_credito").references(() => contabilPlanoContas.id), status: varchar("status", { length: 20 }).default("ativo"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Folha de Pagamento (Cabeçalho) export const peopleFolhaPagamento = pgTable("people_folha_pagamento", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), competencia: varchar("competencia", { length: 7 }).notNull(), // YYYY-MM tipo: varchar("tipo", { length: 20 }).notNull(), // mensal, ferias, 13_1, 13_2, rescisao dataCalculo: timestamp("data_calculo"), dataPagamento: timestamp("data_pagamento"), totalBruto: numeric("total_bruto", { precision: 15, scale: 2 }).default("0"), totalDescontos: numeric("total_descontos", { precision: 15, scale: 2 }).default("0"), totalLiquido: numeric("total_liquido", { precision: 15, scale: 2 }).default("0"), totalInss: numeric("total_inss", { precision: 15, scale: 2 }).default("0"), totalIrrf: numeric("total_irrf", { precision: 15, scale: 2 }).default("0"), totalFgts: numeric("total_fgts", { precision: 15, scale: 2 }).default("0"), status: varchar("status", { length: 20 }).default("aberta"), // aberta, calculada, fechada, paga contabilizado: integer("contabilizado").default(0), lancamentoContabilId: integer("lancamento_contabil_id").references(() => contabilLancamentos.id), createdBy: varchar("created_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Folha de Pagamento (Itens por Funcionário) export const peopleFolhaItens = pgTable("people_folha_itens", { id: serial("id").primaryKey(), folhaId: integer("folha_id").notNull().references(() => peopleFolhaPagamento.id, { onDelete: "cascade" }), funcionarioId: integer("funcionario_id").notNull().references(() => peopleFuncionarios.id), salarioBase: numeric("salario_base", { precision: 15, scale: 2 }), diasTrabalhados: integer("dias_trabalhados"), horasExtras50: numeric("horas_extras_50", { precision: 10, scale: 2 }).default("0"), horasExtras100: numeric("horas_extras_100", { precision: 10, scale: 2 }).default("0"), horasNoturnas: numeric("horas_noturnas", { precision: 10, scale: 2 }).default("0"), faltas: integer("faltas").default(0), atrasos: numeric("atrasos", { precision: 10, scale: 2 }).default("0"), totalProventos: numeric("total_proventos", { precision: 15, scale: 2 }).default("0"), totalDescontos: numeric("total_descontos", { precision: 15, scale: 2 }).default("0"), totalLiquido: numeric("total_liquido", { precision: 15, scale: 2 }).default("0"), baseInss: numeric("base_inss", { precision: 15, scale: 2 }).default("0"), valorInss: numeric("valor_inss", { precision: 15, scale: 2 }).default("0"), baseIrrf: numeric("base_irrf", { precision: 15, scale: 2 }).default("0"), valorIrrf: numeric("valor_irrf", { precision: 15, scale: 2 }).default("0"), baseFgts: numeric("base_fgts", { precision: 15, scale: 2 }).default("0"), valorFgts: numeric("valor_fgts", { precision: 15, scale: 2 }).default("0"), }); // Detalhes dos Eventos na Folha export const peopleFolhaEventos = pgTable("people_folha_eventos", { id: serial("id").primaryKey(), folhaItemId: integer("folha_item_id").notNull().references(() => peopleFolhaItens.id, { onDelete: "cascade" }), eventoId: integer("evento_id").notNull().references(() => peopleEventosFolha.id), referencia: numeric("referencia", { precision: 10, scale: 2 }), // horas, dias, percentual valor: numeric("valor", { precision: 15, scale: 2 }).notNull(), }); // Férias export const peopleFerias = pgTable("people_ferias", { id: serial("id").primaryKey(), funcionarioId: integer("funcionario_id").notNull().references(() => peopleFuncionarios.id, { onDelete: "cascade" }), periodoAquisitivoInicio: timestamp("periodo_aquisitivo_inicio").notNull(), periodoAquisitivoFim: timestamp("periodo_aquisitivo_fim").notNull(), diasDireito: integer("dias_direito").default(30), diasGozados: integer("dias_gozados").default(0), diasVendidos: integer("dias_vendidos").default(0), dataInicio: timestamp("data_inicio"), dataFim: timestamp("data_fim"), valorFerias: numeric("valor_ferias", { precision: 15, scale: 2 }), valorTerco: numeric("valor_terco", { precision: 15, scale: 2 }), valorAbono: numeric("valor_abono", { precision: 15, scale: 2 }), status: varchar("status", { length: 20 }).default("pendente"), // pendente, programada, em_gozo, concluida createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Ponto / Frequência export const peoplePonto = pgTable("people_ponto", { id: serial("id").primaryKey(), funcionarioId: integer("funcionario_id").notNull().references(() => peopleFuncionarios.id, { onDelete: "cascade" }), data: timestamp("data").notNull(), entrada1: varchar("entrada1", { length: 5 }), saida1: varchar("saida1", { length: 5 }), entrada2: varchar("entrada2", { length: 5 }), saida2: varchar("saida2", { length: 5 }), horasTrabalhadas: numeric("horas_trabalhadas", { precision: 10, scale: 2 }), horasExtras: numeric("horas_extras", { precision: 10, scale: 2 }), horasNoturnas: numeric("horas_noturnas", { precision: 10, scale: 2 }), atraso: numeric("atraso", { precision: 10, scale: 2 }), falta: integer("falta").default(0), justificativa: text("justificativa"), status: varchar("status", { length: 20 }).default("normal"), // normal, falta, atestado, folga createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Benefícios export const peopleBeneficios = pgTable("people_beneficios", { id: serial("id").primaryKey(), tenantId: integer("tenant_id"), codigo: varchar("codigo", { length: 20 }), nome: varchar("nome", { length: 100 }).notNull(), tipo: varchar("tipo", { length: 30 }), // vt, vr, va, plano_saude, plano_odonto, seguro_vida fornecedor: varchar("fornecedor", { length: 100 }), valorEmpresa: numeric("valor_empresa", { precision: 15, scale: 2 }), valorFuncionario: numeric("valor_funcionario", { precision: 15, scale: 2 }), percentualDesconto: numeric("percentual_desconto", { precision: 5, scale: 2 }), eventoDescontoId: integer("evento_desconto_id").references(() => peopleEventosFolha.id), status: varchar("status", { length: 20 }).default("ativo"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Funcionário x Benefícios export const peopleFuncionarioBeneficios = pgTable("people_funcionario_beneficios", { id: serial("id").primaryKey(), funcionarioId: integer("funcionario_id").notNull().references(() => peopleFuncionarios.id, { onDelete: "cascade" }), beneficioId: integer("beneficio_id").notNull().references(() => peopleBeneficios.id), dataInicio: timestamp("data_inicio"), dataFim: timestamp("data_fim"), valorPersonalizado: numeric("valor_personalizado", { precision: 15, scale: 2 }), status: varchar("status", { length: 20 }).default("ativo"), }); // Tabelas de Cálculo (INSS, IRRF, Salário Família) export const peopleTabelasCalculo = pgTable("people_tabelas_calculo", { id: serial("id").primaryKey(), tipo: varchar("tipo", { length: 20 }).notNull(), // inss, irrf, salario_familia vigencia: timestamp("vigencia").notNull(), faixaInicio: numeric("faixa_inicio", { precision: 15, scale: 2 }).notNull(), faixaFim: numeric("faixa_fim", { precision: 15, scale: 2 }), aliquota: numeric("aliquota", { precision: 5, scale: 2 }), deducao: numeric("deducao", { precision: 15, scale: 2 }), valor: numeric("valor", { precision: 15, scale: 2 }), }); // ========== Insert Schemas - Arcádia Contábil ========== export const insertContabilPlanoContasSchema = createInsertSchema(contabilPlanoContas).omit({ id: true, createdAt: true, updatedAt: true }); export const insertContabilCentroCustoSchema = createInsertSchema(contabilCentrosCusto).omit({ id: true, createdAt: true }); export const insertContabilLancamentoSchema = createInsertSchema(contabilLancamentos).omit({ id: true, createdAt: true }); export const insertContabilPartidaSchema = createInsertSchema(contabilPartidas).omit({ id: true }); export const insertContabilPeriodoSchema = createInsertSchema(contabilPeriodos).omit({ id: true }); export const insertContabilConfigLancamentoSchema = createInsertSchema(contabilConfigLancamento).omit({ id: true, createdAt: true }); export const insertContabilSaldoSchema = createInsertSchema(contabilSaldos).omit({ id: true, updatedAt: true }); // ========== Types - Arcádia Contábil ========== export type ContabilPlanoContas = typeof contabilPlanoContas.$inferSelect; export type InsertContabilPlanoContas = z.infer; export type ContabilCentroCusto = typeof contabilCentrosCusto.$inferSelect; export type InsertContabilCentroCusto = z.infer; export type ContabilLancamento = typeof contabilLancamentos.$inferSelect; export type InsertContabilLancamento = z.infer; export type ContabilPartida = typeof contabilPartidas.$inferSelect; export type InsertContabilPartida = z.infer; export type ContabilPeriodo = typeof contabilPeriodos.$inferSelect; export type InsertContabilPeriodo = z.infer; export type ContabilConfigLancamento = typeof contabilConfigLancamento.$inferSelect; export type InsertContabilConfigLancamento = z.infer; export type ContabilSaldo = typeof contabilSaldos.$inferSelect; export type InsertContabilSaldo = z.infer; // ========== Insert Schemas - Arcádia People ========== export const insertPeopleCargoSchema = createInsertSchema(peopleCargos).omit({ id: true, createdAt: true }); export const insertPeopleDepartamentoSchema = createInsertSchema(peopleDepartamentos).omit({ id: true, createdAt: true }); export const insertPeopleFuncionarioSchema = createInsertSchema(peopleFuncionarios).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPeopleDependenteSchema = createInsertSchema(peopleDependentes).omit({ id: true, createdAt: true }); export const insertPeopleEventoFolhaSchema = createInsertSchema(peopleEventosFolha).omit({ id: true, createdAt: true }); export const insertPeopleFolhaPagamentoSchema = createInsertSchema(peopleFolhaPagamento).omit({ id: true, createdAt: true, updatedAt: true }); export const insertPeopleFolhaItemSchema = createInsertSchema(peopleFolhaItens).omit({ id: true }); export const insertPeopleFolhaEventoSchema = createInsertSchema(peopleFolhaEventos).omit({ id: true }); export const insertPeopleFeriasSchema = createInsertSchema(peopleFerias).omit({ id: true, createdAt: true }); export const insertPeoplePontoSchema = createInsertSchema(peoplePonto).omit({ id: true, createdAt: true }); export const insertPeopleBeneficioSchema = createInsertSchema(peopleBeneficios).omit({ id: true, createdAt: true }); export const insertPeopleFuncionarioBeneficioSchema = createInsertSchema(peopleFuncionarioBeneficios).omit({ id: true }); export const insertPeopleTabelaCalculoSchema = createInsertSchema(peopleTabelasCalculo).omit({ id: true }); // ========== Types - Arcádia People ========== export type PeopleCargo = typeof peopleCargos.$inferSelect; export type InsertPeopleCargo = z.infer; export type PeopleDepartamento = typeof peopleDepartamentos.$inferSelect; export type InsertPeopleDepartamento = z.infer; export type PeopleFuncionario = typeof peopleFuncionarios.$inferSelect; export type InsertPeopleFuncionario = z.infer; export type PeopleDependente = typeof peopleDependentes.$inferSelect; export type InsertPeopleDependente = z.infer; export type PeopleEventoFolha = typeof peopleEventosFolha.$inferSelect; export type InsertPeopleEventoFolha = z.infer; export type PeopleFolhaPagamento = typeof peopleFolhaPagamento.$inferSelect; export type InsertPeopleFolhaPagamento = z.infer; export type PeopleFolhaItem = typeof peopleFolhaItens.$inferSelect; export type InsertPeopleFolhaItem = z.infer; export type PeopleFolhaEvento = typeof peopleFolhaEventos.$inferSelect; export type InsertPeopleFolhaEvento = z.infer; export type PeopleFerias = typeof peopleFerias.$inferSelect; export type InsertPeopleFerias = z.infer; export type PeoplePonto = typeof peoplePonto.$inferSelect; export type InsertPeoplePonto = z.infer; export type PeopleBeneficio = typeof peopleBeneficios.$inferSelect; export type InsertPeopleBeneficio = z.infer; export type PeopleFuncionarioBeneficio = typeof peopleFuncionarioBeneficios.$inferSelect; export type InsertPeopleFuncionarioBeneficio = z.infer; export type PeopleTabelaCalculo = typeof peopleTabelasCalculo.$inferSelect; export type InsertPeopleTabelaCalculo = z.infer; // ======================================== // ARCÁDIA FINANCEIRO - Financial Management // ======================================== // Contas Bancárias export const finBankAccounts = pgTable("fin_bank_accounts", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), code: varchar("code", { length: 50 }).notNull(), name: varchar("name", { length: 256 }).notNull(), bankCode: varchar("bank_code", { length: 10 }), bankName: varchar("bank_name", { length: 100 }), agency: varchar("agency", { length: 20 }), accountNumber: varchar("account_number", { length: 30 }), accountDigit: varchar("account_digit", { length: 5 }), accountType: varchar("account_type", { length: 50 }).default("checking"), initialBalance: numeric("initial_balance", { precision: 15, scale: 2 }).default("0"), currentBalance: numeric("current_balance", { precision: 15, scale: 2 }).default("0"), isActive: boolean("is_active").default(true), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Meios de Pagamento export const finPaymentMethods = pgTable("fin_payment_methods", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), code: varchar("code", { length: 50 }).notNull(), name: varchar("name", { length: 100 }).notNull(), type: varchar("type", { length: 50 }).notNull(), defaultBankAccountId: integer("default_bank_account_id").references(() => finBankAccounts.id), fee: numeric("fee", { precision: 5, scale: 2 }).default("0"), daysToReceive: integer("days_to_receive").default(0), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Planos de Pagamento export const finPaymentPlans = pgTable("fin_payment_plans", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), code: varchar("code", { length: 50 }).notNull(), name: varchar("name", { length: 100 }).notNull(), installments: integer("installments").default(1), intervalDays: integer("interval_days").default(30), firstDueDays: integer("first_due_days").default(30), discountPercent: numeric("discount_percent", { precision: 5, scale: 2 }).default("0"), interestPercent: numeric("interest_percent", { precision: 5, scale: 2 }).default("0"), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Categorias de Fluxo de Caixa export const finCashFlowCategories = pgTable("fin_cash_flow_categories", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), code: varchar("code", { length: 50 }).notNull(), name: varchar("name", { length: 100 }).notNull(), type: varchar("type", { length: 20 }).notNull(), parentId: integer("parent_id"), contabilAccountId: integer("contabil_account_id"), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Contas a Pagar export const finAccountsPayable = pgTable("fin_accounts_payable", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), documentNumber: varchar("document_number", { length: 100 }), supplierId: integer("supplier_id").references(() => suppliers.id), supplierName: varchar("supplier_name", { length: 256 }), categoryId: integer("category_id").references(() => finCashFlowCategories.id), description: text("description"), issueDate: date("issue_date").notNull(), dueDate: date("due_date").notNull(), originalAmount: numeric("original_amount", { precision: 15, scale: 2 }).notNull(), discountAmount: numeric("discount_amount", { precision: 15, scale: 2 }).default("0"), interestAmount: numeric("interest_amount", { precision: 15, scale: 2 }).default("0"), fineAmount: numeric("fine_amount", { precision: 15, scale: 2 }).default("0"), paidAmount: numeric("paid_amount", { precision: 15, scale: 2 }).default("0"), remainingAmount: numeric("remaining_amount", { precision: 15, scale: 2 }).notNull(), status: varchar("status", { length: 50 }).default("pending"), paymentMethodId: integer("payment_method_id").references(() => finPaymentMethods.id), bankAccountId: integer("bank_account_id").references(() => finBankAccounts.id), paidAt: timestamp("paid_at"), purchaseOrderId: integer("purchase_order_id").references(() => purchaseOrders.id), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Contas a Receber export const finAccountsReceivable = pgTable("fin_accounts_receivable", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), documentNumber: varchar("document_number", { length: 100 }), customerId: integer("customer_id").references(() => customers.id), customerName: varchar("customer_name", { length: 256 }), categoryId: integer("category_id").references(() => finCashFlowCategories.id), description: text("description"), issueDate: date("issue_date").notNull(), dueDate: date("due_date").notNull(), originalAmount: numeric("original_amount", { precision: 15, scale: 2 }).notNull(), discountAmount: numeric("discount_amount", { precision: 15, scale: 2 }).default("0"), interestAmount: numeric("interest_amount", { precision: 15, scale: 2 }).default("0"), fineAmount: numeric("fine_amount", { precision: 15, scale: 2 }).default("0"), receivedAmount: numeric("received_amount", { precision: 15, scale: 2 }).default("0"), remainingAmount: numeric("remaining_amount", { precision: 15, scale: 2 }).notNull(), status: varchar("status", { length: 50 }).default("pending"), paymentMethodId: integer("payment_method_id").references(() => finPaymentMethods.id), bankAccountId: integer("bank_account_id").references(() => finBankAccounts.id), receivedAt: timestamp("received_at"), salesOrderId: integer("sales_order_id").references(() => salesOrders.id), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Transações Financeiras export const finTransactions = pgTable("fin_transactions", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), bankAccountId: integer("bank_account_id").references(() => finBankAccounts.id).notNull(), type: varchar("type", { length: 20 }).notNull(), categoryId: integer("category_id").references(() => finCashFlowCategories.id), amount: numeric("amount", { precision: 15, scale: 2 }).notNull(), balanceAfter: numeric("balance_after", { precision: 15, scale: 2 }), transactionDate: date("transaction_date").notNull(), description: text("description"), documentNumber: varchar("document_number", { length: 100 }), payableId: integer("payable_id").references(() => finAccountsPayable.id), receivableId: integer("receivable_id").references(() => finAccountsReceivable.id), transferFromId: integer("transfer_from_id"), transferToId: integer("transfer_to_id"), reconciled: boolean("reconciled").default(false), reconciledAt: timestamp("reconciled_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== Insert Schemas - Arcádia Financeiro ========== export const insertFinBankAccountSchema = createInsertSchema(finBankAccounts).omit({ id: true, createdAt: true, updatedAt: true }); export const insertFinPaymentMethodSchema = createInsertSchema(finPaymentMethods).omit({ id: true, createdAt: true }); export const insertFinPaymentPlanSchema = createInsertSchema(finPaymentPlans).omit({ id: true, createdAt: true }); export const insertFinCashFlowCategorySchema = createInsertSchema(finCashFlowCategories).omit({ id: true, createdAt: true }); export const insertFinAccountsPayableSchema = createInsertSchema(finAccountsPayable).omit({ id: true, createdAt: true, updatedAt: true }); export const insertFinAccountsReceivableSchema = createInsertSchema(finAccountsReceivable).omit({ id: true, createdAt: true, updatedAt: true }); export const insertFinTransactionSchema = createInsertSchema(finTransactions).omit({ id: true, createdAt: true }); // ========== Types - Arcádia Financeiro ========== export type FinBankAccount = typeof finBankAccounts.$inferSelect; export type InsertFinBankAccount = z.infer; export type FinPaymentMethod = typeof finPaymentMethods.$inferSelect; export type InsertFinPaymentMethod = z.infer; export type FinPaymentPlan = typeof finPaymentPlans.$inferSelect; export type InsertFinPaymentPlan = z.infer; export type FinCashFlowCategory = typeof finCashFlowCategories.$inferSelect; export type InsertFinCashFlowCategory = z.infer; export type FinAccountsPayable = typeof finAccountsPayable.$inferSelect; export type InsertFinAccountsPayable = z.infer; export type FinAccountsReceivable = typeof finAccountsReceivable.$inferSelect; export type InsertFinAccountsReceivable = z.infer; export type FinTransaction = typeof finTransactions.$inferSelect; export type InsertFinTransaction = z.infer; // ========== COMUNIDADES (Discord-like) ========== // Comunidades (workspaces/servidores) export const communities = pgTable("communities", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), description: text("description"), iconEmoji: varchar("icon_emoji", { length: 10 }).default("🏢"), iconColor: varchar("icon_color", { length: 20 }).default("#3b82f6"), isPrivate: boolean("is_private").default(false), createdBy: varchar("created_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Canais de uma comunidade export const communityChannels = pgTable("community_channels", { id: serial("id").primaryKey(), communityId: integer("community_id").references(() => communities.id, { onDelete: "cascade" }).notNull(), name: varchar("name", { length: 100 }).notNull(), description: text("description"), type: varchar("type", { length: 20 }).default("text"), // text, voice, announcement isPrivate: boolean("is_private").default(false), orderIndex: integer("order_index").default(0), projectId: integer("project_id"), // vinculação opcional com projeto createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Membros de uma comunidade export const communityMembers = pgTable("community_members", { id: serial("id").primaryKey(), communityId: integer("community_id").references(() => communities.id, { onDelete: "cascade" }).notNull(), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(), role: varchar("role", { length: 20 }).default("member"), // owner, admin, moderator, member nickname: varchar("nickname", { length: 100 }), status: varchar("status", { length: 20 }).default("offline"), // online, away, busy, offline statusMessage: varchar("status_message", { length: 200 }), joinedAt: timestamp("joined_at").default(sql`CURRENT_TIMESTAMP`).notNull(), lastActiveAt: timestamp("last_active_at"), }); // Mensagens em canais export const communityMessages = pgTable("community_messages", { id: serial("id").primaryKey(), channelId: integer("channel_id").references(() => communityChannels.id, { onDelete: "cascade" }).notNull(), userId: varchar("user_id").references(() => users.id).notNull(), content: text("content").notNull(), replyToId: integer("reply_to_id"), isPinned: boolean("is_pinned").default(false), editedAt: timestamp("edited_at"), deletedAt: timestamp("deleted_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert schemas - Comunidades export const insertCommunitySchema = createInsertSchema(communities).omit({ id: true, createdAt: true, updatedAt: true }); export const insertCommunityChannelSchema = createInsertSchema(communityChannels).omit({ id: true, createdAt: true }); export const insertCommunityMemberSchema = createInsertSchema(communityMembers).omit({ id: true, joinedAt: true }); export const insertCommunityMessageSchema = createInsertSchema(communityMessages).omit({ id: true, createdAt: true }); // Types - Comunidades export type Community = typeof communities.$inferSelect; export type InsertCommunity = z.infer; export type CommunityChannel = typeof communityChannels.$inferSelect; export type InsertCommunityChannel = z.infer; export type CommunityMember = typeof communityMembers.$inferSelect; export type InsertCommunityMember = z.infer; export type CommunityMessage = typeof communityMessages.$inferSelect; export type InsertCommunityMessage = z.infer; // ======================================== // MÓDULO PARA - Produtividade Pessoal // ======================================== // Projetos PARA - Metas com prazo definido export const paraProjects = pgTable("para_projects", { id: serial("id").primaryKey(), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(), name: varchar("name", { length: 200 }).notNull(), description: text("description"), // Tipo do projeto: personal (Kanban pessoal) ou production (link para módulo Produção) projectType: varchar("project_type", { length: 20 }).default("personal"), // personal, production // ID do projeto de produção vinculado (quando projectType = "production") productionProjectId: integer("production_project_id"), status: varchar("status", { length: 20 }).default("active"), // active, completed, archived color: varchar("color", { length: 20 }).default("#3b82f6"), icon: varchar("icon", { length: 50 }), dueDate: timestamp("due_date"), completedAt: timestamp("completed_at"), progress: integer("progress").default(0), // 0-100 createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`), }); // Áreas PARA - Responsabilidades contínuas export const paraAreas = pgTable("para_areas", { id: serial("id").primaryKey(), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(), name: varchar("name", { length: 200 }).notNull(), description: text("description"), icon: varchar("icon", { length: 50 }), color: varchar("color", { length: 20 }).default("#10b981"), status: varchar("status", { length: 20 }).default("active"), // active, archived createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`), }); // Recursos PARA - Materiais de referência export const paraResources = pgTable("para_resources", { id: serial("id").primaryKey(), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(), name: varchar("name", { length: 200 }).notNull(), description: text("description"), type: varchar("type", { length: 50 }).default("link"), // link, document, note, bookmark url: text("url"), content: text("content"), tags: text("tags").array(), projectId: integer("project_id").references(() => paraProjects.id, { onDelete: "set null" }), areaId: integer("area_id").references(() => paraAreas.id, { onDelete: "set null" }), status: varchar("status", { length: 20 }).default("active"), // active, archived createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`), }); // Tarefas PARA com Tríade do Tempo export const paraTasks = pgTable("para_tasks", { id: serial("id").primaryKey(), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(), title: varchar("title", { length: 300 }).notNull(), description: text("description"), projectId: integer("project_id").references(() => paraProjects.id, { onDelete: "set null" }), areaId: integer("area_id").references(() => paraAreas.id, { onDelete: "set null" }), // Tríade do Tempo: importante, urgente, circunstancial triadCategory: varchar("triad_category", { length: 20 }).default("importante").notNull(), status: varchar("status", { length: 20 }).default("pending"), // pending, in_progress, completed, cancelled priority: integer("priority").default(0), // 0=baixa, 1=média, 2=alta dueDate: timestamp("due_date"), reminderAt: timestamp("reminder_at"), estimatedMinutes: integer("estimated_minutes"), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`), }); // Arquivo PARA - Itens arquivados para consulta futura export const paraArchive = pgTable("para_archive", { id: serial("id").primaryKey(), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(), originalType: varchar("original_type", { length: 20 }).notNull(), // project, area, resource, task originalId: integer("original_id").notNull(), name: varchar("name", { length: 200 }).notNull(), metadata: jsonb("metadata"), // Dados originais do item archivedAt: timestamp("archived_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert schemas - PARA export const insertParaProjectSchema = createInsertSchema(paraProjects).omit({ id: true, createdAt: true, updatedAt: true }); export const insertParaAreaSchema = createInsertSchema(paraAreas).omit({ id: true, createdAt: true, updatedAt: true }); export const insertParaResourceSchema = createInsertSchema(paraResources).omit({ id: true, createdAt: true, updatedAt: true }); export const insertParaTaskSchema = createInsertSchema(paraTasks).omit({ id: true, createdAt: true, updatedAt: true }); export const insertParaArchiveSchema = createInsertSchema(paraArchive).omit({ id: true, archivedAt: true }); // Types - PARA export type ParaProject = typeof paraProjects.$inferSelect; export type InsertParaProject = z.infer; export type ParaArea = typeof paraAreas.$inferSelect; export type InsertParaArea = z.infer; export type ParaResource = typeof paraResources.$inferSelect; export type InsertParaResource = z.infer; export type ParaTask = typeof paraTasks.$inferSelect; export type InsertParaTask = z.infer; export type ParaArchive = typeof paraArchive.$inferSelect; export type InsertParaArchive = z.infer; // ========== MÓDULO QUALIDADE - Engenharia Ambiental ========== // Controle de Amostras (RF-QC01) export const qualitySamples = pgTable("quality_samples", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }), sampleCode: varchar("sample_code", { length: 50 }).notNull(), sampleType: varchar("sample_type", { length: 100 }), // água, solo, ar, sedimento, efluente collectionDate: timestamp("collection_date"), collectionLocation: text("collection_location"), collectionResponsible: varchar("collection_responsible", { length: 256 }), collectionMethod: varchar("collection_method", { length: 100 }), preservationMethod: varchar("preservation_method", { length: 100 }), laboratoryId: integer("laboratory_id").references(() => suppliers.id), sentToLabDate: timestamp("sent_to_lab_date"), labReceptionDate: timestamp("lab_reception_date"), expectedResultDate: timestamp("expected_result_date"), actualResultDate: timestamp("actual_result_date"), status: varchar("status", { length: 50 }).default("coletada"), // coletada, enviada, em_analise, resultado_recebido, aprovada, reprovada observations: text("observations"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Laudos Laboratoriais (RF-QC01) export const qualityLabReports = pgTable("quality_lab_reports", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), sampleId: integer("sample_id").references(() => qualitySamples.id, { onDelete: "cascade" }), reportNumber: varchar("report_number", { length: 100 }), laboratoryId: integer("laboratory_id").references(() => suppliers.id), issueDate: timestamp("issue_date"), receptionDate: timestamp("reception_date"), parameters: jsonb("parameters").$type>(), conclusion: text("conclusion"), status: varchar("status", { length: 50 }).default("recebido"), // recebido, em_analise, aprovado, reprovado, pendente_reanálise fileUrl: text("file_url"), observations: text("observations"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Controle de Não Conformidades - RNC (RF-QC03) export const qualityNonConformities = pgTable("quality_non_conformities", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "set null" }), rncNumber: varchar("rnc_number", { length: 50 }).notNull(), title: varchar("title", { length: 256 }).notNull(), description: text("description"), type: varchar("type", { length: 50 }).default("nao_conformidade"), // nao_conformidade, acao_corretiva, oportunidade_melhoria source: varchar("source", { length: 100 }), // auditoria_interna, auditoria_externa, reclamacao_cliente, processo, fornecedor severity: varchar("severity", { length: 20 }).default("media"), // baixa, media, alta, critica detectedBy: varchar("detected_by", { length: 256 }), detectedAt: timestamp("detected_at"), rootCause: text("root_cause"), immediateAction: text("immediate_action"), correctiveAction: text("corrective_action"), preventiveAction: text("preventive_action"), responsibleId: varchar("responsible_id").references(() => users.id), dueDate: timestamp("due_date"), closedAt: timestamp("closed_at"), closedBy: varchar("closed_by").references(() => users.id), verificationDate: timestamp("verification_date"), verifiedBy: varchar("verified_by").references(() => users.id), effectivenessVerified: integer("effectiveness_verified").default(0), status: varchar("status", { length: 50 }).default("aberta"), // aberta, em_tratamento, pendente_verificacao, fechada, cancelada createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Gestão de Documentos da Qualidade - QMS (RF-QC02) export const qualityDocuments = pgTable("quality_documents", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), documentCode: varchar("document_code", { length: 50 }).notNull(), // FT-xx, PG-xx, IT-xx title: varchar("title", { length: 256 }).notNull(), type: varchar("type", { length: 50 }), // formulario, procedimento, instrucao, manual, registro category: varchar("category", { length: 100 }), // qualidade, operacional, administrativo, tecnico version: varchar("version", { length: 20 }).default("01"), revisionNumber: integer("revision_number").default(0), status: varchar("status", { length: 50 }).default("vigente"), // rascunho, em_revisao, aprovado, vigente, obsoleto effectiveDate: timestamp("effective_date"), expiryDate: timestamp("expiry_date"), nextReviewDate: timestamp("next_review_date"), author: varchar("author", { length: 256 }), approvedBy: varchar("approved_by").references(() => users.id), approvedAt: timestamp("approved_at"), fileUrl: text("file_url"), description: text("description"), keywords: text("keywords").array(), accessLevel: varchar("access_level", { length: 20 }).default("interno"), // publico, interno, restrito, confidencial createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Histórico de Revisões de Documentos export const qualityDocumentRevisions = pgTable("quality_document_revisions", { id: serial("id").primaryKey(), documentId: integer("document_id").references(() => qualityDocuments.id, { onDelete: "cascade" }), version: varchar("version", { length: 20 }), revisionNumber: integer("revision_number"), changeDescription: text("change_description"), revisedBy: varchar("revised_by").references(() => users.id), revisedAt: timestamp("revised_at").default(sql`CURRENT_TIMESTAMP`), fileUrl: text("file_url"), }); // Formulários de Campo Digitais (RF-OP03) export const qualityFieldForms = pgTable("quality_field_forms", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "cascade" }), formType: varchar("form_type", { length: 100 }).notNull(), // plano_amostragem, pt, monitoramento_pocos, ficha_campo formCode: varchar("form_code", { length: 50 }), title: varchar("title", { length: 256 }), collectionDate: timestamp("collection_date"), location: text("location"), coordinates: varchar("coordinates", { length: 100 }), responsibleId: varchar("responsible_id").references(() => users.id), teamMembers: text("team_members").array(), weatherConditions: varchar("weather_conditions", { length: 100 }), formData: jsonb("form_data").$type>(), // Dados dinâmicos do formulário photos: text("photos").array(), signature: text("signature"), status: varchar("status", { length: 50 }).default("rascunho"), // rascunho, preenchido, revisado, aprovado syncedAt: timestamp("synced_at"), // Para suporte offline observations: text("observations"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Matriz de Treinamentos (FT-57) export const qualityTrainingMatrix = pgTable("quality_training_matrix", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), employeeId: varchar("employee_id").references(() => users.id), trainingName: varchar("training_name", { length: 256 }).notNull(), trainingType: varchar("training_type", { length: 100 }), // obrigatorio, complementar, reciclagem provider: varchar("provider", { length: 256 }), completedDate: timestamp("completed_date"), expiryDate: timestamp("expiry_date"), certificateUrl: text("certificate_url"), hours: integer("hours"), status: varchar("status", { length: 50 }).default("pendente"), // pendente, em_andamento, concluido, vencido createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Prestação de Contas / Despesas de Campo (RF-AF05) export const fieldExpenses = pgTable("field_expenses", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), projectId: integer("project_id").references(() => pcProjects.id, { onDelete: "set null" }), expenseCode: varchar("expense_code", { length: 50 }), responsibleId: varchar("responsible_id").references(() => users.id), expenseDate: timestamp("expense_date"), category: varchar("category", { length: 100 }), // hospedagem, alimentacao, combustivel, transporte, material, outros description: text("description"), amount: numeric("amount", { precision: 15, scale: 2 }).notNull(), paymentMethod: varchar("payment_method", { length: 50 }), // cartao_corporativo, dinheiro, reembolso cardId: varchar("card_id", { length: 50 }), // ID do cartão CAJU receiptUrl: text("receipt_url"), costCenter: varchar("cost_center", { length: 100 }), status: varchar("status", { length: 50 }).default("pendente"), // pendente, aprovado_lider, aprovado_financeiro, pago, rejeitado approvedByLeader: varchar("approved_by_leader").references(() => users.id), approvedByLeaderAt: timestamp("approved_by_leader_at"), approvedByFinance: varchar("approved_by_finance").references(() => users.id), approvedByFinanceAt: timestamp("approved_by_finance_at"), rejectionReason: text("rejection_reason"), observations: text("observations"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert schemas - Qualidade export const insertQualitySampleSchema = createInsertSchema(qualitySamples).omit({ id: true, createdAt: true, updatedAt: true }); export const insertQualityLabReportSchema = createInsertSchema(qualityLabReports).omit({ id: true, createdAt: true, updatedAt: true }); export const insertQualityNonConformitySchema = createInsertSchema(qualityNonConformities).omit({ id: true, createdAt: true, updatedAt: true }); export const insertQualityDocumentSchema = createInsertSchema(qualityDocuments).omit({ id: true, createdAt: true, updatedAt: true }); export const insertQualityFieldFormSchema = createInsertSchema(qualityFieldForms).omit({ id: true, createdAt: true, updatedAt: true }); export const insertQualityTrainingMatrixSchema = createInsertSchema(qualityTrainingMatrix).omit({ id: true, createdAt: true, updatedAt: true }); export const insertFieldExpenseSchema = createInsertSchema(fieldExpenses).omit({ id: true, createdAt: true, updatedAt: true }); // Types - Qualidade export type QualitySample = typeof qualitySamples.$inferSelect; export type InsertQualitySample = z.infer; export type QualityLabReport = typeof qualityLabReports.$inferSelect; export type InsertQualityLabReport = z.infer; export type QualityNonConformity = typeof qualityNonConformities.$inferSelect; export type InsertQualityNonConformity = z.infer; export type QualityDocument = typeof qualityDocuments.$inferSelect; export type InsertQualityDocument = z.infer; export type QualityFieldForm = typeof qualityFieldForms.$inferSelect; export type InsertQualityFieldForm = z.infer; export type QualityTrainingMatrix = typeof qualityTrainingMatrix.$inferSelect; export type InsertQualityTrainingMatrix = z.infer; export type FieldExpense = typeof fieldExpenses.$inferSelect; export type InsertFieldExpense = z.infer; // ========================================== // SERVIÇOS AMBIENTAIS - Catálogo de Serviços // ========================================== export const environmentalServices = pgTable("environmental_services", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), code: varchar("code", { length: 50 }), name: text("name").notNull(), description: text("description"), category: varchar("category", { length: 100 }), // Monitoramento, Investigação, Remediação, Licenciamento, Consultoria basePrice: numeric("base_price", { precision: 15, scale: 2 }), unit: varchar("unit", { length: 50 }).default("projeto"), // projeto, campanha, hora, metro estimatedDuration: integer("estimated_duration"), // dias items: text("items").array(), // itens inclusos no serviço isActive: integer("is_active").default(1), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertEnvironmentalServiceSchema = createInsertSchema(environmentalServices).omit({ id: true, createdAt: true, updatedAt: true }); export type EnvironmentalService = typeof environmentalServices.$inferSelect; export type InsertEnvironmentalService = z.infer; // ========================================== // ARCÁDIA LOW-CODE - Sistema de Metadados // ========================================== // DocTypes - Define tipos de dados/entidades customizáveis export const arcDocTypes = pgTable("arc_doctypes", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), // Nome técnico (snake_case): controle_ponto label: varchar("label", { length: 200 }).notNull(), // Nome exibição: Controle de Ponto module: varchar("module", { length: 100 }), // Módulo pai: hrm, qualidade, comercial description: text("description"), icon: varchar("icon", { length: 50 }).default("FileText"), // Lucide icon name color: varchar("color", { length: 20 }).default("blue"), isSubmittable: boolean("is_submittable").default(false), // Requer workflow de aprovação isChild: boolean("is_child").default(false), // É tabela filho de outro doctype parentDocType: integer("parent_doctype_id"), isSingle: boolean("is_single").default(false), // Apenas um registro (configurações) isTree: boolean("is_tree").default(false), // Estrutura hierárquica trackChanges: boolean("track_changes").default(true), allowImport: boolean("allow_import").default(true), allowExport: boolean("allow_export").default(true), hasWebView: boolean("has_web_view").default(true), // Gerar página automaticamente permissions: jsonb("permissions").$type<{ role: string; read: boolean; write: boolean; delete: boolean; }[]>(), hooks: jsonb("hooks").$type<{ event: string; script: string; }[]>(), // before_save, after_save, validate status: varchar("status", { length: 20 }).default("active"), createdBy: varchar("created_by"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Fields - Define campos de cada DocType export const arcFields = pgTable("arc_fields", { id: serial("id").primaryKey(), docTypeId: integer("doctype_id").references(() => arcDocTypes.id, { onDelete: "cascade" }).notNull(), fieldName: varchar("field_name", { length: 100 }).notNull(), // Nome técnico: data_entrada label: varchar("label", { length: 200 }).notNull(), // Label: Data de Entrada fieldType: varchar("field_type", { length: 50 }).notNull(), // text, number, date, datetime, select, link, table, etc. options: text("options"), // Para select: opções separadas por \n; Para link: nome do DocType relacionado defaultValue: text("default_value"), mandatory: boolean("mandatory").default(false), unique: boolean("unique").default(false), readOnly: boolean("read_only").default(false), hidden: boolean("hidden").default(false), inListView: boolean("in_list_view").default(false), // Exibir na listagem inFilter: boolean("in_filter").default(false), // Usar como filtro searchable: boolean("searchable").default(false), sortOrder: integer("sort_order").default(0), section: varchar("section", { length: 100 }), // Agrupamento visual column: integer("column").default(1), // 1 ou 2 (layout em colunas) width: varchar("width", { length: 20 }), // full, half, third placeholder: text("placeholder"), helpText: text("help_text"), validation: jsonb("validation").$type<{ min?: number; max?: number; pattern?: string; message?: string; }>(), depends_on: text("depends_on"), // Expressão para exibição condicional fetchFrom: text("fetch_from"), // Auto-preencher de link: customer.customer_name createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Pages - Define páginas customizadas export const arcPages = pgTable("arc_pages", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), // Nome técnico: dashboard_vendas title: varchar("title", { length: 200 }).notNull(), // Título: Dashboard de Vendas route: varchar("route", { length: 200 }).notNull(), // /app/vendas-dashboard pageType: varchar("page_type", { length: 50 }).default("page"), // page, form, list, report, dashboard docType: integer("doctype_id").references(() => arcDocTypes.id), // Se for form/list de um DocType icon: varchar("icon", { length: 50 }), module: varchar("module", { length: 100 }), isPublic: boolean("is_public").default(false), roles: text("roles").array(), // Roles que podem acessar layout: jsonb("layout").$type(), // Configuração do layout (widgets, posições) script: text("script"), // Script customizado (JS) style: text("style"), // CSS customizado status: varchar("status", { length: 20 }).default("active"), createdBy: varchar("created_by"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Layouts - Templates de layout reutilizáveis export const arcLayouts = pgTable("arc_layouts", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), description: text("description"), layoutType: varchar("layout_type", { length: 50 }).default("form"), // form, list, dashboard, report config: jsonb("config").$type().notNull(), // Configuração JSON do layout isDefault: boolean("is_default").default(false), createdBy: varchar("created_by"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Widgets - Componentes reutilizáveis para páginas export const arcWidgets = pgTable("arc_widgets", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), label: varchar("label", { length: 200 }).notNull(), widgetType: varchar("widget_type", { length: 50 }).notNull(), // chart, table, kpi, form, list, custom category: varchar("category", { length: 50 }), // dashboard, report, form icon: varchar("icon", { length: 50 }), config: jsonb("config").$type(), // Configuração específica do widget dataSource: jsonb("data_source").$type<{ type: string; doctype?: string; query?: string; endpoint?: string; }>(), isSystem: boolean("is_system").default(false), status: varchar("status", { length: 20 }).default("active"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Scripts - Scripts customizados para automações export const arcScripts = pgTable("arc_scripts", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), docTypeId: integer("doctype_id").references(() => arcDocTypes.id), scriptType: varchar("script_type", { length: 50 }).notNull(), // client, server, form, list, report triggerEvent: varchar("trigger_event", { length: 50 }), // before_save, after_save, on_load, validate script: text("script").notNull(), isEnabled: boolean("is_enabled").default(true), createdBy: varchar("created_by"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Workflows - Automações visuais no-code export const arcWorkflows = pgTable("arc_workflows", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 255 }).notNull(), description: text("description"), nodes: jsonb("nodes").$type().default([]), status: varchar("status", { length: 50 }).default("draft"), // draft, active, inactive, deleted createdBy: varchar("created_by"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert schemas e types export const insertArcDocTypeSchema = createInsertSchema(arcDocTypes).omit({ id: true, createdAt: true, updatedAt: true }); export type ArcDocType = typeof arcDocTypes.$inferSelect; export type InsertArcDocType = z.infer; export const insertArcFieldSchema = createInsertSchema(arcFields).omit({ id: true, createdAt: true }); export type ArcField = typeof arcFields.$inferSelect; export type InsertArcField = z.infer; export const insertArcPageSchema = createInsertSchema(arcPages).omit({ id: true, createdAt: true, updatedAt: true }); export type ArcPage = typeof arcPages.$inferSelect; export type InsertArcPage = z.infer; export const insertArcLayoutSchema = createInsertSchema(arcLayouts).omit({ id: true, createdAt: true }); export type ArcLayout = typeof arcLayouts.$inferSelect; export type InsertArcLayout = z.infer; export const insertArcWidgetSchema = createInsertSchema(arcWidgets).omit({ id: true, createdAt: true }); export type ArcWidget = typeof arcWidgets.$inferSelect; export type InsertArcWidget = z.infer; export const insertArcScriptSchema = createInsertSchema(arcScripts).omit({ id: true, createdAt: true, updatedAt: true }); export type ArcScript = typeof arcScripts.$inferSelect; export type InsertArcScript = z.infer; export const insertArcWorkflowSchema = createInsertSchema(arcWorkflows).omit({ id: true, createdAt: true, updatedAt: true }); export type ArcWorkflow = typeof arcWorkflows.$inferSelect; export type InsertArcWorkflow = z.infer; // ========== ARCÁDIA RETAIL - LOJA E ASSISTÊNCIA DE CELULARES ========== // Stores - Lojas da rede de franquia export const retailStores = pgTable("retail_stores", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), code: varchar("code", { length: 20 }).notNull(), name: varchar("name", { length: 200 }).notNull(), storeType: varchar("store_type", { length: 50 }).default("store"), // holding, distributor, store parentStoreId: integer("parent_store_id"), warehouseId: integer("warehouse_id"), cnpj: varchar("cnpj", { length: 20 }), legalName: varchar("legal_name", { length: 200 }), address: text("address"), city: varchar("city", { length: 100 }), state: varchar("state", { length: 2 }), zipCode: varchar("zip_code", { length: 10 }), phone: varchar("phone", { length: 20 }), email: varchar("email", { length: 100 }), managerId: varchar("manager_id"), posEnabled: boolean("pos_enabled").default(true), serviceEnabled: boolean("service_enabled").default(true), leaseEnabled: boolean("lease_enabled").default(false), status: varchar("status", { length: 20 }).default("active"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Warehouses - Depósitos/Armazéns export const retailWarehouses = pgTable("retail_warehouses", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), storeId: integer("store_id").references(() => retailStores.id), // Opcional - pode ser depósito central code: varchar("code", { length: 20 }).notNull(), name: varchar("name", { length: 200 }).notNull(), description: text("description"), type: varchar("type", { length: 30 }).default("store"), // store, central, transit, virtual parentStoreId: integer("parent_store_id").references(() => retailStores.id), isMainWarehouse: boolean("is_main_warehouse").default(false), isDefault: boolean("is_default").default(false), address: text("address"), city: varchar("city", { length: 100 }), state: varchar("state", { length: 2 }), phone: varchar("phone", { length: 20 }), responsibleId: varchar("responsible_id").references(() => users.id), managerId: varchar("manager_id"), isActive: boolean("is_active").default(true), allowNegativeStock: boolean("allow_negative_stock").default(false), visibleToAllCompanies: boolean("visible_to_all_companies").default(true), // Visível para todo o grupo status: varchar("status", { length: 20 }).default("active"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`), }); // Mobile Devices - Celulares com IMEI export const mobileDevices = pgTable("mobile_devices", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), productId: integer("product_id").references(() => products.id), imei: varchar("imei", { length: 20 }).notNull(), imei2: varchar("imei2", { length: 20 }), brand: varchar("brand", { length: 50 }).notNull(), model: varchar("model", { length: 100 }).notNull(), color: varchar("color", { length: 50 }), storage: varchar("storage", { length: 20 }), ram: varchar("ram", { length: 20 }), condition: varchar("condition", { length: 20 }).default("new"), // new, refurbished, used purchaseDate: date("purchase_date"), purchasePrice: numeric("purchase_price", { precision: 12, scale: 2 }), sellingPrice: numeric("selling_price", { precision: 12, scale: 2 }), warrantyExpiry: date("warranty_expiry"), warehouseId: integer("warehouse_id").references(() => retailWarehouses.id), storeId: integer("store_id").references(() => retailStores.id), status: varchar("status", { length: 20 }).default("in_stock"), // in_stock, sold, in_service, returned, damaged, leased soldDate: date("sold_date"), soldToCustomer: varchar("sold_to_customer"), lastServiceDate: date("last_service_date"), notes: text("notes"), // Campos de Origem/Aquisição (Phase 0) acquisitionType: varchar("acquisition_type", { length: 20 }).default("purchase"), // trade_in, purchase, consignment, internal_transfer acquisitionCost: numeric("acquisition_cost", { precision: 12, scale: 2 }), relatedEvaluationId: integer("related_evaluation_id"), // Link com avaliação Trade-In relatedServiceOrderId: integer("related_service_order_id"), // Link com O.S. de revisão personId: integer("person_id"), // Referência à pessoa unificada (fornecedor/cliente origem) suggestedPrice: numeric("suggested_price", { precision: 12, scale: 2 }), // Preço sugerido baseado em margem profitMargin: numeric("profit_margin", { precision: 5, scale: 2 }), // Margem de lucro configurada // ERPNext Sync Fields erpnextItemCode: varchar("erpnext_item_code", { length: 140 }), erpnextSerialNo: varchar("erpnext_serial_no", { length: 140 }), lastSyncAt: timestamp("last_sync_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Device Evaluations - Avaliação de Trade-In export const deviceEvaluations = pgTable("device_evaluations", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), storeId: integer("store_id").references(() => retailStores.id), imei: varchar("imei", { length: 20 }).notNull(), brand: varchar("brand", { length: 50 }).notNull(), model: varchar("model", { length: 100 }).notNull(), color: varchar("color", { length: 50 }), customerId: varchar("customer_id"), customerName: varchar("customer_name", { length: 200 }), customerPhone: varchar("customer_phone", { length: 20 }), personId: integer("person_id"), // Referência à pessoa unificada evaluationDate: date("evaluation_date").default(sql`CURRENT_DATE`), // Checklist completo de avaliação powerOn: boolean("power_on").default(true), // Aparelho liga corretamente powerOnNotes: text("power_on_notes"), screenIssues: boolean("screen_issues").default(false), // Avarias, travamentos ou toque fantasma screenIssuesNotes: text("screen_issues_notes"), screenSpots: boolean("screen_spots").default(false), // Manchas na tela screenSpotsNotes: text("screen_spots_notes"), buttonsWorking: boolean("buttons_working").default(true), // Botões funcionando buttonsWorkingNotes: text("buttons_working_notes"), wearMarks: boolean("wear_marks").default(false), // Marcas de uso wearMarksNotes: text("wear_marks_notes"), wifiWorking: boolean("wifi_working").default(true), // Wi-Fi funcionando wifiWorkingNotes: text("wifi_working_notes"), simWorking: boolean("sim_working").default(true), // Chip funcionando simWorkingNotes: text("sim_working_notes"), mobileDataWorking: boolean("mobile_data_working").default(true), // 4G/5G funcionando mobileDataWorkingNotes: text("mobile_data_working_notes"), sensorsNfcWorking: boolean("sensors_nfc_working").default(true), // Sensores funcionando / NFC sensorsNfcWorkingNotes: text("sensors_nfc_working_notes"), biometricWorking: boolean("biometric_working").default(true), // Face ID / Touch ID funcionando biometricWorkingNotes: text("biometric_working_notes"), microphonesWorking: boolean("microphones_working").default(true), // Microfones funcionando microphonesWorkingNotes: text("microphones_working_notes"), earSpeakerWorking: boolean("ear_speaker_working").default(true), // Áudio auricular funcionando earSpeakerWorkingNotes: text("ear_speaker_working_notes"), loudspeakerWorking: boolean("loudspeaker_working").default(true), // Áudio alto-falante funcionando loudspeakerWorkingNotes: text("loudspeaker_working_notes"), chargingPortWorking: boolean("charging_port_working").default(true), // Entrada de carregamento funcionando chargingPortWorkingNotes: text("charging_port_working_notes"), camerasWorking: boolean("cameras_working").default(true), // Câmeras funcionando / Manchas camerasWorkingNotes: text("cameras_working_notes"), flashWorking: boolean("flash_working").default(true), // Flash funcionando flashWorkingNotes: text("flash_working_notes"), hasCharger: boolean("has_charger").default(false), // Possui carregador hasChargerNotes: text("has_charger_notes"), toolsAnalysisOk: boolean("tools_analysis_ok").default(true), // Análise pelo 3uTools OK toolsAnalysisNotes: text("tools_analysis_notes"), batteryHealth: integer("battery_health"), // Saúde da Bateria 0-100% batteryHealthNotes: text("battery_health_notes"), // Campos legados mantidos para compatibilidade screenCondition: varchar("screen_condition", { length: 20 }), bodyCondition: varchar("body_condition", { length: 20 }), overallCondition: varchar("overall_condition", { length: 20 }), estimatedValue: numeric("estimated_value", { precision: 12, scale: 2 }), approved: boolean("approved").default(false), rejectionReason: text("rejection_reason"), evaluatedBy: varchar("evaluated_by"), approvedBy: varchar("approved_by"), deviceId: integer("device_id").references(() => mobileDevices.id), // Quando aprovado, cria dispositivo status: varchar("status", { length: 20 }).default("pending"), // pending, analyzing, approved, rejected diagnosisStatus: varchar("diagnosis_status", { length: 20 }).default("pending"), // pending, in_progress, completed checklistData: jsonb("checklist_data"), // Dados completos do checklist de avaliação linkedServiceOrderId: integer("linked_service_order_id"), // O.S. de diagnóstico vinculada maintenanceOrderId: integer("maintenance_order_id"), // O.S. de manutenção interna após aprovação acquisitionValue: numeric("acquisition_value", { precision: 12, scale: 2 }), // Valor final de aquisição (pode ser diferente do estimado) creditGenerated: boolean("credit_generated").default(false), // Se já gerou crédito para o cliente creditId: integer("credit_id"), // ID do crédito gerado createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Service Orders - Ordens de Serviço export const serviceOrders = pgTable("service_orders", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), orderNumber: varchar("order_number", { length: 20 }).notNull(), storeId: integer("store_id").references(() => retailStores.id), deviceId: integer("device_id").references(() => mobileDevices.id), imei: varchar("imei", { length: 20 }).notNull(), brand: varchar("brand", { length: 50 }), model: varchar("model", { length: 100 }), customerId: varchar("customer_id"), customerName: varchar("customer_name", { length: 200 }).notNull(), customerPhone: varchar("customer_phone", { length: 20 }), customerEmail: varchar("customer_email", { length: 100 }), personId: integer("person_id"), // Referência à pessoa unificada serviceType: varchar("service_type", { length: 50 }).default("repair"), // repair, maintenance, internal_review, diagnostic issueDescription: text("issue_description").notNull(), diagnosisNotes: text("diagnosis_notes"), origin: varchar("origin", { length: 50 }).default("customer_request"), // customer_request, device_acquisition, warranty assignedTo: varchar("assigned_to"), technicianName: varchar("technician_name", { length: 200 }), technicianPersonId: integer("technician_person_id"), // Referência ao técnico (pessoa) partsCost: numeric("parts_cost", { precision: 12, scale: 2 }).default("0"), laborCost: numeric("labor_cost", { precision: 12, scale: 2 }).default("0"), totalCost: numeric("total_cost", { precision: 12, scale: 2 }).default("0"), expectedCompletionDate: date("expected_completion_date"), actualCompletionDate: date("actual_completion_date"), paymentStatus: varchar("payment_status", { length: 20 }).default("pending"), // pending, paid, partial status: varchar("status", { length: 20 }).default("open"), // open, diagnosis, quote, pending_approval, approved, rejected, in_repair, waiting_parts, quality_check, ready_pickup, completed, cancelled priority: varchar("priority", { length: 20 }).default("normal"), // low, normal, high, urgent // Campos para O.S. Interna (Phase 0) isInternal: boolean("is_internal").default(false), // true = O.S. interna (revisão trade-in) internalType: varchar("internal_type", { length: 30 }), // revision, cleaning, maintenance, quality_check, trade_in_diagnosis, trade_in_maintenance sourceEvaluationId: integer("source_evaluation_id"), // Link com avaliação Trade-In que originou esta O.S. evaluationStatus: varchar("evaluation_status", { length: 30 }).default("pending"), // pending, in_analysis, approved, rejected estimatedValue: numeric("estimated_value", { precision: 12, scale: 2 }), // Valor estimado inicial evaluatedValue: numeric("evaluated_value", { precision: 12, scale: 2 }), // Valor avaliado final // Campos de Checklist unificado checklistData: jsonb("checklist_data"), // Dados do checklist de avaliação/diagnóstico checklistCompletedAt: timestamp("checklist_completed_at"), // Quando o checklist foi finalizado checklistCompletedBy: varchar("checklist_completed_by"), // Quem finalizou o checklist // ERPNext Sync Fields erpnextDocType: varchar("erpnext_doc_type", { length: 50 }), // Maintenance Visit, Work Order, etc. erpnextDocName: varchar("erpnext_doc_name", { length: 140 }), lastSyncAt: timestamp("last_sync_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Service Order Items - Peças/Serviços da OS export const serviceOrderItems = pgTable("service_order_items", { id: serial("id").primaryKey(), serviceOrderId: integer("service_order_id").references(() => serviceOrders.id).notNull(), productId: integer("product_id").references(() => products.id), itemType: varchar("item_type", { length: 20 }).default("part"), // part, labor, accessory itemCode: varchar("item_code", { length: 50 }), itemName: varchar("item_name", { length: 200 }).notNull(), quantity: integer("quantity").default(1), unitPrice: numeric("unit_price", { precision: 12, scale: 2 }).notNull(), totalPrice: numeric("total_price", { precision: 12, scale: 2 }).notNull(), status: varchar("status", { length: 20 }).default("pending"), // pending, applied, removed createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // POS Sessions - Sessões de Caixa export const posSessions = pgTable("pos_sessions", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), storeId: integer("store_id").references(() => retailStores.id).notNull(), cashierId: varchar("cashier_id").notNull(), cashierName: varchar("cashier_name", { length: 200 }), sessionStartTime: timestamp("session_start_time").default(sql`CURRENT_TIMESTAMP`).notNull(), sessionEndTime: timestamp("session_end_time"), openingBalance: numeric("opening_balance", { precision: 12, scale: 2 }).default("0"), closingBalance: numeric("closing_balance", { precision: 12, scale: 2 }), totalSales: numeric("total_sales", { precision: 12, scale: 2 }).default("0"), totalRefunds: numeric("total_refunds", { precision: 12, scale: 2 }).default("0"), netSales: numeric("net_sales", { precision: 12, scale: 2 }).default("0"), cashPayments: numeric("cash_payments", { precision: 12, scale: 2 }).default("0"), cardPayments: numeric("card_payments", { precision: 12, scale: 2 }).default("0"), pixPayments: numeric("pix_payments", { precision: 12, scale: 2 }).default("0"), otherPayments: numeric("other_payments", { precision: 12, scale: 2 }).default("0"), transactionCount: integer("transaction_count").default(0), status: varchar("status", { length: 20 }).default("open"), // open, closed, reconciled notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // POS Sales - Vendas do PDV export const posSales = pgTable("pos_sales", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), sessionId: integer("session_id").references(() => posSessions.id), storeId: integer("store_id").references(() => retailStores.id).notNull(), saleNumber: varchar("sale_number", { length: 20 }).notNull(), saleType: varchar("sale_type", { length: 20 }).default("direct_sale"), // direct_sale, lease_to_own customerId: varchar("customer_id"), customerName: varchar("customer_name", { length: 200 }), customerPhone: varchar("customer_phone", { length: 20 }), customerCpf: varchar("customer_cpf", { length: 14 }), subtotal: numeric("subtotal", { precision: 12, scale: 2 }).notNull(), discountAmount: numeric("discount_amount", { precision: 12, scale: 2 }).default("0"), discountPercent: numeric("discount_percent", { precision: 5, scale: 2 }).default("0"), tradeInValue: numeric("trade_in_value", { precision: 12, scale: 2 }).default("0"), tradeInEvaluationId: integer("trade_in_evaluation_id").references(() => deviceEvaluations.id), totalAmount: numeric("total_amount", { precision: 12, scale: 2 }).notNull(), paymentMethod: varchar("payment_method", { length: 50 }), // cash, debit, credit, pix, combined paymentDetails: jsonb("payment_details").$type(), installments: integer("installments").default(1), paymentPlanId: integer("payment_plan_id"), status: varchar("status", { length: 20 }).default("completed"), // pending, completed, cancelled, refunded soldBy: varchar("sold_by"), notes: text("notes"), plusVendaId: integer("plus_venda_id"), plusNfeChave: varchar("plus_nfe_chave", { length: 60 }), plusSyncStatus: varchar("plus_sync_status", { length: 20 }).default("pending"), // pending, synced, error, not_applicable plusSyncError: text("plus_sync_error"), plusSyncedAt: timestamp("plus_synced_at"), empresaId: integer("empresa_id").references(() => tenantEmpresas.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // POS Sale Items - Itens da Venda export const posSaleItems = pgTable("pos_sale_items", { id: serial("id").primaryKey(), saleId: integer("sale_id").references(() => posSales.id).notNull(), itemType: varchar("item_type", { length: 20 }).default("product"), // product, device, accessory, service itemCode: varchar("item_code", { length: 50 }), itemName: varchar("item_name", { length: 200 }).notNull(), imei: varchar("imei", { length: 20 }), deviceId: integer("device_id").references(() => mobileDevices.id), quantity: integer("quantity").default(1), unitPrice: numeric("unit_price", { precision: 12, scale: 2 }).notNull(), discountAmount: numeric("discount_amount", { precision: 12, scale: 2 }).default("0"), totalPrice: numeric("total_price", { precision: 12, scale: 2 }).notNull(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Payment Plans - Planos de Pagamento/Parcelamento export const paymentPlans = pgTable("payment_plans", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), saleId: integer("sale_id").references(() => posSales.id), customerId: varchar("customer_id"), customerName: varchar("customer_name", { length: 200 }), totalAmount: numeric("total_amount", { precision: 12, scale: 2 }).notNull(), downPayment: numeric("down_payment", { precision: 12, scale: 2 }).default("0"), remainingAmount: numeric("remaining_amount", { precision: 12, scale: 2 }).notNull(), numberOfInstallments: integer("number_of_installments").notNull(), installmentAmount: numeric("installment_amount", { precision: 12, scale: 2 }).notNull(), interestRate: numeric("interest_rate", { precision: 5, scale: 2 }).default("0"), firstInstallmentDate: date("first_installment_date").notNull(), paidInstallments: integer("paid_installments").default(0), totalPaid: numeric("total_paid", { precision: 12, scale: 2 }).default("0"), status: varchar("status", { length: 20 }).default("active"), // active, completed, defaulted, cancelled createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Payment Plan Installments - Parcelas do Plano export const paymentPlanInstallments = pgTable("payment_plan_installments", { id: serial("id").primaryKey(), planId: integer("plan_id").references(() => paymentPlans.id).notNull(), installmentNumber: integer("installment_number").notNull(), dueDate: date("due_date").notNull(), amount: numeric("amount", { precision: 12, scale: 2 }).notNull(), paidAmount: numeric("paid_amount", { precision: 12, scale: 2 }).default("0"), paidDate: date("paid_date"), paymentMethod: varchar("payment_method", { length: 50 }), status: varchar("status", { length: 20 }).default("pending"), // pending, paid, overdue, cancelled createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Lease Agreements - Contratos de Locação com Opção de Compra export const leaseAgreements = pgTable("lease_agreements", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), storeId: integer("store_id").references(() => retailStores.id), agreementNumber: varchar("agreement_number", { length: 20 }).notNull(), customerId: varchar("customer_id"), customerName: varchar("customer_name", { length: 200 }).notNull(), customerCpf: varchar("customer_cpf", { length: 14 }), customerPhone: varchar("customer_phone", { length: 20 }), deviceId: integer("device_id").references(() => mobileDevices.id).notNull(), imei: varchar("imei", { length: 20 }).notNull(), leaseStartDate: date("lease_start_date").notNull(), leaseEndDate: date("lease_end_date").notNull(), numberOfMonths: integer("number_of_months").notNull(), monthlyPayment: numeric("monthly_payment", { precision: 12, scale: 2 }).notNull(), totalLeaseCost: numeric("total_lease_cost", { precision: 12, scale: 2 }).notNull(), purchaseOptionAvailable: boolean("purchase_option_available").default(true), purchasePrice: numeric("purchase_price", { precision: 12, scale: 2 }), purchasePriceIncludesPaidRent: boolean("purchase_price_includes_paid_rent").default(false), rentCreditPercent: numeric("rent_credit_percent", { precision: 5, scale: 2 }).default("50"), paidMonths: integer("paid_months").default(0), totalPaid: numeric("total_paid", { precision: 12, scale: 2 }).default("0"), status: varchar("status", { length: 20 }).default("active"), // active, completed, purchased, cancelled, defaulted createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Lease Payments - Pagamentos de Locação export const leasePayments = pgTable("lease_payments", { id: serial("id").primaryKey(), leaseId: integer("lease_id").references(() => leaseAgreements.id).notNull(), paymentNumber: integer("payment_number").notNull(), dueDate: date("due_date").notNull(), amount: numeric("amount", { precision: 12, scale: 2 }).notNull(), paidAmount: numeric("paid_amount", { precision: 12, scale: 2 }).default("0"), paidDate: date("paid_date"), paymentMethod: varchar("payment_method", { length: 50 }), status: varchar("status", { length: 20 }).default("pending"), // pending, paid, overdue createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Stock Transfers - Transferências de Estoque entre Lojas export const stockTransfers = pgTable("stock_transfers", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), transferNumber: varchar("transfer_number", { length: 20 }).notNull(), fromWarehouseId: integer("from_warehouse_id").references(() => retailWarehouses.id), fromStoreId: integer("from_store_id").references(() => retailStores.id), toWarehouseId: integer("to_warehouse_id").references(() => retailWarehouses.id), toStoreId: integer("to_store_id").references(() => retailStores.id), requestedDate: date("requested_date").default(sql`CURRENT_DATE`), shippedDate: date("shipped_date"), receivedDate: date("received_date"), trackingNumber: varchar("tracking_number", { length: 100 }), totalItems: integer("total_items").default(0), requestedBy: varchar("requested_by"), approvedBy: varchar("approved_by"), receivedBy: varchar("received_by"), status: varchar("status", { length: 20 }).default("draft"), // draft, submitted, approved, in_transit, received, cancelled notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Stock Transfer Items - Itens da Transferência export const stockTransferItems = pgTable("stock_transfer_items", { id: serial("id").primaryKey(), transferId: integer("transfer_id").references(() => stockTransfers.id).notNull(), deviceId: integer("device_id").references(() => mobileDevices.id).notNull(), imei: varchar("imei", { length: 20 }).notNull(), status: varchar("status", { length: 20 }).default("pending"), // pending, shipped, received createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Return/Exchange - Devoluções e Trocas export const returnExchanges = pgTable("return_exchanges", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), storeId: integer("store_id").references(() => retailStores.id), returnNumber: varchar("return_number", { length: 20 }).notNull(), originalSaleId: integer("original_sale_id").references(() => posSales.id), customerId: varchar("customer_id"), customerName: varchar("customer_name", { length: 200 }), returnType: varchar("return_type", { length: 20 }).default("return"), // return, exchange reason: text("reason"), refundAmount: numeric("refund_amount", { precision: 12, scale: 2 }).default("0"), refundMethod: varchar("refund_method", { length: 50 }), processedBy: varchar("processed_by"), returnDate: date("return_date").default(sql`CURRENT_DATE`), processedDate: date("processed_date"), status: varchar("status", { length: 20 }).default("pending"), // pending, approved, rejected, processed notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Return Exchange Items - Itens da Devolução export const returnExchangeItems = pgTable("return_exchange_items", { id: serial("id").primaryKey(), returnId: integer("return_id").references(() => returnExchanges.id).notNull(), itemCode: varchar("item_code", { length: 50 }), itemName: varchar("item_name", { length: 200 }).notNull(), quantity: integer("quantity").default(1), imei: varchar("imei", { length: 20 }), deviceId: integer("device_id").references(() => mobileDevices.id), reason: text("reason"), refundAmount: numeric("refund_amount", { precision: 12, scale: 2 }).default("0"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Device History - Histórico de Movimentação do IMEI export const deviceHistory = pgTable("device_history", { id: serial("id").primaryKey(), deviceId: integer("device_id").references(() => mobileDevices.id).notNull(), imei: varchar("imei", { length: 20 }).notNull(), eventType: varchar("event_type", { length: 50 }).notNull(), // received, transferred, sold, returned, service_in, service_out, leased, purchased eventDate: timestamp("event_date").default(sql`CURRENT_TIMESTAMP`).notNull(), fromLocation: varchar("from_location", { length: 100 }), toLocation: varchar("to_location", { length: 100 }), referenceType: varchar("reference_type", { length: 50 }), // sale, transfer, service_order, lease referenceId: integer("reference_id"), notes: text("notes"), createdBy: varchar("created_by"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== RETAIL CADASTROS ========== // Formas de Pagamento e Taxas export const retailPaymentMethods = pgTable("retail_payment_methods", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), type: varchar("type", { length: 30 }).notNull(), // cash, debit, credit, pix, boleto, financing brand: varchar("brand", { length: 50 }), // visa, mastercard, elo, amex, hipercard feePercent: numeric("fee_percent", { precision: 5, scale: 2 }).default("0"), fixedFee: numeric("fixed_fee", { precision: 12, scale: 2 }).default("0"), installmentsMax: integer("installments_max").default(1), installmentFees: jsonb("installment_fees").$type<{installments: number; feePercent: number}[]>(), daysToReceive: integer("days_to_receive").default(1), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Vendedores export const retailSellers = pgTable("retail_sellers", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), personId: integer("person_id").references(() => persons.id), code: varchar("code", { length: 20 }), name: varchar("name", { length: 200 }).notNull(), email: varchar("email", { length: 100 }), phone: varchar("phone", { length: 20 }), storeId: integer("store_id").references(() => retailStores.id), commissionPlanId: integer("commission_plan_id"), hireDate: date("hire_date"), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Planos de Comissão export const retailCommissionPlans = pgTable("retail_commission_plans", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), description: text("description"), type: varchar("type", { length: 30 }).notNull(), // fixed, percent, tiered, per_product baseValue: numeric("base_value", { precision: 12, scale: 2 }).default("0"), basePercent: numeric("base_percent", { precision: 5, scale: 2 }).default("0"), rules: jsonb("rules").$type(), // Regras específicas (tiers, produtos, categorias) isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Metas de Vendedor export const retailSellerGoals = pgTable("retail_seller_goals", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), sellerId: integer("seller_id").references(() => retailSellers.id), storeId: integer("store_id").references(() => retailStores.id), month: integer("month").notNull(), // 1-12 year: integer("year").notNull(), goalAmount: numeric("goal_amount", { precision: 14, scale: 2 }).notNull(), goalType: varchar("goal_type", { length: 20 }).default("sales"), // sales, units, margin achievedAmount: numeric("achieved_amount", { precision: 14, scale: 2 }).default("0"), achievedPercent: numeric("achieved_percent", { precision: 5, scale: 2 }).default("0"), bonus: numeric("bonus", { precision: 12, scale: 2 }).default("0"), // bônus por atingir meta notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Meta da Loja export const retailStoreGoals = pgTable("retail_store_goals", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), storeId: integer("store_id").references(() => retailStores.id), month: integer("month").notNull(), year: integer("year").notNull(), goalAmount: numeric("goal_amount", { precision: 14, scale: 2 }).notNull(), achievedAmount: numeric("achieved_amount", { precision: 14, scale: 2 }).default("0"), achievedPercent: numeric("achieved_percent", { precision: 5, scale: 2 }).default("0"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Fechamento de Comissão export const retailCommissionClosures = pgTable("retail_commission_closures", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), storeId: integer("store_id").references(() => retailStores.id), sellerId: integer("seller_id").references(() => retailSellers.id), periodType: varchar("period_type", { length: 20 }).notNull(), // daily, monthly, custom periodStart: date("period_start").notNull(), periodEnd: date("period_end").notNull(), totalSales: numeric("total_sales", { precision: 14, scale: 2 }).default("0"), totalReturns: numeric("total_returns", { precision: 14, scale: 2 }).default("0"), // devoluções deduzidas netSales: numeric("net_sales", { precision: 14, scale: 2 }).default("0"), // vendas líquidas commissionRate: numeric("commission_rate", { precision: 5, scale: 2 }).default("0"), commissionAmount: numeric("commission_amount", { precision: 12, scale: 2 }).default("0"), bonusAmount: numeric("bonus_amount", { precision: 12, scale: 2 }).default("0"), // bônus por meta totalAmount: numeric("total_amount", { precision: 12, scale: 2 }).default("0"), // comissão + bônus salesCount: integer("sales_count").default(0), returnsCount: integer("returns_count").default(0), status: varchar("status", { length: 20 }).default("open"), // open, closed, paid closedAt: timestamp("closed_at"), closedBy: integer("closed_by"), paidAt: timestamp("paid_at"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Itens do Fechamento (vendas incluídas) export const retailCommissionClosureItems = pgTable("retail_commission_closure_items", { id: serial("id").primaryKey(), closureId: integer("closure_id").references(() => retailCommissionClosures.id).notNull(), saleId: integer("sale_id").references(() => posSales.id), returnId: integer("return_id").references(() => returnExchanges.id), itemType: varchar("item_type", { length: 20 }).notNull(), // sale, return amount: numeric("amount", { precision: 12, scale: 2 }).notNull(), commission: numeric("commission", { precision: 12, scale: 2 }).default("0"), originalDate: timestamp("original_date"), // data original da venda (para devoluções) createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Tabelas de Preço export const retailPriceTables = pgTable("retail_price_tables", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), code: varchar("code", { length: 20 }), description: text("description"), customerType: varchar("customer_type", { length: 50 }), // retail, wholesale, vip, employee discountPercent: numeric("discount_percent", { precision: 5, scale: 2 }).default("0"), markupPercent: numeric("markup_percent", { precision: 5, scale: 2 }).default("0"), validFrom: date("valid_from"), validTo: date("valid_to"), isDefault: boolean("is_default").default(false), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Preços por Tabela (preços específicos por produto/tabela) export const retailPriceTableItems = pgTable("retail_price_table_items", { id: serial("id").primaryKey(), priceTableId: integer("price_table_id").references(() => retailPriceTables.id).notNull(), productId: integer("product_id"), deviceId: integer("device_id").references(() => mobileDevices.id), productCode: varchar("product_code", { length: 50 }), customPrice: numeric("custom_price", { precision: 12, scale: 2 }), discountPercent: numeric("discount_percent", { precision: 5, scale: 2 }), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Promoções export const retailPromotions = pgTable("retail_promotions", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), description: text("description"), type: varchar("type", { length: 30 }).notNull(), // percent_off, fixed_off, buy_x_get_y, bundle discountValue: numeric("discount_value", { precision: 12, scale: 2 }), discountPercent: numeric("discount_percent", { precision: 5, scale: 2 }), applyTo: varchar("apply_to", { length: 30 }).default("all"), // all, category, product, brand applyToIds: jsonb("apply_to_ids").$type(), priceTableId: integer("price_table_id").references(() => retailPriceTables.id), minQuantity: integer("min_quantity").default(1), maxQuantity: integer("max_quantity"), validFrom: timestamp("valid_from"), validTo: timestamp("valid_to"), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Tipos de Produtos (Dispositivos e Acessórios) com atributos fiscais export const retailProductTypes = pgTable("retail_product_types", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), description: text("description"), category: varchar("category", { length: 30 }).notNull(), // device, accessory, part, service // Controle de série/IMEI requiresImei: boolean("requires_imei").default(false), requiresSerial: boolean("requires_serial").default(false), // Atributos fiscais padrão ncm: varchar("ncm", { length: 10 }), cest: varchar("cest", { length: 9 }), origem: integer("origem").default(0), // 0=Nacional, 1=Estrangeira importação direta, 2=Estrangeira adquirida mercado interno, etc. // CST/CSOSN ICMS cstIcms: varchar("cst_icms", { length: 3 }), // 00, 10, 20, 30, 40, 41, 50, 51, 60, 70, 90 csosn: varchar("csosn", { length: 3 }), // 101, 102, 103, 201, 202, 203, 300, 400, 500, 900 (Simples Nacional) // CFOPs padrão cfopVendaEstadual: varchar("cfop_venda_estadual", { length: 4 }).default("5102"), cfopVendaInterestadual: varchar("cfop_venda_interestadual", { length: 4 }).default("6102"), cfopDevolucaoEstadual: varchar("cfop_devolucao_estadual", { length: 4 }).default("1202"), cfopDevolucaoInterestadual: varchar("cfop_devolucao_interestadual", { length: 4 }).default("2202"), // Alíquotas padrão (pode ser sobrescrito pelo grupo tributário) aliqIcms: numeric("aliq_icms", { precision: 5, scale: 2 }), aliqPis: numeric("aliq_pis", { precision: 5, scale: 4 }).default("0.65"), aliqCofins: numeric("aliq_cofins", { precision: 5, scale: 4 }).default("3.00"), aliqIpi: numeric("aliq_ipi", { precision: 5, scale: 2 }).default("0"), // Reforma Tributária - IBS e CBS (Substituem PIS/COFINS/ICMS gradualmente) classTribIbs: varchar("class_trib_ibs", { length: 20 }), // Classificação tributária IBS aliqIbs: numeric("aliq_ibs", { precision: 5, scale: 2 }), // Alíquota IBS (estados/municípios) classTribCbs: varchar("class_trib_cbs", { length: 20 }), // Classificação tributária CBS aliqCbs: numeric("aliq_cbs", { precision: 5, scale: 2 }), // Alíquota CBS (federal) // Vínculo com grupo tributário (fallback) taxGroupId: integer("tax_group_id").references(() => fiscalGruposTributacao.id), // Unidade de medida padrão unidadeMedida: varchar("unidade_medida", { length: 6 }).default("UN"), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Aquisições (Compras/Trade-In prontos para estoque) export const retailAcquisitions = pgTable("retail_acquisitions", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), acquisitionNumber: varchar("acquisition_number", { length: 20 }).notNull(), type: varchar("type", { length: 30 }).notNull(), // trade_in, purchase, consignment sourceEvaluationId: integer("source_evaluation_id").references(() => deviceEvaluations.id), sourceServiceOrderId: integer("source_service_order_id").references(() => serviceOrders.id), deviceId: integer("device_id").references(() => mobileDevices.id), imei: varchar("imei", { length: 20 }), brand: varchar("brand", { length: 50 }), model: varchar("model", { length: 100 }), condition: varchar("condition", { length: 30 }), acquisitionCost: numeric("acquisition_cost", { precision: 12, scale: 2 }).default("0"), repairCost: numeric("repair_cost", { precision: 12, scale: 2 }).default("0"), totalCost: numeric("total_cost", { precision: 12, scale: 2 }).default("0"), suggestedPrice: numeric("suggested_price", { precision: 12, scale: 2 }), finalPrice: numeric("final_price", { precision: 12, scale: 2 }), linkedProductId: integer("linked_product_id"), linkedDeviceId: integer("linked_device_id").references(() => mobileDevices.id), status: varchar("status", { length: 30 }).default("pending"), // pending, ready_for_stock, in_stock, sold notes: text("notes"), processedBy: varchar("processed_by"), processedAt: timestamp("processed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== GESTÃO DE DEPÓSITOS E ESTOQUE ========== // Saldos de Estoque por Depósito export const retailWarehouseStock = pgTable("retail_warehouse_stock", { id: serial("id").primaryKey(), warehouseId: integer("warehouse_id").references(() => retailWarehouses.id).notNull(), productId: integer("product_id").references(() => products.id).notNull(), quantity: numeric("quantity", { precision: 12, scale: 4 }).default("0").notNull(), reservedQuantity: numeric("reserved_quantity", { precision: 12, scale: 4 }).default("0"), availableQuantity: numeric("available_quantity", { precision: 12, scale: 4 }).default("0"), minStock: numeric("min_stock", { precision: 12, scale: 4 }), maxStock: numeric("max_stock", { precision: 12, scale: 4 }), lastMovementAt: timestamp("last_movement_at"), lastInventoryAt: timestamp("last_inventory_at"), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Transferências entre Depósitos export const retailStockTransfers = pgTable("retail_stock_transfers", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), transferNumber: varchar("transfer_number", { length: 20 }).notNull(), sourceWarehouseId: integer("source_warehouse_id").references(() => retailWarehouses.id).notNull(), destinationWarehouseId: integer("destination_warehouse_id").references(() => retailWarehouses.id).notNull(), status: varchar("status", { length: 30 }).default("pending"), // pending, in_transit, completed, cancelled notes: text("notes"), requestedBy: varchar("requested_by").references(() => users.id), approvedBy: varchar("approved_by").references(() => users.id), completedBy: varchar("completed_by").references(() => users.id), requestedAt: timestamp("requested_at").default(sql`CURRENT_TIMESTAMP`), approvedAt: timestamp("approved_at"), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Itens da Transferência export const retailStockTransferItems = pgTable("retail_stock_transfer_items", { id: serial("id").primaryKey(), transferId: integer("transfer_id").references(() => retailStockTransfers.id).notNull(), productId: integer("product_id").references(() => products.id).notNull(), requestedQuantity: numeric("requested_quantity", { precision: 12, scale: 4 }).notNull(), transferredQuantity: numeric("transferred_quantity", { precision: 12, scale: 4 }), receivedQuantity: numeric("received_quantity", { precision: 12, scale: 4 }), notes: text("notes"), }); // Números de Série/IMEI por Item de Transferência export const retailTransferSerials = pgTable("retail_transfer_serials", { id: serial("id").primaryKey(), transferItemId: integer("transfer_item_id").references(() => retailStockTransferItems.id).notNull(), serialId: integer("serial_id").references(() => retailProductSerials.id).notNull(), }); // Movimentações de Estoque export const retailStockMovements = pgTable("retail_stock_movements", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), warehouseId: integer("warehouse_id").references(() => retailWarehouses.id).notNull(), productId: integer("product_id").references(() => products.id).notNull(), movementType: varchar("movement_type", { length: 30 }).notNull(), // entry, exit, transfer_in, transfer_out, adjustment, inventory, return operationType: varchar("operation_type", { length: 50 }), // purchase, sale, manual_entry, trade_in, devolution, inventory_adjustment quantity: numeric("quantity", { precision: 12, scale: 4 }).notNull(), previousStock: numeric("previous_stock", { precision: 12, scale: 4 }), newStock: numeric("new_stock", { precision: 12, scale: 4 }), unitCost: numeric("unit_cost", { precision: 12, scale: 4 }), totalCost: numeric("total_cost", { precision: 12, scale: 4 }), referenceType: varchar("reference_type", { length: 50 }), // purchase_order, sale, transfer, adjustment, nfe referenceId: integer("reference_id"), referenceNumber: varchar("reference_number", { length: 50 }), // NF número, pedido, etc. supplierId: integer("supplier_id").references(() => persons.id), notes: text("notes"), userId: varchar("user_id").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Números de Série/IMEI dos Produtos export const retailProductSerials = pgTable("retail_product_serials", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), productId: integer("product_id").references(() => products.id).notNull(), warehouseId: integer("warehouse_id").references(() => retailWarehouses.id), serialNumber: varchar("serial_number", { length: 50 }), imei: varchar("imei", { length: 20 }), imei2: varchar("imei2", { length: 20 }), // Segundo IMEI para celulares dual-SIM status: varchar("status", { length: 30 }).default("in_stock"), // in_stock, reserved, sold, returned, defective, in_transit acquisitionCost: numeric("acquisition_cost", { precision: 12, scale: 4 }), salePrice: numeric("sale_price", { precision: 12, scale: 4 }), soldPrice: numeric("sold_price", { precision: 12, scale: 4 }), movementId: integer("movement_id").references(() => retailStockMovements.id), // Movimento de entrada saleMovementId: integer("sale_movement_id"), // Movimento de saída/venda purchaseNfeNumber: varchar("purchase_nfe_number", { length: 50 }), saleNfeNumber: varchar("sale_nfe_number", { length: 50 }), customerId: integer("customer_id").references(() => persons.id), // Cliente que comprou notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Inventários export const retailInventories = pgTable("retail_inventories", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), warehouseId: integer("warehouse_id").references(() => retailWarehouses.id).notNull(), inventoryNumber: varchar("inventory_number", { length: 20 }).notNull(), type: varchar("type", { length: 30 }).default("full"), // full, partial, cyclic status: varchar("status", { length: 30 }).default("open"), // open, counting, adjusting, completed, cancelled startedAt: timestamp("started_at"), completedAt: timestamp("completed_at"), notes: text("notes"), createdBy: varchar("created_by").references(() => users.id), completedBy: varchar("completed_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Itens do Inventário export const retailInventoryItems = pgTable("retail_inventory_items", { id: serial("id").primaryKey(), inventoryId: integer("inventory_id").references(() => retailInventories.id).notNull(), productId: integer("product_id").references(() => products.id).notNull(), systemQuantity: numeric("system_quantity", { precision: 12, scale: 4 }), countedQuantity: numeric("counted_quantity", { precision: 12, scale: 4 }), difference: numeric("difference", { precision: 12, scale: 4 }), adjustmentApplied: boolean("adjustment_applied").default(false), countedBy: varchar("counted_by").references(() => users.id), countedAt: timestamp("counted_at"), notes: text("notes"), }); // Feed de Atividades export const retailActivityFeed = pgTable("retail_activity_feed", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), storeId: integer("store_id").references(() => retailStores.id), activityType: varchar("activity_type", { length: 50 }).notNull(), // sale, trade_in, service_order, stock_in, stock_out, evaluation, price_change, customer entityType: varchar("entity_type", { length: 50 }), // pos_sale, device_evaluation, service_order, mobile_device, person entityId: integer("entity_id"), title: varchar("title", { length: 200 }).notNull(), description: text("description"), metadata: jsonb("metadata").$type(), severity: varchar("severity", { length: 20 }).default("info"), // info, success, warning, error createdBy: varchar("created_by"), createdByName: varchar("created_by_name", { length: 200 }), isRead: boolean("is_read").default(false), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Relatórios Personalizados export const retailReports = pgTable("retail_reports", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), description: text("description"), type: varchar("type", { length: 30 }).default("custom"), // sales, inventory, commissions, financial, custom query: text("query"), filters: jsonb("filters").$type(), columns: jsonb("columns").$type<{field: string; label: string; type: string}[]>(), isSystem: boolean("is_system").default(false), isActive: boolean("is_active").default(true), createdBy: varchar("created_by"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Insert schemas e types para Retail export const insertRetailStoreSchema = createInsertSchema(retailStores).omit({ id: true, createdAt: true, updatedAt: true }); export type RetailStore = typeof retailStores.$inferSelect; export type InsertRetailStore = z.infer; export const insertRetailWarehouseSchema = createInsertSchema(retailWarehouses).omit({ id: true, createdAt: true }); export type RetailWarehouse = typeof retailWarehouses.$inferSelect; export type InsertRetailWarehouse = z.infer; export const insertMobileDeviceSchema = createInsertSchema(mobileDevices).omit({ id: true, createdAt: true, updatedAt: true }); export type MobileDevice = typeof mobileDevices.$inferSelect; export type InsertMobileDevice = z.infer; export const insertDeviceEvaluationSchema = createInsertSchema(deviceEvaluations).omit({ id: true, createdAt: true, updatedAt: true }); export type DeviceEvaluation = typeof deviceEvaluations.$inferSelect; export type InsertDeviceEvaluation = z.infer; export const insertServiceOrderSchema = createInsertSchema(serviceOrders).omit({ id: true, createdAt: true, updatedAt: true }); export type ServiceOrder = typeof serviceOrders.$inferSelect; export type InsertServiceOrder = z.infer; export const insertServiceOrderItemSchema = createInsertSchema(serviceOrderItems).omit({ id: true, createdAt: true }); export type ServiceOrderItem = typeof serviceOrderItems.$inferSelect; export type InsertServiceOrderItem = z.infer; export const insertPosSessionSchema = createInsertSchema(posSessions).omit({ id: true, createdAt: true }); export type PosSession = typeof posSessions.$inferSelect; export type InsertPosSession = z.infer; export const insertPosSaleSchema = createInsertSchema(posSales).omit({ id: true, createdAt: true }); export type PosSale = typeof posSales.$inferSelect; export type InsertPosSale = z.infer; export const insertPosSaleItemSchema = createInsertSchema(posSaleItems).omit({ id: true, createdAt: true }); export type PosSaleItem = typeof posSaleItems.$inferSelect; export type InsertPosSaleItem = z.infer; export const insertPaymentPlanSchema = createInsertSchema(paymentPlans).omit({ id: true, createdAt: true, updatedAt: true }); export type PaymentPlan = typeof paymentPlans.$inferSelect; export type InsertPaymentPlan = z.infer; export const insertPaymentPlanInstallmentSchema = createInsertSchema(paymentPlanInstallments).omit({ id: true, createdAt: true }); export type PaymentPlanInstallment = typeof paymentPlanInstallments.$inferSelect; export type InsertPaymentPlanInstallment = z.infer; export const insertLeaseAgreementSchema = createInsertSchema(leaseAgreements).omit({ id: true, createdAt: true, updatedAt: true }); export type LeaseAgreement = typeof leaseAgreements.$inferSelect; export type InsertLeaseAgreement = z.infer; export const insertLeasePaymentSchema = createInsertSchema(leasePayments).omit({ id: true, createdAt: true }); export type LeasePayment = typeof leasePayments.$inferSelect; export type InsertLeasePayment = z.infer; export const insertStockTransferSchema = createInsertSchema(stockTransfers).omit({ id: true, createdAt: true, updatedAt: true }); export type StockTransfer = typeof stockTransfers.$inferSelect; export type InsertStockTransfer = z.infer; export const insertStockTransferItemSchema = createInsertSchema(stockTransferItems).omit({ id: true, createdAt: true }); export type StockTransferItem = typeof stockTransferItems.$inferSelect; export type InsertStockTransferItem = z.infer; export const insertReturnExchangeSchema = createInsertSchema(returnExchanges).omit({ id: true, createdAt: true }); export type ReturnExchange = typeof returnExchanges.$inferSelect; export type InsertReturnExchange = z.infer; export const insertReturnExchangeItemSchema = createInsertSchema(returnExchangeItems).omit({ id: true, createdAt: true }); export type ReturnExchangeItem = typeof returnExchangeItems.$inferSelect; export type InsertReturnExchangeItem = z.infer; export const insertDeviceHistorySchema = createInsertSchema(deviceHistory).omit({ id: true, createdAt: true }); export type DeviceHistory = typeof deviceHistory.$inferSelect; export type InsertDeviceHistory = z.infer; // Trade-In Checklist Templates - Modelos de Checklist para Avaliação export const tradeInChecklistTemplates = pgTable("trade_in_checklist_templates", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), name: varchar("name", { length: 100 }).notNull(), description: text("description"), deviceCategory: varchar("device_category", { length: 50 }).default("smartphone"), // smartphone, tablet, laptop, smartwatch isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Trade-In Checklist Items - Itens do Checklist export const tradeInChecklistItems = pgTable("trade_in_checklist_items", { id: serial("id").primaryKey(), templateId: integer("template_id").references(() => tradeInChecklistTemplates.id).notNull(), category: varchar("category", { length: 50 }).notNull(), // visual, funcional, acessorios, documentacao itemName: varchar("item_name", { length: 100 }).notNull(), itemDescription: text("item_description"), evaluationType: varchar("evaluation_type", { length: 20 }).default("condition"), // condition, boolean, percentage, text options: text("options"), // JSON array de opções para tipo condition: ["perfeito","bom","regular","ruim"] impactOnValue: numeric("impact_on_value", { precision: 5, scale: 2 }).default("0"), // % de impacto no valor isRequired: boolean("is_required").default(true), displayOrder: integer("display_order").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Trade-In Evaluation Results - Resultados da Avaliação por Item export const tradeInEvaluationResults = pgTable("trade_in_evaluation_results", { id: serial("id").primaryKey(), evaluationId: integer("evaluation_id").references(() => deviceEvaluations.id).notNull(), checklistItemId: integer("checklist_item_id").references(() => tradeInChecklistItems.id).notNull(), result: varchar("result", { length: 50 }), // valor selecionado (perfeito, bom, etc) ou true/false percentValue: integer("percent_value"), // para tipo percentage notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Trade-In Transfer Documents - Documentos de Transferência de Posse export const tradeInTransferDocuments = pgTable("trade_in_transfer_documents", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), storeId: integer("store_id").references(() => retailStores.id), evaluationId: integer("evaluation_id").references(() => deviceEvaluations.id).notNull(), documentNumber: varchar("document_number", { length: 30 }).notNull(), // Dados do Cedente (Cliente) customerName: varchar("customer_name", { length: 200 }).notNull(), customerCpf: varchar("customer_cpf", { length: 14 }), customerRg: varchar("customer_rg", { length: 20 }), customerAddress: text("customer_address"), customerPhone: varchar("customer_phone", { length: 20 }), customerEmail: varchar("customer_email", { length: 100 }), // Dados do Dispositivo deviceBrand: varchar("device_brand", { length: 50 }).notNull(), deviceModel: varchar("device_model", { length: 100 }).notNull(), deviceImei: varchar("device_imei", { length: 20 }).notNull(), deviceImei2: varchar("device_imei2", { length: 20 }), deviceColor: varchar("device_color", { length: 50 }), deviceStorage: varchar("device_storage", { length: 20 }), deviceCondition: varchar("device_condition", { length: 50 }), // Valores agreedValue: numeric("agreed_value", { precision: 12, scale: 2 }).notNull(), paymentMethod: varchar("payment_method", { length: 50 }), // credit, cash, discount_on_purchase // Assinatura Digital customerSignature: text("customer_signature"), // Base64 da assinatura customerSignedAt: timestamp("customer_signed_at"), employeeSignature: text("employee_signature"), employeeName: varchar("employee_name", { length: 200 }), employeeSignedAt: timestamp("employee_signed_at"), // Termos termsAccepted: boolean("terms_accepted").default(false), // Status status: varchar("status", { length: 20 }).default("draft"), // draft, pending_signature, signed, completed, cancelled pdfUrl: text("pdf_url"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertTradeInChecklistTemplateSchema = createInsertSchema(tradeInChecklistTemplates).omit({ id: true, createdAt: true, updatedAt: true }); export type TradeInChecklistTemplate = typeof tradeInChecklistTemplates.$inferSelect; export type InsertTradeInChecklistTemplate = z.infer; export const insertTradeInChecklistItemSchema = createInsertSchema(tradeInChecklistItems).omit({ id: true, createdAt: true }); export type TradeInChecklistItem = typeof tradeInChecklistItems.$inferSelect; export type InsertTradeInChecklistItem = z.infer; export const insertTradeInEvaluationResultSchema = createInsertSchema(tradeInEvaluationResults).omit({ id: true, createdAt: true }); export type TradeInEvaluationResult = typeof tradeInEvaluationResults.$inferSelect; export type InsertTradeInEvaluationResult = z.infer; export const insertTradeInTransferDocumentSchema = createInsertSchema(tradeInTransferDocuments).omit({ id: true, createdAt: true, updatedAt: true }); export type TradeInTransferDocument = typeof tradeInTransferDocuments.$inferSelect; export type InsertTradeInTransferDocument = z.infer; // POS Cash Movements - Sangria e Reforço de Caixa export const posCashMovements = pgTable("pos_cash_movements", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), sessionId: integer("session_id").references(() => posSessions.id).notNull(), storeId: integer("store_id").references(() => retailStores.id).notNull(), type: varchar("type", { length: 20 }).notNull(), // withdrawal (sangria), reinforcement (reforço) amount: numeric("amount", { precision: 12, scale: 2 }).notNull(), reason: text("reason"), performedBy: varchar("performed_by"), performedByName: varchar("performed_by_name", { length: 200 }), authorizedBy: varchar("authorized_by"), authorizedByName: varchar("authorized_by_name", { length: 200 }), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertPosCashMovementSchema = createInsertSchema(posCashMovements).omit({ id: true, createdAt: true }); export type PosCashMovement = typeof posCashMovements.$inferSelect; export type InsertPosCashMovement = z.infer; // Service Warranties - Garantias de Serviço vinculadas à OS e IMEI export const serviceWarranties = pgTable("service_warranties", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), storeId: integer("store_id").references(() => retailStores.id), serviceOrderId: integer("service_order_id").references(() => serviceOrders.id).notNull(), deviceId: integer("device_id").references(() => mobileDevices.id), imei: varchar("imei", { length: 20 }), serviceType: varchar("service_type", { length: 50 }).notNull(), warrantyDays: integer("warranty_days").notNull(), startDate: date("start_date").notNull(), endDate: date("end_date").notNull(), customerName: varchar("customer_name", { length: 200 }), customerPhone: varchar("customer_phone", { length: 20 }), description: text("description"), status: varchar("status", { length: 20 }).default("active"), // active, expired, claimed, voided claimedAt: timestamp("claimed_at"), claimNotes: text("claim_notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertServiceWarrantySchema = createInsertSchema(serviceWarranties).omit({ id: true, createdAt: true }); export type ServiceWarranty = typeof serviceWarranties.$inferSelect; export type InsertServiceWarranty = z.infer; // Customer Credits - Créditos de Cliente (Trade-In, Devoluções, etc.) export const customerCredits = pgTable("customer_credits", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), storeId: integer("store_id").references(() => retailStores.id), personId: integer("person_id").notNull(), // Referência à pessoa unificada customerName: varchar("customer_name", { length: 200 }).notNull(), customerCpf: varchar("customer_cpf", { length: 20 }), amount: numeric("amount", { precision: 12, scale: 2 }).notNull(), usedAmount: numeric("used_amount", { precision: 12, scale: 2 }).default("0"), remainingAmount: numeric("remaining_amount", { precision: 12, scale: 2 }).notNull(), origin: varchar("origin", { length: 50 }).notNull(), // trade_in, refund, bonus, promotion originId: integer("origin_id"), // ID da avaliação, devolução, etc. description: text("description"), expiresAt: timestamp("expires_at"), // Validade do crédito (null = não expira) status: varchar("status", { length: 20 }).default("active"), // active, used, expired, cancelled usedInSaleId: integer("used_in_sale_id"), // Referência à venda onde foi usado createdBy: varchar("created_by"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertCustomerCreditSchema = createInsertSchema(customerCredits).omit({ id: true, createdAt: true, updatedAt: true }); export type CustomerCredit = typeof customerCredits.$inferSelect; export type InsertCustomerCredit = z.infer; // ========== PHASE 0: UNIFIED PERSON REGISTRY ========== // Persons - Cadastro Unificado de Pessoas (pode ter múltiplos papéis) export const persons = pgTable("persons", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), fullName: varchar("full_name", { length: 255 }).notNull(), cpfCnpj: varchar("cpf_cnpj", { length: 20 }), rgIe: varchar("rg_ie", { length: 30 }), // RG ou Inscrição Estadual email: varchar("email", { length: 255 }), phone: varchar("phone", { length: 20 }), phone2: varchar("phone2", { length: 20 }), whatsapp: varchar("whatsapp", { length: 20 }), address: text("address"), addressNumber: varchar("address_number", { length: 20 }), complement: varchar("complement", { length: 100 }), neighborhood: varchar("neighborhood", { length: 100 }), city: varchar("city", { length: 100 }), state: varchar("state", { length: 2 }), zipCode: varchar("zip_code", { length: 10 }), country: varchar("country", { length: 50 }).default("Brasil"), birthDate: date("birth_date"), gender: varchar("gender", { length: 20 }), // male, female, other, not_specified notes: text("notes"), photoUrl: text("photo_url"), isActive: boolean("is_active").default(true), // ERPNext Sync Fields erpnextCustomerId: varchar("erpnext_customer_id", { length: 140 }), erpnextSupplierId: varchar("erpnext_supplier_id", { length: 140 }), erpnextEmployeeId: varchar("erpnext_employee_id", { length: 140 }), // Plus Sync Fields plusClienteId: integer("plus_cliente_id"), plusFornecedorId: integer("plus_fornecedor_id"), lastSyncAt: timestamp("last_sync_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Person Roles - Papéis da Pessoa (uma pessoa pode ter múltiplos papéis) export const personRoles = pgTable("person_roles", { id: serial("id").primaryKey(), personId: integer("person_id").references(() => persons.id, { onDelete: "cascade" }).notNull(), roleType: varchar("role_type", { length: 30 }).notNull(), // customer, supplier, employee, technician, partner, sales_rep // Campos específicos por papel - Cliente creditLimit: numeric("credit_limit", { precision: 12, scale: 2 }), paymentTerms: varchar("payment_terms", { length: 50 }), // cash, 7_days, 14_days, 30_days, etc. customerSince: date("customer_since"), // Campos específicos - Fornecedor supplierCode: varchar("supplier_code", { length: 30 }), supplierCategory: varchar("supplier_category", { length: 50 }), // devices, parts, accessories leadTime: integer("lead_time"), // Dias para entrega minOrderValue: numeric("min_order_value", { precision: 12, scale: 2 }), // Campos específicos - Colaborador/Técnico employeeCode: varchar("employee_code", { length: 30 }), department: varchar("department", { length: 50 }), position: varchar("position", { length: 100 }), hireDate: date("hire_date"), terminationDate: date("termination_date"), salary: numeric("salary", { precision: 12, scale: 2 }), commissionRate: numeric("commission_rate", { precision: 5, scale: 2 }), // Campos específicos - Técnico specializations: text("specializations").array(), // ['iphone', 'samsung', 'xiaomi', 'software', 'hardware'] certifications: text("certifications").array(), avgRepairTime: integer("avg_repair_time"), // Minutos qualityScore: numeric("quality_score", { precision: 5, scale: 2 }), // 0-100 // Status e Metadados isActive: boolean("is_active").default(true), metadata: jsonb("metadata").$type>(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // IMEI History - Histórico/Kardex de Movimentações do IMEI export const imeiHistory = pgTable("imei_history", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id), deviceId: integer("device_id").references(() => mobileDevices.id).notNull(), imei: varchar("imei", { length: 20 }).notNull(), action: varchar("action", { length: 50 }).notNull(), // Ações: entry, sale, return, transfer, repair_start, repair_end, trade_in_approved, internal_os_created, stock_entry, quality_check previousStatus: varchar("previous_status", { length: 50 }), newStatus: varchar("new_status", { length: 50 }), previousLocation: varchar("previous_location", { length: 100 }), // Warehouse/Store anterior newLocation: varchar("new_location", { length: 100 }), // Warehouse/Store novo relatedOrderId: integer("related_order_id"), // ID da OS, Venda, Transferência relatedOrderType: varchar("related_order_type", { length: 30 }), // service_order, sale, transfer, evaluation relatedOrderNumber: varchar("related_order_number", { length: 30 }), cost: numeric("cost", { precision: 12, scale: 2 }), // Custo associado à movimentação notes: text("notes"), createdBy: varchar("created_by"), // User ID createdByName: varchar("created_by_name", { length: 200 }), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Schemas e Types - Persons export const insertPersonSchema = createInsertSchema(persons).omit({ id: true, createdAt: true, updatedAt: true }); export type Person = typeof persons.$inferSelect; export type InsertPerson = z.infer; export const insertPersonRoleSchema = createInsertSchema(personRoles).omit({ id: true, createdAt: true, updatedAt: true }); export type PersonRole = typeof personRoles.$inferSelect; export type InsertPersonRole = z.infer; export const insertImeiHistorySchema = createInsertSchema(imeiHistory).omit({ id: true, createdAt: true }); export type ImeiHistory = typeof imeiHistory.$inferSelect; export type InsertImeiHistory = z.infer; // ========== MARKETPLACE DE MÓDULOS ========== // Catálogo de Módulos disponíveis no Arcádia Suite export const marketplaceModules = pgTable("marketplace_modules", { id: serial("id").primaryKey(), code: varchar("code", { length: 50 }).notNull().unique(), // fisco, retail, lms, bi, whatsapp, etc. name: varchar("name", { length: 100 }).notNull(), description: text("description"), longDescription: text("long_description"), category: varchar("category", { length: 50 }).notNull(), // core, business, intelligence, communication, segment icon: varchar("icon", { length: 100 }), // Lucide icon name color: varchar("color", { length: 20 }), // Tailwind color class imageUrl: text("image_url"), price: numeric("price", { precision: 12, scale: 2 }).default("0"), // Preço mensal setupFee: numeric("setup_fee", { precision: 12, scale: 2 }).default("0"), // Taxa de implantação features: text("features").array(), // Lista de funcionalidades dependencies: text("dependencies").array(), // Módulos que este depende route: varchar("route", { length: 100 }), // Rota principal do módulo (ex: /fisco, /lms) apiEndpoint: varchar("api_endpoint", { length: 200 }), // Endpoint da API do módulo version: varchar("version", { length: 20 }).default("1.0.0"), isCore: boolean("is_core").default(false), // Módulos core são obrigatórios isActive: boolean("is_active").default(true), isFeatured: boolean("is_featured").default(false), // Destaque no marketplace sortOrder: integer("sort_order").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Assinaturas de Módulos por Empresa (Tenant) export const moduleSubscriptions = pgTable("module_subscriptions", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }).notNull(), moduleId: integer("module_id").references(() => marketplaceModules.id, { onDelete: "cascade" }).notNull(), status: varchar("status", { length: 20 }).default("active"), // active, suspended, cancelled, trial startDate: date("start_date").notNull(), endDate: date("end_date"), // null = sem data de término trialEndsAt: timestamp("trial_ends_at"), // Se em trial price: numeric("price", { precision: 12, scale: 2 }), // Preço negociado (pode ser diferente do catálogo) discount: numeric("discount", { precision: 5, scale: 2 }).default("0"), // Desconto percentual billingCycle: varchar("billing_cycle", { length: 20 }).default("monthly"), // monthly, yearly, custom autoRenew: boolean("auto_renew").default(true), notes: text("notes"), activatedBy: varchar("activated_by"), // User ID activatedAt: timestamp("activated_at"), cancelledBy: varchar("cancelled_by"), cancelledAt: timestamp("cancelled_at"), cancellationReason: text("cancellation_reason"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Histórico de uso/consumo de módulos (para métricas e billing) export const moduleUsage = pgTable("module_usage", { id: serial("id").primaryKey(), subscriptionId: integer("subscription_id").references(() => moduleSubscriptions.id, { onDelete: "cascade" }).notNull(), tenantId: integer("tenant_id").references(() => tenants.id).notNull(), moduleCode: varchar("module_code", { length: 50 }).notNull(), period: varchar("period", { length: 7 }).notNull(), // YYYY-MM usageCount: integer("usage_count").default(0), // Acessos/execuções apiCalls: integer("api_calls").default(0), storageUsedMb: numeric("storage_used_mb", { precision: 12, scale: 2 }).default("0"), documentsGenerated: integer("documents_generated").default(0), // NFes, relatórios, etc. activeUsers: integer("active_users").default(0), metadata: jsonb("metadata").$type>(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Schemas e Types - Marketplace export const insertMarketplaceModuleSchema = createInsertSchema(marketplaceModules).omit({ id: true, createdAt: true, updatedAt: true }); export type MarketplaceModule = typeof marketplaceModules.$inferSelect; export type InsertMarketplaceModule = z.infer; export const insertModuleSubscriptionSchema = createInsertSchema(moduleSubscriptions).omit({ id: true, createdAt: true, updatedAt: true }); export type ModuleSubscription = typeof moduleSubscriptions.$inferSelect; export type InsertModuleSubscription = z.infer; export const insertModuleUsageSchema = createInsertSchema(moduleUsage).omit({ id: true, createdAt: true }); export type ModuleUsage = typeof moduleUsage.$inferSelect; export type InsertModuleUsage = z.infer; // ========== XOS - Experience Operating System ========== // Contatos XOS (Leads, Clientes, Parceiros) export const xosContacts = pgTable("xos_contacts", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), type: varchar("type", { length: 20 }).default("lead"), // lead, customer, partner, supplier name: varchar("name", { length: 200 }).notNull(), email: varchar("email", { length: 200 }), phone: varchar("phone", { length: 50 }), whatsapp: varchar("whatsapp", { length: 50 }), avatarUrl: text("avatar_url"), company: varchar("company", { length: 200 }), position: varchar("position", { length: 100 }), tags: text("tags").array(), customFields: jsonb("custom_fields").$type>(), leadScore: integer("lead_score").default(0), leadStatus: varchar("lead_status", { length: 30 }).default("new"), // new, contacted, qualified, proposal, negotiation, won, lost source: varchar("source", { length: 50 }), // website, whatsapp, manual, import, referral sourceDetails: text("source_details"), assignedTo: varchar("assigned_to").references(() => users.id), lastContactAt: timestamp("last_contact_at"), address: text("address"), city: varchar("city", { length: 100 }), state: varchar("state", { length: 50 }), country: varchar("country", { length: 50 }).default("Brasil"), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Empresas XOS (Contas B2B) export const xosCompanies = pgTable("xos_companies", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: varchar("name", { length: 200 }).notNull(), tradeName: varchar("trade_name", { length: 200 }), document: varchar("document", { length: 20 }), // CNPJ domain: varchar("domain", { length: 200 }), industry: varchar("industry", { length: 100 }), size: varchar("size", { length: 30 }), // micro, small, medium, large, enterprise employees: integer("employees"), annualRevenue: numeric("annual_revenue", { precision: 18, scale: 2 }), phone: varchar("phone", { length: 50 }), email: varchar("email", { length: 200 }), website: varchar("website", { length: 300 }), address: text("address"), city: varchar("city", { length: 100 }), state: varchar("state", { length: 50 }), country: varchar("country", { length: 50 }).default("Brasil"), logoUrl: text("logo_url"), tags: text("tags").array(), customFields: jsonb("custom_fields").$type>(), assignedTo: varchar("assigned_to").references(() => users.id), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Pipelines de Vendas export const xosPipelines = pgTable("xos_pipelines", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: varchar("name", { length: 100 }).notNull(), description: text("description"), type: varchar("type", { length: 30 }).default("sales"), // sales, support, onboarding, custom isDefault: boolean("is_default").default(false), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Estágios do Pipeline export const xosPipelineStages = pgTable("xos_pipeline_stages", { id: serial("id").primaryKey(), pipelineId: integer("pipeline_id").references(() => xosPipelines.id, { onDelete: "cascade" }).notNull(), name: varchar("name", { length: 100 }).notNull(), color: varchar("color", { length: 20 }), sortOrder: integer("sort_order").default(0), probability: integer("probability").default(0), // 0-100% isWon: boolean("is_won").default(false), isLost: boolean("is_lost").default(false), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Deals/Oportunidades export const xosDeals = pgTable("xos_deals", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), pipelineId: integer("pipeline_id").references(() => xosPipelines.id).notNull(), stageId: integer("stage_id").references(() => xosPipelineStages.id).notNull(), contactId: integer("contact_id").references(() => xosContacts.id), companyId: integer("company_id").references(() => xosCompanies.id), title: varchar("title", { length: 200 }).notNull(), value: numeric("value", { precision: 18, scale: 2 }).default("0"), currency: varchar("currency", { length: 3 }).default("BRL"), expectedCloseDate: date("expected_close_date"), actualCloseDate: date("actual_close_date"), probability: integer("probability").default(0), status: varchar("status", { length: 20 }).default("open"), // open, won, lost lostReason: text("lost_reason"), wonReason: text("won_reason"), assignedTo: varchar("assigned_to").references(() => users.id), tags: text("tags").array(), customFields: jsonb("custom_fields").$type>(), notes: text("notes"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), closedAt: timestamp("closed_at"), }); // Conversas Omnichannel export const xosConversations = pgTable("xos_conversations", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), contactId: integer("contact_id").references(() => xosContacts.id), channel: varchar("channel", { length: 30 }).notNull(), // whatsapp, chat, email, instagram, facebook channelId: varchar("channel_id", { length: 100 }), // ID externo do canal status: varchar("status", { length: 20 }).default("open"), // open, pending, resolved, closed priority: varchar("priority", { length: 20 }).default("normal"), // low, normal, high, urgent subject: varchar("subject", { length: 300 }), assignedTo: varchar("assigned_to").references(() => users.id), tags: text("tags").array(), metadata: jsonb("metadata").$type>(), firstResponseAt: timestamp("first_response_at"), resolvedAt: timestamp("resolved_at"), satisfactionScore: integer("satisfaction_score"), // 1-5 satisfactionComment: text("satisfaction_comment"), messagesCount: integer("messages_count").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Mensagens export const xosMessages = pgTable("xos_messages", { id: serial("id").primaryKey(), conversationId: integer("conversation_id").references(() => xosConversations.id, { onDelete: "cascade" }).notNull(), direction: varchar("direction", { length: 10 }).notNull(), // inbound, outbound senderType: varchar("sender_type", { length: 20 }).notNull(), // contact, user, bot senderId: varchar("sender_id"), // ID do remetente senderName: varchar("sender_name", { length: 200 }), content: text("content"), contentType: varchar("content_type", { length: 30 }).default("text"), // text, image, file, audio, video, location attachments: jsonb("attachments").$type>(), metadata: jsonb("metadata").$type>(), externalId: varchar("external_id", { length: 200 }), // ID no canal externo readAt: timestamp("read_at"), deliveredAt: timestamp("delivered_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Tickets de Suporte export const xosTickets = pgTable("xos_tickets", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), contactId: integer("contact_id").references(() => xosContacts.id), conversationId: integer("conversation_id").references(() => xosConversations.id), ticketNumber: varchar("ticket_number", { length: 20 }).notNull(), subject: varchar("subject", { length: 300 }).notNull(), description: text("description"), category: varchar("category", { length: 100 }), priority: varchar("priority", { length: 20 }).default("normal"), // low, normal, high, urgent status: varchar("status", { length: 30 }).default("open"), // open, pending, in_progress, waiting_customer, resolved, closed assignedTo: varchar("assigned_to").references(() => users.id), tags: text("tags").array(), slaDueAt: timestamp("sla_due_at"), firstResponseAt: timestamp("first_response_at"), resolvedAt: timestamp("resolved_at"), closedAt: timestamp("closed_at"), satisfactionScore: integer("satisfaction_score"), satisfactionComment: text("satisfaction_comment"), customFields: jsonb("custom_fields").$type>(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Campanhas de Marketing export const xosCampaigns = pgTable("xos_campaigns", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: varchar("name", { length: 200 }).notNull(), description: text("description"), type: varchar("type", { length: 30 }).notNull(), // email, sms, whatsapp, push status: varchar("status", { length: 20 }).default("draft"), // draft, scheduled, running, paused, completed, cancelled segmentQuery: jsonb("segment_query").$type>(), // Filtro de contatos templateId: integer("template_id"), content: text("content"), subject: varchar("subject", { length: 300 }), scheduledAt: timestamp("scheduled_at"), startedAt: timestamp("started_at"), completedAt: timestamp("completed_at"), stats: jsonb("stats").$type<{ sent: number; delivered: number; opened: number; clicked: number; bounced: number; unsubscribed: number; }>(), createdBy: varchar("created_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Automações export const xosAutomations = pgTable("xos_automations", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: varchar("name", { length: 200 }).notNull(), description: text("description"), triggerType: varchar("trigger_type", { length: 50 }).notNull(), // contact_created, deal_stage_changed, form_submitted, etc triggerConfig: jsonb("trigger_config").$type>(), actions: jsonb("actions").$type }>>(), conditions: jsonb("conditions").$type>(), isActive: boolean("is_active").default(true), executionCount: integer("execution_count").default(0), lastExecutedAt: timestamp("last_executed_at"), createdBy: varchar("created_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Atividades (Tarefas, Ligações, Reuniões) export const xosActivities = pgTable("xos_activities", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), type: varchar("type", { length: 30 }).notNull(), // task, call, meeting, email, note title: varchar("title", { length: 300 }).notNull(), description: text("description"), status: varchar("status", { length: 20 }).default("pending"), // pending, in_progress, completed, cancelled priority: varchar("priority", { length: 20 }).default("normal"), dueAt: timestamp("due_at"), completedAt: timestamp("completed_at"), contactId: integer("contact_id").references(() => xosContacts.id), companyId: integer("company_id").references(() => xosCompanies.id), dealId: integer("deal_id").references(() => xosDeals.id), assignedTo: varchar("assigned_to").references(() => users.id), createdBy: varchar("created_by").references(() => users.id), outcome: text("outcome"), duration: integer("duration"), // minutos createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // ========== NOVAS TABELAS XOS (Fase 1-3) ========== // Filas de Atendimento export const xosQueues = pgTable("xos_queues", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: varchar("name", { length: 100 }).notNull(), color: varchar("color", { length: 20 }).default("blue"), greetingMessage: text("greeting_message"), outOfHoursMessage: text("out_of_hours_message"), schedules: jsonb("schedules").$type>(), orderPriority: integer("order_priority").default(0), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Usuários por Fila (N:N) export const xosQueueUsers = pgTable("xos_queue_users", { id: serial("id").primaryKey(), queueId: integer("queue_id").references(() => xosQueues.id, { onDelete: "cascade" }).notNull(), userId: varchar("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Notas Internas (visíveis só para equipe) export const xosInternalNotes = pgTable("xos_internal_notes", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), conversationId: integer("conversation_id").references(() => xosConversations.id, { onDelete: "cascade" }), contactId: integer("contact_id").references(() => xosContacts.id, { onDelete: "cascade" }), ticketId: integer("ticket_id").references(() => xosTickets.id, { onDelete: "cascade" }), userId: varchar("user_id").references(() => users.id), userName: varchar("user_name", { length: 200 }), content: text("content").notNull(), isPinned: boolean("is_pinned").default(false), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Mensagens Rápidas (atalhos como /preco, /horario) export const xosQuickMessages = pgTable("xos_quick_messages", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), shortcode: varchar("shortcode", { length: 50 }).notNull(), // /preco, /horario title: varchar("title", { length: 200 }), content: text("content").notNull(), mediaUrl: text("media_url"), mediaType: varchar("media_type", { length: 30 }), // image, file, audio scope: varchar("scope", { length: 20 }).default("company"), // personal, team, company userId: varchar("user_id").references(() => users.id), usageCount: integer("usage_count").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Mensagens Agendadas export const xosScheduledMessages = pgTable("xos_scheduled_messages", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), contactId: integer("contact_id").references(() => xosContacts.id), conversationId: integer("conversation_id").references(() => xosConversations.id), content: text("content").notNull(), mediaUrl: text("media_url"), mediaType: varchar("media_type", { length: 30 }), scheduledAt: timestamp("scheduled_at").notNull(), sentAt: timestamp("sent_at"), status: varchar("status", { length: 20 }).default("pending"), // pending, sent, failed, cancelled errorMessage: text("error_message"), createdBy: varchar("created_by").references(() => users.id), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Tracking de Conversa (tempos e métricas) export const xosConversationTracking = pgTable("xos_conversation_tracking", { id: serial("id").primaryKey(), conversationId: integer("conversation_id").references(() => xosConversations.id, { onDelete: "cascade" }).notNull(), queueId: integer("queue_id").references(() => xosQueues.id), queuedAt: timestamp("queued_at"), firstResponseAt: timestamp("first_response_at"), assignedAt: timestamp("assigned_at"), chatbotEndedAt: timestamp("chatbot_ended_at"), humanStartedAt: timestamp("human_started_at"), resolvedAt: timestamp("resolved_at"), ratedAt: timestamp("rated_at"), ratingScore: integer("rating_score"), // 1-5 ratingComment: text("rating_comment"), totalDurationSeconds: integer("total_duration_seconds"), responseTimeSeconds: integer("response_time_seconds"), }); // Conexões WhatsApp (para multi-número) export const xosWhatsappConnections = pgTable("xos_whatsapp_connections", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: varchar("name", { length: 100 }).notNull(), phoneNumber: varchar("phone_number", { length: 20 }), status: varchar("status", { length: 30 }).default("disconnected"), // connected, disconnected, qr_pending qrCode: text("qr_code"), sessionData: jsonb("session_data"), greetingMessage: text("greeting_message"), farewellMessage: text("farewell_message"), completionMessage: text("completion_message"), ratingMessage: text("rating_message"), outOfHoursMessage: text("out_of_hours_message"), ticketExpiresMinutes: integer("ticket_expires_minutes").default(1440), // 24h inactiveMessage: text("inactive_message"), isDefault: boolean("is_default").default(false), lastSeenAt: timestamp("last_seen_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); // Link WhatsApp <-> Fila export const xosWhatsappQueueLinks = pgTable("xos_whatsapp_queue_links", { id: serial("id").primaryKey(), whatsappId: integer("whatsapp_id").references(() => xosWhatsappConnections.id, { onDelete: "cascade" }).notNull(), queueId: integer("queue_id").references(() => xosQueues.id, { onDelete: "cascade" }).notNull(), }); // Schemas e Types - XOS export const insertXosContactSchema = createInsertSchema(xosContacts).omit({ id: true, createdAt: true, updatedAt: true }); export type XosContact = typeof xosContacts.$inferSelect; export type InsertXosContact = z.infer; export const insertXosCompanySchema = createInsertSchema(xosCompanies).omit({ id: true, createdAt: true, updatedAt: true }); export type XosCompany = typeof xosCompanies.$inferSelect; export type InsertXosCompany = z.infer; export const insertXosPipelineSchema = createInsertSchema(xosPipelines).omit({ id: true, createdAt: true, updatedAt: true }); export type XosPipeline = typeof xosPipelines.$inferSelect; export type InsertXosPipeline = z.infer; export const insertXosPipelineStageSchema = createInsertSchema(xosPipelineStages).omit({ id: true, createdAt: true }); export type XosPipelineStage = typeof xosPipelineStages.$inferSelect; export type InsertXosPipelineStage = z.infer; export const insertXosDealSchema = createInsertSchema(xosDeals).omit({ id: true, createdAt: true, updatedAt: true }); export type XosDeal = typeof xosDeals.$inferSelect; export type InsertXosDeal = z.infer; export const insertXosConversationSchema = createInsertSchema(xosConversations).omit({ id: true, createdAt: true, updatedAt: true }); export type XosConversation = typeof xosConversations.$inferSelect; export type InsertXosConversation = z.infer; export const insertXosMessageSchema = createInsertSchema(xosMessages).omit({ id: true, createdAt: true }); export type XosMessage = typeof xosMessages.$inferSelect; export type InsertXosMessage = z.infer; export const insertXosTicketSchema = createInsertSchema(xosTickets).omit({ id: true, createdAt: true, updatedAt: true }); export type XosTicket = typeof xosTickets.$inferSelect; export type InsertXosTicket = z.infer; export const insertXosCampaignSchema = createInsertSchema(xosCampaigns).omit({ id: true, createdAt: true, updatedAt: true }); export type XosCampaign = typeof xosCampaigns.$inferSelect; export type InsertXosCampaign = z.infer; export const insertXosAutomationSchema = createInsertSchema(xosAutomations).omit({ id: true, createdAt: true, updatedAt: true }); export type XosAutomation = typeof xosAutomations.$inferSelect; export type InsertXosAutomation = z.infer; export const insertXosActivitySchema = createInsertSchema(xosActivities).omit({ id: true, createdAt: true, updatedAt: true }); export type XosActivity = typeof xosActivities.$inferSelect; export type InsertXosActivity = z.infer; // Types - Novas tabelas XOS export const insertXosQueueSchema = createInsertSchema(xosQueues).omit({ id: true, createdAt: true, updatedAt: true }); export type XosQueue = typeof xosQueues.$inferSelect; export type InsertXosQueue = z.infer; export const insertXosInternalNoteSchema = createInsertSchema(xosInternalNotes).omit({ id: true, createdAt: true }); export type XosInternalNote = typeof xosInternalNotes.$inferSelect; export type InsertXosInternalNote = z.infer; export const insertXosQuickMessageSchema = createInsertSchema(xosQuickMessages).omit({ id: true, createdAt: true, updatedAt: true }); export type XosQuickMessage = typeof xosQuickMessages.$inferSelect; export type InsertXosQuickMessage = z.infer; export const insertXosScheduledMessageSchema = createInsertSchema(xosScheduledMessages).omit({ id: true, createdAt: true }); export type XosScheduledMessage = typeof xosScheduledMessages.$inferSelect; export type InsertXosScheduledMessage = z.infer; export type XosConversationTracking = typeof xosConversationTracking.$inferSelect; export type XosWhatsappConnection = typeof xosWhatsappConnections.$inferSelect; // ============================================================ // MÓDULO DE MIGRAÇÃO DE DADOS // ============================================================ export const migrationJobs = pgTable("migration_jobs", { id: serial("id").primaryKey(), name: varchar("name", { length: 200 }).notNull(), sourceType: varchar("source_type", { length: 50 }).notNull(), // mongodb, mysql, csv, json sourceSystem: varchar("source_system", { length: 100 }), // nome do sistema de origem status: varchar("status", { length: 30 }).default("pending"), // pending, analyzing, mapping, importing, completed, failed fileName: varchar("file_name", { length: 500 }), fileSize: integer("file_size"), totalRecords: integer("total_records").default(0), importedRecords: integer("imported_records").default(0), failedRecords: integer("failed_records").default(0), skippedRecords: integer("skipped_records").default(0), analysisResult: jsonb("analysis_result"), // resultado da análise do backup mappingConfig: jsonb("mapping_config"), // configuração de mapeamento de campos importConfig: jsonb("import_config"), // opções de importação errorLog: text("error_log"), storeId: integer("store_id"), tenantId: integer("tenant_id"), createdBy: varchar("created_by", { length: 255 }), startedAt: timestamp("started_at"), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const migrationMappings = pgTable("migration_mappings", { id: serial("id").primaryKey(), jobId: integer("job_id").references(() => migrationJobs.id, { onDelete: "cascade" }).notNull(), sourceEntity: varchar("source_entity", { length: 100 }).notNull(), // ex: DtoPessoa targetEntity: varchar("target_entity", { length: 100 }).notNull(), // ex: customers fieldMappings: jsonb("field_mappings").notNull(), // { "CNPJ_CPF": "cpf_cnpj", "Nome": "name", ... } transformations: jsonb("transformations"), // funções de transformação filters: jsonb("filters"), // filtros a aplicar isEnabled: boolean("is_enabled").default(true), priority: integer("priority").default(0), // ordem de execução recordCount: integer("record_count").default(0), importedCount: integer("imported_count").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const migrationLogs = pgTable("migration_logs", { id: serial("id").primaryKey(), jobId: integer("job_id").references(() => migrationJobs.id, { onDelete: "cascade" }).notNull(), mappingId: integer("mapping_id").references(() => migrationMappings.id, { onDelete: "set null" }), level: varchar("level", { length: 20 }).default("info"), // info, warning, error, success message: text("message").notNull(), sourceId: varchar("source_id", { length: 100 }), // ID original do registro targetId: varchar("target_id", { length: 100 }), // ID do registro criado details: jsonb("details"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const migrationTemplates = pgTable("migration_templates", { id: serial("id").primaryKey(), name: varchar("name", { length: 200 }).notNull(), description: text("description"), sourceType: varchar("source_type", { length: 50 }).notNull(), sourceSystem: varchar("source_system", { length: 100 }), mappings: jsonb("mappings").notNull(), // templates de mapeamento pré-definidos isPublic: boolean("is_public").default(false), usageCount: integer("usage_count").default(0), createdBy: varchar("created_by", { length: 255 }), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertMigrationJobSchema = createInsertSchema(migrationJobs).omit({ id: true, createdAt: true, updatedAt: true }); export type MigrationJob = typeof migrationJobs.$inferSelect; export type InsertMigrationJob = z.infer; export const insertMigrationMappingSchema = createInsertSchema(migrationMappings).omit({ id: true, createdAt: true }); export type MigrationMapping = typeof migrationMappings.$inferSelect; export type InsertMigrationMapping = z.infer; export const insertMigrationLogSchema = createInsertSchema(migrationLogs).omit({ id: true, createdAt: true }); export type MigrationLog = typeof migrationLogs.$inferSelect; export type InsertMigrationLog = z.infer; export const insertMigrationTemplateSchema = createInsertSchema(migrationTemplates).omit({ id: true, createdAt: true, updatedAt: true }); export type MigrationTemplate = typeof migrationTemplates.$inferSelect; export type InsertMigrationTemplate = z.infer; // ============================================================ // BLACKBOARD - SISTEMA DE AGENTES COLABORATIVOS // ============================================================ export const blackboardTasks = pgTable("blackboard_tasks", { id: serial("id").primaryKey(), parentId: integer("parent_id"), type: varchar("type", { length: 50 }).notNull(), // main_task, subtask title: varchar("title", { length: 500 }).notNull(), description: text("description"), status: varchar("status", { length: 30 }).default("pending"), // pending, in_progress, completed, failed, blocked priority: integer("priority").default(5), // 1-10 assignedAgent: varchar("assigned_agent", { length: 100 }), // architect, generator, validator, executor, evolution dependencies: jsonb("dependencies"), // IDs de tarefas que precisam ser concluídas primeiro context: jsonb("context"), // Contexto adicional da tarefa result: jsonb("result"), // Resultado da execução errorMessage: text("error_message"), userId: varchar("user_id", { length: 255 }), startedAt: timestamp("started_at"), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const blackboardArtifacts = pgTable("blackboard_artifacts", { id: serial("id").primaryKey(), taskId: integer("task_id").references(() => blackboardTasks.id, { onDelete: "cascade" }).notNull(), type: varchar("type", { length: 50 }).notNull(), // spec, code, test, doc, config name: varchar("name", { length: 255 }).notNull(), content: text("content"), metadata: jsonb("metadata"), version: integer("version").default(1), createdBy: varchar("created_by", { length: 100 }), // qual agente criou createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const blackboardAgentLogs = pgTable("blackboard_agent_logs", { id: serial("id").primaryKey(), taskId: integer("task_id").references(() => blackboardTasks.id, { onDelete: "cascade" }), agentName: varchar("agent_name", { length: 100 }).notNull(), action: varchar("action", { length: 100 }).notNull(), // thinking, executing, completed, error thought: text("thought"), observation: text("observation"), metadata: jsonb("metadata"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertBlackboardTaskSchema = createInsertSchema(blackboardTasks).omit({ id: true, createdAt: true, updatedAt: true }); export type BlackboardTask = typeof blackboardTasks.$inferSelect; export type InsertBlackboardTask = z.infer; export const insertBlackboardArtifactSchema = createInsertSchema(blackboardArtifacts).omit({ id: true, createdAt: true }); export type BlackboardArtifact = typeof blackboardArtifacts.$inferSelect; export type InsertBlackboardArtifact = z.infer; export const insertBlackboardAgentLogSchema = createInsertSchema(blackboardAgentLogs).omit({ id: true, createdAt: true }); export type BlackboardAgentLog = typeof blackboardAgentLogs.$inferSelect; export type InsertBlackboardAgentLog = z.infer; export const xosContractRegistry = pgTable("xos_contract_registry", { id: serial("id").primaryKey(), name: varchar("name", { length: 255 }).notNull().unique(), action: varchar("action", { length: 255 }).notNull(), description: text("description"), inputSchema: jsonb("input_schema"), outputSchema: jsonb("output_schema"), requiredPermissions: text("required_permissions").array(), category: varchar("category", { length: 100 }), isActive: boolean("is_active").default(true).notNull(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const xosToolRegistry = pgTable("xos_tool_registry", { id: serial("id").primaryKey(), name: varchar("name", { length: 255 }).notNull().unique(), category: varchar("category", { length: 100 }), description: text("description"), paramsSchema: jsonb("params_schema"), version: varchar("version", { length: 50 }).default("1.0.0"), allowedAgents: text("allowed_agents").array(), isActive: boolean("is_active").default(true).notNull(), registeredAt: timestamp("registered_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const xosSkillRegistry = pgTable("xos_skill_registry", { id: serial("id").primaryKey(), name: varchar("name", { length: 255 }).notNull(), version: varchar("version", { length: 50 }).notNull(), description: text("description"), steps: jsonb("steps"), tools: text("tools").array(), inputSchema: jsonb("input_schema"), outputSchema: jsonb("output_schema"), status: varchar("status", { length: 30 }).default("active"), createdBy: varchar("created_by", { length: 100 }), usageCount: integer("usage_count").default(0), successRate: integer("success_rate").default(0), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const xosPolicyRules = pgTable("xos_policy_rules", { id: serial("id").primaryKey(), name: varchar("name", { length: 255 }).notNull(), scope: varchar("scope", { length: 50 }).notNull(), target: varchar("target", { length: 255 }).notNull(), effect: varchar("effect", { length: 10 }).notNull(), conditions: jsonb("conditions"), priority: integer("priority").default(100), description: text("description"), isActive: boolean("is_active").default(true).notNull(), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const xosAuditTrail = pgTable("xos_audit_trail", { id: serial("id").primaryKey(), correlationId: text("correlation_id"), agentName: varchar("agent_name", { length: 100 }).notNull(), action: varchar("action", { length: 255 }).notNull(), target: varchar("target", { length: 500 }), decision: varchar("decision", { length: 30 }).notNull(), justification: text("justification"), input: jsonb("input"), output: jsonb("output"), taskId: integer("task_id"), policyId: integer("policy_id"), metadata: jsonb("metadata"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const insertContractRegistrySchema = createInsertSchema(xosContractRegistry).omit({ id: true, createdAt: true, updatedAt: true }); export type XosContract = typeof xosContractRegistry.$inferSelect; export type InsertXosContract = z.infer; export const insertToolRegistrySchema = createInsertSchema(xosToolRegistry).omit({ id: true, registeredAt: true }); export type XosTool = typeof xosToolRegistry.$inferSelect; export type InsertXosTool = z.infer; export const insertSkillRegistrySchema = createInsertSchema(xosSkillRegistry).omit({ id: true, createdAt: true }); export type XosSkill = typeof xosSkillRegistry.$inferSelect; export type InsertXosSkill = z.infer; export const insertPolicyRuleSchema = createInsertSchema(xosPolicyRules).omit({ id: true, createdAt: true }); export type XosPolicyRule = typeof xosPolicyRules.$inferSelect; export type InsertXosPolicyRule = z.infer; export const insertAuditTrailSchema = createInsertSchema(xosAuditTrail).omit({ id: true, createdAt: true }); export type XosAuditEntry = typeof xosAuditTrail.$inferSelect; export type InsertXosAuditEntry = z.infer; export const xosJobQueue = pgTable("xos_job_queue", { id: serial("id").primaryKey(), type: text("type").notNull(), priority: integer("priority").default(50), status: text("status").notNull().default("pending"), assignedAgent: text("assigned_agent"), payload: jsonb("payload"), result: jsonb("result"), error: text("error"), attempts: integer("attempts").default(0), maxAttempts: integer("max_attempts").default(3), scheduledAt: timestamp("scheduled_at"), startedAt: timestamp("started_at"), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`), createdBy: text("created_by").default("system"), parentJobId: integer("parent_job_id"), metadata: jsonb("metadata"), }); export const insertJobQueueSchema = createInsertSchema(xosJobQueue).omit({ id: true, createdAt: true, startedAt: true, completedAt: true }); export type XosJob = typeof xosJobQueue.$inferSelect; export type InsertXosJob = z.infer; export const xosAgentMetrics = pgTable("xos_agent_metrics", { id: serial("id").primaryKey(), agentName: text("agent_name").notNull(), period: text("period").notNull(), tasksCompleted: integer("tasks_completed").default(0), tasksFailed: integer("tasks_failed").default(0), avgDurationMs: integer("avg_duration_ms"), skillsCreated: integer("skills_created").default(0), policiesTriggered: integer("policies_triggered").default(0), metadata: jsonb("metadata"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`), }); export const insertAgentMetricsSchema = createInsertSchema(xosAgentMetrics).omit({ id: true, createdAt: true }); export type XosAgentMetric = typeof xosAgentMetrics.$inferSelect; export type InsertXosAgentMetric = z.infer; export const xosDevPipelines = pgTable("xos_dev_pipelines", { id: serial("id").primaryKey(), correlationId: text("correlation_id").notNull().default(sql`gen_random_uuid()`), prompt: text("prompt").notNull(), status: text("status").notNull().default("queued"), currentPhase: text("current_phase").default("queued"), mainTaskId: integer("main_task_id"), userId: text("user_id").default("system"), phases: jsonb("phases").$type>(), metadata: jsonb("metadata"), error: text("error"), budget: jsonb("budget").$type<{ maxTokens: number; maxTimeMs: number; maxCalls: number; usedTokens: number; usedTimeMs: number; usedCalls: number; exceeded: boolean }>(), runbook: jsonb("runbook").$type<{ context: string; decisions: Array<{ phase: string; agent: string; decision: string; timestamp: string }>; validations: any; approval: any; deployment: any }>(), startedAt: timestamp("started_at"), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`), }); export const insertDevPipelineSchema = createInsertSchema(xosDevPipelines).omit({ id: true, createdAt: true, startedAt: true, completedAt: true }); export type XosDevPipeline = typeof xosDevPipelines.$inferSelect; export type InsertXosDevPipeline = z.infer; export const xosStagingChanges = pgTable("xos_staging_changes", { id: serial("id").primaryKey(), pipelineId: integer("pipeline_id"), taskId: integer("task_id"), correlationId: text("correlation_id"), filePath: text("file_path").notNull(), content: text("content").notNull(), originalContent: text("original_content"), diff: text("diff"), action: text("action").default("create"), status: text("status").notNull().default("pending"), validationScore: integer("validation_score"), reviewedBy: text("reviewed_by"), reviewedAt: timestamp("reviewed_at"), appliedAt: timestamp("applied_at"), rolledBackAt: timestamp("rolled_back_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`), }); export const insertStagingChangeSchema = createInsertSchema(xosStagingChanges).omit({ id: true, createdAt: true, reviewedAt: true, appliedAt: true }); export type XosStagingChange = typeof xosStagingChanges.$inferSelect; export type InsertXosStagingChange = z.infer; // ======================================================================== // MOTOR DE COMUNICAÇÃO - Tabelas Canônicas (comm_*) // Unifica xos_*, crm_*, whatsapp_*, email_* preservando regras de negócio // ======================================================================== export const commChannels = pgTable("comm_channels", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), type: varchar("type", { length: 30 }).notNull(), // whatsapp, email, chat, instagram, facebook, telegram, sms, phone name: varchar("name", { length: 200 }).notNull(), identifier: varchar("identifier", { length: 200 }), // phone, email address, page id status: varchar("status", { length: 30 }).default("disconnected"), // connected, disconnected, connecting, error config: jsonb("config").$type>(), // channel-specific settings greetingMessage: text("greeting_message"), outOfHoursMessage: text("out_of_hours_message"), schedules: jsonb("schedules").$type>(), sourceRef: varchar("source_ref", { length: 50 }), // "crm_channels:5" or "whatsapp_sessions:1" isActive: boolean("is_active").default(true), lastConnectedAt: timestamp("last_connected_at"), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const commContacts = pgTable("comm_contacts", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), type: varchar("type", { length: 30 }).default("lead"), // lead, customer, partner, supplier name: varchar("name", { length: 200 }).notNull(), email: varchar("email", { length: 200 }), phone: varchar("phone", { length: 50 }), whatsapp: varchar("whatsapp", { length: 50 }), avatarUrl: text("avatar_url"), company: varchar("company", { length: 200 }), tradeName: varchar("trade_name", { length: 200 }), cnpj: varchar("cnpj", { length: 20 }), position: varchar("position", { length: 100 }), website: varchar("website", { length: 300 }), address: text("address"), city: varchar("city", { length: 100 }), state: varchar("state", { length: 50 }), country: varchar("country", { length: 50 }).default("Brasil"), segment: varchar("segment", { length: 100 }), tags: text("tags").array(), customFields: jsonb("custom_fields").$type>(), leadScore: integer("lead_score").default(0), leadStatus: varchar("lead_status", { length: 30 }).default("new"), source: varchar("source", { length: 50 }), sourceDetails: text("source_details"), assignedTo: varchar("assigned_to").references(() => users.id), primaryContactName: varchar("primary_contact_name", { length: 200 }), primaryContactEmail: varchar("primary_contact_email", { length: 200 }), primaryContactPhone: varchar("primary_contact_phone", { length: 50 }), notes: text("notes"), lastContactAt: timestamp("last_contact_at"), convertedAt: timestamp("converted_at"), xosContactId: integer("xos_contact_id"), // ref to xos_contacts.id crmClientId: integer("crm_client_id"), // ref to crm_clients.id crmLeadId: integer("crm_lead_id"), // ref to crm_leads.id createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const commThreads = pgTable("comm_threads", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), contactId: integer("contact_id").references(() => commContacts.id), channelId: integer("channel_id").references(() => commChannels.id), channel: varchar("channel", { length: 30 }).notNull(), // whatsapp, email, chat, etc externalId: varchar("external_id", { length: 200 }), // ID in external channel status: varchar("status", { length: 20 }).default("open"), // open, pending, resolved, closed priority: varchar("priority", { length: 20 }).default("normal"), // low, normal, high, urgent subject: varchar("subject", { length: 300 }), assignedTo: varchar("assigned_to").references(() => users.id), queueId: integer("queue_id"), tags: text("tags").array(), metadata: jsonb("metadata").$type>(), messagesCount: integer("messages_count").default(0), unreadCount: integer("unread_count").default(0), firstResponseAt: timestamp("first_response_at"), lastMessageAt: timestamp("last_message_at"), resolvedAt: timestamp("resolved_at"), satisfactionScore: integer("satisfaction_score"), satisfactionComment: text("satisfaction_comment"), xosConversationId: integer("xos_conversation_id"), // ref to xos_conversations.id crmThreadId: integer("crm_thread_id"), // ref to crm_threads.id whatsappTicketId: integer("whatsapp_ticket_id"), // ref to whatsapp_tickets.id createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const commMessages = pgTable("comm_messages", { id: serial("id").primaryKey(), threadId: integer("thread_id").references(() => commThreads.id, { onDelete: "cascade" }).notNull(), channelId: integer("channel_id").references(() => commChannels.id), direction: varchar("direction", { length: 10 }).notNull(), // inbound, outbound senderType: varchar("sender_type", { length: 20 }).notNull(), // contact, user, bot, system senderId: varchar("sender_id"), senderName: varchar("sender_name", { length: 200 }), content: text("content"), contentType: varchar("content_type", { length: 30 }).default("text"), // text, image, file, audio, video, location, template mediaUrl: text("media_url"), mediaType: varchar("media_type", { length: 30 }), attachments: jsonb("attachments").$type>(), metadata: jsonb("metadata").$type>(), externalId: varchar("external_id", { length: 200 }), status: varchar("status", { length: 20 }).default("sent"), // pending, sent, delivered, read, failed isFromAgent: boolean("is_from_agent").default(false), readAt: timestamp("read_at"), deliveredAt: timestamp("delivered_at"), xosMessageId: integer("xos_message_id"), // ref to xos_messages.id crmMessageId: integer("crm_message_id"), // ref to crm_messages.id whatsappMessageId: integer("whatsapp_message_id"), // ref to whatsapp_messages.id emailMessageId: integer("email_message_id"), // ref to email_messages.id createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const commQueues = pgTable("comm_queues", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), name: varchar("name", { length: 100 }).notNull(), color: varchar("color", { length: 20 }).default("blue"), greetingMessage: text("greeting_message"), outOfHoursMessage: text("out_of_hours_message"), schedules: jsonb("schedules").$type>(), autoAssign: boolean("auto_assign").default(false), assignmentMethod: varchar("assignment_method", { length: 20 }).default("round_robin"), // round_robin, least_busy, manual orderPriority: integer("order_priority").default(0), isActive: boolean("is_active").default(true), xosQueueId: integer("xos_queue_id"), // ref to xos_queues.id createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const commQueueMembers = pgTable("comm_queue_members", { id: serial("id").primaryKey(), queueId: integer("queue_id").references(() => commQueues.id, { onDelete: "cascade" }).notNull(), userId: varchar("user_id").references(() => users.id).notNull(), role: varchar("role", { length: 20 }).default("agent"), // agent, supervisor isAvailable: boolean("is_available").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const commQuickMessages = pgTable("comm_quick_messages", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), shortcode: varchar("shortcode", { length: 50 }).notNull(), title: varchar("title", { length: 200 }), content: text("content").notNull(), mediaUrl: text("media_url"), mediaType: varchar("media_type", { length: 30 }), category: varchar("category", { length: 50 }), scope: varchar("scope", { length: 20 }).default("company"), // personal, team, company userId: varchar("user_id").references(() => users.id), variables: text("variables").array(), // {nome}, {empresa}, {ticket} usageCount: integer("usage_count").default(0), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), updatedAt: timestamp("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(), }); export const commEvents = pgTable("comm_events", { id: serial("id").primaryKey(), tenantId: integer("tenant_id").references(() => tenants.id, { onDelete: "cascade" }), type: varchar("type", { length: 50 }).notNull(), // message_received, message_sent, thread_created, thread_closed, contact_created, ticket_created, sla_breach entityType: varchar("entity_type", { length: 30 }).notNull(), // thread, message, contact, ticket, channel entityId: integer("entity_id").notNull(), data: jsonb("data").$type>(), processedByKg: boolean("processed_by_kg").default(false), // indexed in Knowledge Graph? processedByAgents: boolean("processed_by_agents").default(false), // consumed by agents? createdAt: timestamp("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(), });