arcadiasuite/shared/schema.ts

7147 lines
383 KiB
TypeScript

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<typeof insertProfileSchema>;
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<typeof insertUserSchema>;
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<typeof insertApplicationSchema>;
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<typeof insertUserApplicationSchema>;
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<typeof insertRoleSchema>;
export type Permission = typeof permissions.$inferSelect;
export type InsertPermission = z.infer<typeof insertPermissionSchema>;
export type RolePermission = typeof rolePermissions.$inferSelect;
export type InsertRolePermission = z.infer<typeof insertRolePermissionSchema>;
export type UserRole = typeof userRoles.$inferSelect;
export type InsertUserRole = z.infer<typeof insertUserRoleSchema>;
export type ModuleAccess = typeof moduleAccess.$inferSelect;
export type InsertModuleAccess = z.infer<typeof insertModuleAccessSchema>;
export type ExternalAppPermission = typeof externalAppPermissions.$inferSelect;
export type InsertExternalAppPermission = z.infer<typeof insertExternalAppPermissionSchema>;
// ========== 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<typeof insertWorkspacePageSchema>;
export type PageBlock = typeof pageBlocks.$inferSelect;
export type InsertPageBlock = z.infer<typeof insertPageBlockSchema>;
export type PageLink = typeof pageLinks.$inferSelect;
export type InsertPageLink = z.infer<typeof insertPageLinkSchema>;
export type DashboardWidget = typeof dashboardWidgets.$inferSelect;
export type InsertDashboardWidget = z.infer<typeof insertDashboardWidgetSchema>;
export type QuickNote = typeof quickNotes.$inferSelect;
export type InsertQuickNote = z.infer<typeof insertQuickNoteSchema>;
export type ActivityFeedEntry = typeof activityFeed.$inferSelect;
export type InsertActivityFeedEntry = z.infer<typeof insertActivityFeedSchema>;
export type UserFavorite = typeof userFavorites.$inferSelect;
export type InsertUserFavorite = z.infer<typeof insertUserFavoriteSchema>;
export type CommandHistoryEntry = typeof commandHistory.$inferSelect;
export type InsertCommandHistoryEntry = z.infer<typeof insertCommandHistorySchema>;
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<typeof insertConversationSchema>;
export type Message = typeof messages.$inferSelect;
export type InsertMessage = z.infer<typeof insertMessageSchema>;
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<typeof insertKnowledgeBaseSchema>;
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<typeof insertChatAttachmentSchema>;
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<typeof insertErpConnectionSchema>;
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<typeof insertAgentTaskSchema>;
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<typeof insertTaskExecutionSchema>;
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"),
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<typeof insertChatThreadSchema>;
export type ChatParticipant = typeof chatParticipants.$inferSelect;
export type InsertChatParticipant = z.infer<typeof insertChatParticipantSchema>;
export type ChatMessage = typeof chatMessages.$inferSelect;
export type InsertChatMessage = z.infer<typeof insertChatMessageSchema>;
export type WhatsappSession = typeof whatsappSessions.$inferSelect;
export type InsertWhatsappSession = z.infer<typeof insertWhatsappSessionSchema>;
export type WhatsappContact = typeof whatsappContacts.$inferSelect;
export type InsertWhatsappContact = z.infer<typeof insertWhatsappContactSchema>;
export type WhatsappMessage = typeof whatsappMessages.$inferSelect;
export type InsertWhatsappMessage = z.infer<typeof insertWhatsappMessageSchema>;
export type WhatsappQueue = typeof whatsappQueues.$inferSelect;
export type InsertWhatsappQueue = z.infer<typeof insertWhatsappQueueSchema>;
export type WhatsappTicket = typeof whatsappTickets.$inferSelect;
export type InsertWhatsappTicket = z.infer<typeof insertWhatsappTicketSchema>;
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<typeof insertManusRunSchema>;
export type ManusStep = typeof manusSteps.$inferSelect;
export type InsertManusStep = z.infer<typeof insertManusStepSchema>;
// ========== 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<typeof insertCustomMcpServerSchema>;
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<typeof insertAutomationSchema>;
export type AutomationAction = typeof automationActions.$inferSelect;
export type InsertAutomationAction = z.infer<typeof insertAutomationActionSchema>;
export type AutomationLog = typeof automationLogs.$inferSelect;
export type InsertAutomationLog = z.infer<typeof insertAutomationLogSchema>;
export type ScheduledTask = typeof scheduledTasks.$inferSelect;
export type InsertScheduledTask = z.infer<typeof insertScheduledTaskSchema>;
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<typeof insertDataSourceSchema>;
export type BiDataset = typeof biDatasets.$inferSelect;
export type InsertBiDataset = z.infer<typeof insertBiDatasetSchema>;
export type BiChart = typeof biCharts.$inferSelect;
export type InsertBiChart = z.infer<typeof insertBiChartSchema>;
export type BiDashboard = typeof biDashboards.$inferSelect;
export type InsertBiDashboard = z.infer<typeof insertBiDashboardSchema>;
export type BiDashboardChart = typeof biDashboardCharts.$inferSelect;
export type InsertBiDashboardChart = z.infer<typeof insertBiDashboardChartSchema>;
export type BackupJob = typeof backupJobs.$inferSelect;
export type InsertBackupJob = z.infer<typeof insertBackupJobSchema>;
export type BackupArtifact = typeof backupArtifacts.$inferSelect;
export type InsertBackupArtifact = z.infer<typeof insertBackupArtifactSchema>;
export type StagedTable = typeof stagedTables.$inferSelect;
export type InsertStagedTable = z.infer<typeof insertStagedTableSchema>;
export type StagingMapping = typeof stagingMappings.$inferSelect;
export type InsertStagingMapping = z.infer<typeof insertStagingMappingSchema>;
export type EtlMigrationJob = typeof etlMigrationJobs.$inferSelect;
export type InsertEtlMigrationJob = z.infer<typeof insertEtlMigrationJobSchema>;
// ==========================================
// 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<TenantFeatures>(),
// 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<TenantFeatures>(),
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<typeof insertTenantSchema>;
export type TenantUser = typeof tenantUsers.$inferSelect;
export type InsertTenantUser = z.infer<typeof insertTenantUserSchema>;
export type TenantPlan = typeof tenantPlans.$inferSelect;
export type InsertTenantPlan = z.infer<typeof insertTenantPlanSchema>;
export type PartnerClient = typeof partnerClients.$inferSelect;
export type InsertPartnerClient = z.infer<typeof insertPartnerClientSchema>;
export type PartnerCommission = typeof partnerCommissions.$inferSelect;
export type InsertPartnerCommission = z.infer<typeof insertPartnerCommissionSchema>;
export type TenantEmpresa = typeof tenantEmpresas.$inferSelect;
export type InsertTenantEmpresa = z.infer<typeof insertTenantEmpresaSchema>;
// ==========================================
// 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<any[]>().default([]),
diagramEdges: jsonb("diagram_edges").$type<any[]>().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<typeof insertPcClientSchema>;
export type PcClientContact = typeof pcClientContacts.$inferSelect;
export type InsertPcClientContact = z.infer<typeof insertPcClientContactSchema>;
export type PcProject = typeof pcProjects.$inferSelect;
export type InsertPcProject = z.infer<typeof insertPcProjectSchema>;
export type PcProjectMember = typeof pcProjectMembers.$inferSelect;
export type InsertPcProjectMember = z.infer<typeof insertPcProjectMemberSchema>;
export type PcProjectActivity = typeof pcProjectActivities.$inferSelect;
export type InsertPcProjectActivity = z.infer<typeof insertPcProjectActivitySchema>;
export type PcCanvasBlock = typeof pcCanvasBlocks.$inferSelect;
export type InsertPcCanvasBlock = z.infer<typeof insertPcCanvasBlockSchema>;
export type PcCanvasQuestion = typeof pcCanvasQuestions.$inferSelect;
export type InsertPcCanvasQuestion = z.infer<typeof insertPcCanvasQuestionSchema>;
export type PcCanvasExpectedOutput = typeof pcCanvasExpectedOutputs.$inferSelect;
export type InsertPcCanvasExpectedOutput = z.infer<typeof insertPcCanvasExpectedOutputSchema>;
export type PcCanvasPdcaLink = typeof pcCanvasPdcaLinks.$inferSelect;
export type InsertPcCanvasPdcaLink = z.infer<typeof insertPcCanvasPdcaLinkSchema>;
export type PcCanvasSwotLink = typeof pcCanvasSwotLinks.$inferSelect;
export type InsertPcCanvasSwotLink = z.infer<typeof insertPcCanvasSwotLinkSchema>;
export type PcProcess = typeof pcProcesses.$inferSelect;
export type InsertPcProcess = z.infer<typeof insertPcProcessSchema>;
export type PcProcessStep = typeof pcProcessSteps.$inferSelect;
export type InsertPcProcessStep = z.infer<typeof insertPcProcessStepSchema>;
export type PcSwotAnalysis = typeof pcSwotAnalyses.$inferSelect;
export type InsertPcSwotAnalysis = z.infer<typeof insertPcSwotAnalysisSchema>;
export type PcSwotItem = typeof pcSwotItems.$inferSelect;
export type InsertPcSwotItem = z.infer<typeof insertPcSwotItemSchema>;
export type PcCrmStage = typeof pcCrmStages.$inferSelect;
export type InsertPcCrmStage = z.infer<typeof insertPcCrmStageSchema>;
export type PcCrmLead = typeof pcCrmLeads.$inferSelect;
export type InsertPcCrmLead = z.infer<typeof insertPcCrmLeadSchema>;
export type PcCrmOpportunity = typeof pcCrmOpportunities.$inferSelect;
export type InsertPcCrmOpportunity = z.infer<typeof insertPcCrmOpportunitySchema>;
export type PcCrmActivity = typeof pcCrmActivities.$inferSelect;
export type InsertPcCrmActivity = z.infer<typeof insertPcCrmActivitySchema>;
export type PcDeliverable = typeof pcDeliverables.$inferSelect;
export type InsertPcDeliverable = z.infer<typeof insertPcDeliverableSchema>;
export type PcTask = typeof pcTasks.$inferSelect;
export type InsertPcTask = z.infer<typeof insertPcTaskSchema>;
export type PcPdcaCycle = typeof pcPdcaCycles.$inferSelect;
export type InsertPcPdcaCycle = z.infer<typeof insertPcPdcaCycleSchema>;
export type PcPdcaAction = typeof pcPdcaActions.$inferSelect;
export type InsertPcPdcaAction = z.infer<typeof insertPcPdcaActionSchema>;
export type PcRequirement = typeof pcRequirements.$inferSelect;
export type InsertPcRequirement = z.infer<typeof insertPcRequirementSchema>;
export type PcProjectTeamMember = typeof pcProjectTeamMembers.$inferSelect;
export type InsertPcProjectTeamMember = z.infer<typeof insertPcProjectTeamMemberSchema>;
export type PcProjectTask = typeof pcProjectTasks.$inferSelect;
export type InsertPcProjectTask = z.infer<typeof insertPcProjectTaskSchema>;
export type PcProjectFile = typeof pcProjectFiles.$inferSelect;
export type InsertPcProjectFile = z.infer<typeof insertPcProjectFileSchema>;
export type PcProjectHistory = typeof pcProjectHistory.$inferSelect;
export type InsertPcProjectHistory = z.infer<typeof insertPcProjectHistorySchema>;
// ==========================================
// 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<typeof insertCrmPartnerSchema>;
export type CrmPartnerCertification = typeof crmPartnerCertifications.$inferSelect;
export type InsertCrmPartnerCertification = z.infer<typeof insertCrmPartnerCertificationSchema>;
export type CrmPartnerPerformance = typeof crmPartnerPerformance.$inferSelect;
export type InsertCrmPartnerPerformance = z.infer<typeof insertCrmPartnerPerformanceSchema>;
export type CrmContract = typeof crmContracts.$inferSelect;
export type InsertCrmContract = z.infer<typeof insertCrmContractSchema>;
export type CrmRevenueSchedule = typeof crmRevenueSchedule.$inferSelect;
export type InsertCrmRevenueSchedule = z.infer<typeof insertCrmRevenueScheduleSchema>;
export type CrmCommissionRule = typeof crmCommissionRules.$inferSelect;
export type InsertCrmCommissionRule = z.infer<typeof insertCrmCommissionRuleSchema>;
export type CrmCommission = typeof crmCommissions.$inferSelect;
export type InsertCrmCommission = z.infer<typeof insertCrmCommissionSchema>;
export type CrmChannel = typeof crmChannels.$inferSelect;
export type InsertCrmChannel = z.infer<typeof insertCrmChannelSchema>;
export type CrmThread = typeof crmThreads.$inferSelect;
export type InsertCrmThread = z.infer<typeof insertCrmThreadSchema>;
export type CrmMessage = typeof crmMessages.$inferSelect;
export type InsertCrmMessage = z.infer<typeof insertCrmMessageSchema>;
export type CrmQuickMessage = typeof crmQuickMessages.$inferSelect;
export type InsertCrmQuickMessage = z.infer<typeof insertCrmQuickMessageSchema>;
export type CrmCampaign = typeof crmCampaigns.$inferSelect;
export type InsertCrmCampaign = z.infer<typeof insertCrmCampaignSchema>;
export type CrmCampaignContact = typeof crmCampaignContacts.$inferSelect;
export type InsertCrmCampaignContact = z.infer<typeof insertCrmCampaignContactSchema>;
export type CrmEvent = typeof crmEvents.$inferSelect;
export type InsertCrmEvent = z.infer<typeof insertCrmEventSchema>;
export type CrmGoogleToken = typeof crmGoogleTokens.$inferSelect;
export type InsertCrmGoogleToken = z.infer<typeof insertCrmGoogleTokenSchema>;
export type CrmOpportunityRegistration = typeof crmOpportunityRegistrations.$inferSelect;
export type InsertCrmOpportunityRegistration = z.infer<typeof insertCrmOpportunityRegistrationSchema>;
export type CrmProduct = typeof crmProducts.$inferSelect;
export type InsertCrmProduct = z.infer<typeof insertCrmProductSchema>;
export type CrmClient = typeof crmClients.$inferSelect;
export type InsertCrmClient = z.infer<typeof insertCrmClientSchema>;
export type CrmPipelineStage = typeof crmPipelineStages.$inferSelect;
export type InsertCrmPipelineStage = z.infer<typeof insertCrmPipelineStageSchema>;
export type CrmLead = typeof crmLeads.$inferSelect;
export type InsertCrmLead = z.infer<typeof insertCrmLeadSchema>;
export type CrmOpportunity = typeof crmOpportunities.$inferSelect;
export type InsertCrmOpportunity = z.infer<typeof insertCrmOpportunitySchema>;
export type CrmOpportunityProduct = typeof crmOpportunityProducts.$inferSelect;
export type InsertCrmOpportunityProduct = z.infer<typeof insertCrmOpportunityProductSchema>;
export type CrmProposal = typeof crmProposals.$inferSelect;
export type InsertCrmProposal = z.infer<typeof insertCrmProposalSchema>;
export type CrmProposalItem = typeof crmProposalItems.$inferSelect;
export type InsertCrmProposalItem = z.infer<typeof insertCrmProposalItemSchema>;
export type CrmContractMilestone = typeof crmContractMilestones.$inferSelect;
export type InsertCrmContractMilestone = z.infer<typeof insertCrmContractMilestoneSchema>;
// ========== 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<typeof insertCrmFrappeConnectorSchema>;
export type CrmFrappeMapping = typeof crmFrappeMappings.$inferSelect;
export type InsertCrmFrappeMapping = z.infer<typeof insertCrmFrappeMappingSchema>;
export type CrmSyncLog = typeof crmSyncLogs.$inferSelect;
export type InsertCrmSyncLog = z.infer<typeof insertCrmSyncLogSchema>;
// ==========================================
// 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<typeof insertTenantProductionSettingsSchema>;
// ==========================================
// 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<typeof insertPcSquadSchema>;
export type PcSquadMember = typeof pcSquadMembers.$inferSelect;
export type InsertPcSquadMember = z.infer<typeof insertPcSquadMemberSchema>;
export type PcSprint = typeof pcSprints.$inferSelect;
export type InsertPcSprint = z.infer<typeof insertPcSprintSchema>;
export type PcWorkItem = typeof pcWorkItems.$inferSelect;
export type InsertPcWorkItem = z.infer<typeof insertPcWorkItemSchema>;
export type PcWorkItemComment = typeof pcWorkItemComments.$inferSelect;
export type InsertPcWorkItemComment = z.infer<typeof insertPcWorkItemCommentSchema>;
export type PcTimesheetEntry = typeof pcTimesheetEntries.$inferSelect;
export type InsertPcTimesheetEntry = z.infer<typeof insertPcTimesheetEntrySchema>;
export type PcCollaborator = typeof pcCollaborators.$inferSelect;
export type InsertPcCollaborator = z.infer<typeof insertPcCollaboratorSchema>;
export type SupportTicket = typeof supportTickets.$inferSelect;
export type InsertSupportTicket = z.infer<typeof insertSupportTicketSchema>;
export type SupportConversation = typeof supportConversations.$inferSelect;
export type InsertSupportConversation = z.infer<typeof insertSupportConversationSchema>;
export type SupportKnowledgeBase = typeof supportKnowledgeBase.$inferSelect;
export type InsertSupportKnowledgeBase = z.infer<typeof insertSupportKnowledgeBaseSchema>;
// ==========================================
// 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<string[]>().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<string[]>().default([]),
sectionOptions: jsonb("section_options").$type<Record<string, any>>().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<Record<string, any>>().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<typeof insertPcReportTemplateSchema>;
export type PcReportConfiguration = typeof pcReportConfigurations.$inferSelect;
export type InsertPcReportConfiguration = z.infer<typeof insertPcReportConfigurationSchema>;
export type PcGeneratedReport = typeof pcGeneratedReports.$inferSelect;
export type InsertPcGeneratedReport = z.infer<typeof insertPcGeneratedReportSchema>;
// ==========================================
// 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<typeof insertPcErpModuleSchema>;
export type PcErpRequirement = typeof pcErpRequirements.$inferSelect;
export type InsertPcErpRequirement = z.infer<typeof insertPcErpRequirementSchema>;
export type PcErpParameterizationTopic = typeof pcErpParameterizationTopics.$inferSelect;
export type InsertPcErpParameterizationTopic = z.infer<typeof insertPcErpParameterizationTopicSchema>;
export type PcErpParameterizationItem = typeof pcErpParameterizationItems.$inferSelect;
export type InsertPcErpParameterizationItem = z.infer<typeof insertPcErpParameterizationItemSchema>;
// ==========================================
// 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(),
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"),
currency: text("currency").default("BRL"),
reportUrl: text("report_url"),
notes: text("notes"),
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),
revenue: numeric("revenue"),
grossProfit: numeric("gross_profit"),
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"),
freeCashFlow: numeric("free_cash_flow"),
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(),
});
// Insert Schemas - Valuation
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<typeof insertValuationProjectSchema>;
export type ValuationInput = typeof valuationInputs.$inferSelect;
export type InsertValuationInput = z.infer<typeof insertValuationInputSchema>;
export type ValuationAssumption = typeof valuationAssumptions.$inferSelect;
export type InsertValuationAssumption = z.infer<typeof insertValuationAssumptionSchema>;
export type ValuationCalculation = typeof valuationCalculations.$inferSelect;
export type InsertValuationCalculation = z.infer<typeof insertValuationCalculationSchema>;
export type ValuationMaturityScore = typeof valuationMaturityScores.$inferSelect;
export type InsertValuationMaturityScore = z.infer<typeof insertValuationMaturityScoreSchema>;
export type ValuationCapTableEntry = typeof valuationCapTable.$inferSelect;
export type InsertValuationCapTableEntry = z.infer<typeof insertValuationCapTableSchema>;
export type ValuationTransaction = typeof valuationTransactions.$inferSelect;
export type InsertValuationTransaction = z.infer<typeof insertValuationTransactionSchema>;
export type ValuationDocument = typeof valuationDocuments.$inferSelect;
export type InsertValuationDocument = z.infer<typeof insertValuationDocumentSchema>;
export type ValuationDocumentLog = typeof valuationDocumentLogs.$inferSelect;
export type InsertValuationDocumentLog = z.infer<typeof insertValuationDocumentLogSchema>;
export type ValuationCanvas = typeof valuationCanvas.$inferSelect;
export type InsertValuationCanvas = z.infer<typeof insertValuationCanvasSchema>;
export type ValuationAgentInsight = typeof valuationAgentInsights.$inferSelect;
export type InsertValuationAgentInsight = z.infer<typeof insertValuationAgentInsightSchema>;
// ========== 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<typeof insertValuationChecklistCategorySchema>;
export type ValuationChecklistItem = typeof valuationChecklistItems.$inferSelect;
export type InsertValuationChecklistItem = z.infer<typeof insertValuationChecklistItemSchema>;
export type ValuationChecklistProgress = typeof valuationChecklistProgress.$inferSelect;
export type InsertValuationChecklistProgress = z.infer<typeof insertValuationChecklistProgressSchema>;
export type ValuationChecklistAttachment = typeof valuationChecklistAttachments.$inferSelect;
export type InsertValuationChecklistAttachment = z.infer<typeof insertValuationChecklistAttachmentSchema>;
// ========== 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<typeof insertValuationCategoryWeightSchema>;
export type ValuationSectorBenchmark = typeof valuationSectorBenchmarks.$inferSelect;
export type InsertValuationSectorBenchmark = z.infer<typeof insertValuationSectorBenchmarkSchema>;
export type ValuationSectorScore = typeof valuationSectorScores.$inferSelect;
export type InsertValuationSectorScore = z.infer<typeof insertValuationSectorScoreSchema>;
// ========== 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<typeof insertValuationCanvasBlockSchema>;
export type ValuationCanvasSnapshot = typeof valuationCanvasSnapshots.$inferSelect;
export type InsertValuationCanvasSnapshot = z.infer<typeof insertValuationCanvasSnapshotSchema>;
// ========== 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<typeof insertGraphNodeSchema>;
export type GraphEdge = typeof graphEdges.$inferSelect;
export type InsertGraphEdge = z.infer<typeof insertGraphEdgeSchema>;
// ========== 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<typeof insertLearningEventSchema>;
// ========== 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<typeof insertLearnedInteractionSchema>;
export type LearnedPattern = typeof learnedPatterns.$inferSelect;
export type InsertLearnedPattern = z.infer<typeof insertLearnedPatternSchema>;
export type GeneratedCode = typeof generatedCode.$inferSelect;
export type InsertGeneratedCode = z.infer<typeof insertGeneratedCodeSchema>;
// ==========================================
// 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<typeof insertEmailAccountSchema>;
export type EmailFolder = typeof emailFolders.$inferSelect;
export type InsertEmailFolder = z.infer<typeof insertEmailFolderSchema>;
export type EmailMessage = typeof emailMessages.$inferSelect;
export type InsertEmailMessage = z.infer<typeof insertEmailMessageSchema>;
// ========== 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<Record<string, string[]>>(),
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<Record<string, any>>().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<Record<string, boolean>>(), // 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<typeof insertErpSegmentSchema>;
export const insertErpConfigSchema = createInsertSchema(erpConfig).omit({ id: true, createdAt: true, updatedAt: true });
export type ErpConfig = typeof erpConfig.$inferSelect;
export type InsertErpConfig = z.infer<typeof insertErpConfigSchema>;
// ========== 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<typeof insertDoctypeSchema>;
export type DoctypeField = typeof doctypeFields.$inferSelect;
export type InsertDoctypeField = z.infer<typeof insertDoctypeFieldSchema>;
export type DoctypeRecord = typeof doctypeRecords.$inferSelect;
export type InsertDoctypeRecord = z.infer<typeof insertDoctypeRecordSchema>;
export type Customer = typeof customers.$inferSelect;
export type InsertCustomer = z.infer<typeof insertCustomerSchema>;
export type Supplier = typeof suppliers.$inferSelect;
export type InsertSupplier = z.infer<typeof insertSupplierSchema>;
export type Product = typeof products.$inferSelect;
export type InsertProduct = z.infer<typeof insertProductSchema>;
export type SalesOrder = typeof salesOrders.$inferSelect;
export type InsertSalesOrder = z.infer<typeof insertSalesOrderSchema>;
export type SalesOrderItem = typeof salesOrderItems.$inferSelect;
export type InsertSalesOrderItem = z.infer<typeof insertSalesOrderItemSchema>;
export type PurchaseOrder = typeof purchaseOrders.$inferSelect;
export type InsertPurchaseOrder = z.infer<typeof insertPurchaseOrderSchema>;
export type PurchaseOrderItem = typeof purchaseOrderItems.$inferSelect;
export type InsertPurchaseOrderItem = z.infer<typeof insertPurchaseOrderItemSchema>;
// ========== 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<typeof insertFiscalNcmSchema>;
export type FiscalCest = typeof fiscalCests.$inferSelect;
export type InsertFiscalCest = z.infer<typeof insertFiscalCestSchema>;
export type FiscalCfop = typeof fiscalCfops.$inferSelect;
export type InsertFiscalCfop = z.infer<typeof insertFiscalCfopSchema>;
export type FiscalGrupoTributacao = typeof fiscalGruposTributacao.$inferSelect;
export type InsertFiscalGrupoTributacao = z.infer<typeof insertFiscalGrupoTributacaoSchema>;
export type FiscalNaturezaOperacao = typeof fiscalNaturezaOperacao.$inferSelect;
export type InsertFiscalNaturezaOperacao = z.infer<typeof insertFiscalNaturezaOperacaoSchema>;
export type FiscalCertificado = typeof fiscalCertificados.$inferSelect;
export type InsertFiscalCertificado = z.infer<typeof insertFiscalCertificadoSchema>;
export type FiscalConfiguracao = typeof fiscalConfiguracoes.$inferSelect;
export type InsertFiscalConfiguracao = z.infer<typeof insertFiscalConfiguracaoSchema>;
export type FiscalNota = typeof fiscalNotas.$inferSelect;
export type InsertFiscalNota = z.infer<typeof insertFiscalNotaSchema>;
export type FiscalNotaItem = typeof fiscalNotaItens.$inferSelect;
export type InsertFiscalNotaItem = z.infer<typeof insertFiscalNotaItemSchema>;
export type FiscalEvento = typeof fiscalEventos.$inferSelect;
export type InsertFiscalEvento = z.infer<typeof insertFiscalEventoSchema>;
export type FiscalIbpt = typeof fiscalIbpt.$inferSelect;
export type InsertFiscalIbpt = z.infer<typeof insertFiscalIbptSchema>;
// ========== 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<typeof insertContabilPlanoContasSchema>;
export type ContabilCentroCusto = typeof contabilCentrosCusto.$inferSelect;
export type InsertContabilCentroCusto = z.infer<typeof insertContabilCentroCustoSchema>;
export type ContabilLancamento = typeof contabilLancamentos.$inferSelect;
export type InsertContabilLancamento = z.infer<typeof insertContabilLancamentoSchema>;
export type ContabilPartida = typeof contabilPartidas.$inferSelect;
export type InsertContabilPartida = z.infer<typeof insertContabilPartidaSchema>;
export type ContabilPeriodo = typeof contabilPeriodos.$inferSelect;
export type InsertContabilPeriodo = z.infer<typeof insertContabilPeriodoSchema>;
export type ContabilConfigLancamento = typeof contabilConfigLancamento.$inferSelect;
export type InsertContabilConfigLancamento = z.infer<typeof insertContabilConfigLancamentoSchema>;
export type ContabilSaldo = typeof contabilSaldos.$inferSelect;
export type InsertContabilSaldo = z.infer<typeof insertContabilSaldoSchema>;
// ========== 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<typeof insertPeopleCargoSchema>;
export type PeopleDepartamento = typeof peopleDepartamentos.$inferSelect;
export type InsertPeopleDepartamento = z.infer<typeof insertPeopleDepartamentoSchema>;
export type PeopleFuncionario = typeof peopleFuncionarios.$inferSelect;
export type InsertPeopleFuncionario = z.infer<typeof insertPeopleFuncionarioSchema>;
export type PeopleDependente = typeof peopleDependentes.$inferSelect;
export type InsertPeopleDependente = z.infer<typeof insertPeopleDependenteSchema>;
export type PeopleEventoFolha = typeof peopleEventosFolha.$inferSelect;
export type InsertPeopleEventoFolha = z.infer<typeof insertPeopleEventoFolhaSchema>;
export type PeopleFolhaPagamento = typeof peopleFolhaPagamento.$inferSelect;
export type InsertPeopleFolhaPagamento = z.infer<typeof insertPeopleFolhaPagamentoSchema>;
export type PeopleFolhaItem = typeof peopleFolhaItens.$inferSelect;
export type InsertPeopleFolhaItem = z.infer<typeof insertPeopleFolhaItemSchema>;
export type PeopleFolhaEvento = typeof peopleFolhaEventos.$inferSelect;
export type InsertPeopleFolhaEvento = z.infer<typeof insertPeopleFolhaEventoSchema>;
export type PeopleFerias = typeof peopleFerias.$inferSelect;
export type InsertPeopleFerias = z.infer<typeof insertPeopleFeriasSchema>;
export type PeoplePonto = typeof peoplePonto.$inferSelect;
export type InsertPeoplePonto = z.infer<typeof insertPeoplePontoSchema>;
export type PeopleBeneficio = typeof peopleBeneficios.$inferSelect;
export type InsertPeopleBeneficio = z.infer<typeof insertPeopleBeneficioSchema>;
export type PeopleFuncionarioBeneficio = typeof peopleFuncionarioBeneficios.$inferSelect;
export type InsertPeopleFuncionarioBeneficio = z.infer<typeof insertPeopleFuncionarioBeneficioSchema>;
export type PeopleTabelaCalculo = typeof peopleTabelasCalculo.$inferSelect;
export type InsertPeopleTabelaCalculo = z.infer<typeof insertPeopleTabelaCalculoSchema>;
// ========================================
// 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<typeof insertFinBankAccountSchema>;
export type FinPaymentMethod = typeof finPaymentMethods.$inferSelect;
export type InsertFinPaymentMethod = z.infer<typeof insertFinPaymentMethodSchema>;
export type FinPaymentPlan = typeof finPaymentPlans.$inferSelect;
export type InsertFinPaymentPlan = z.infer<typeof insertFinPaymentPlanSchema>;
export type FinCashFlowCategory = typeof finCashFlowCategories.$inferSelect;
export type InsertFinCashFlowCategory = z.infer<typeof insertFinCashFlowCategorySchema>;
export type FinAccountsPayable = typeof finAccountsPayable.$inferSelect;
export type InsertFinAccountsPayable = z.infer<typeof insertFinAccountsPayableSchema>;
export type FinAccountsReceivable = typeof finAccountsReceivable.$inferSelect;
export type InsertFinAccountsReceivable = z.infer<typeof insertFinAccountsReceivableSchema>;
export type FinTransaction = typeof finTransactions.$inferSelect;
export type InsertFinTransaction = z.infer<typeof insertFinTransactionSchema>;
// ========== 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<typeof insertCommunitySchema>;
export type CommunityChannel = typeof communityChannels.$inferSelect;
export type InsertCommunityChannel = z.infer<typeof insertCommunityChannelSchema>;
export type CommunityMember = typeof communityMembers.$inferSelect;
export type InsertCommunityMember = z.infer<typeof insertCommunityMemberSchema>;
export type CommunityMessage = typeof communityMessages.$inferSelect;
export type InsertCommunityMessage = z.infer<typeof insertCommunityMessageSchema>;
// ========================================
// 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<typeof insertParaProjectSchema>;
export type ParaArea = typeof paraAreas.$inferSelect;
export type InsertParaArea = z.infer<typeof insertParaAreaSchema>;
export type ParaResource = typeof paraResources.$inferSelect;
export type InsertParaResource = z.infer<typeof insertParaResourceSchema>;
export type ParaTask = typeof paraTasks.$inferSelect;
export type InsertParaTask = z.infer<typeof insertParaTaskSchema>;
export type ParaArchive = typeof paraArchive.$inferSelect;
export type InsertParaArchive = z.infer<typeof insertParaArchiveSchema>;
// ========== 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<Array<{ name: string; value: string; unit: string; limit?: string; status?: string }>>(),
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<Record<string, any>>(), // 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<typeof insertQualitySampleSchema>;
export type QualityLabReport = typeof qualityLabReports.$inferSelect;
export type InsertQualityLabReport = z.infer<typeof insertQualityLabReportSchema>;
export type QualityNonConformity = typeof qualityNonConformities.$inferSelect;
export type InsertQualityNonConformity = z.infer<typeof insertQualityNonConformitySchema>;
export type QualityDocument = typeof qualityDocuments.$inferSelect;
export type InsertQualityDocument = z.infer<typeof insertQualityDocumentSchema>;
export type QualityFieldForm = typeof qualityFieldForms.$inferSelect;
export type InsertQualityFieldForm = z.infer<typeof insertQualityFieldFormSchema>;
export type QualityTrainingMatrix = typeof qualityTrainingMatrix.$inferSelect;
export type InsertQualityTrainingMatrix = z.infer<typeof insertQualityTrainingMatrixSchema>;
export type FieldExpense = typeof fieldExpenses.$inferSelect;
export type InsertFieldExpense = z.infer<typeof insertFieldExpenseSchema>;
// ==========================================
// 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<typeof insertEnvironmentalServiceSchema>;
// ==========================================
// 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<any>(), // 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<any>().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<any>(), // 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<any[]>().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<typeof insertArcDocTypeSchema>;
export const insertArcFieldSchema = createInsertSchema(arcFields).omit({ id: true, createdAt: true });
export type ArcField = typeof arcFields.$inferSelect;
export type InsertArcField = z.infer<typeof insertArcFieldSchema>;
export const insertArcPageSchema = createInsertSchema(arcPages).omit({ id: true, createdAt: true, updatedAt: true });
export type ArcPage = typeof arcPages.$inferSelect;
export type InsertArcPage = z.infer<typeof insertArcPageSchema>;
export const insertArcLayoutSchema = createInsertSchema(arcLayouts).omit({ id: true, createdAt: true });
export type ArcLayout = typeof arcLayouts.$inferSelect;
export type InsertArcLayout = z.infer<typeof insertArcLayoutSchema>;
export const insertArcWidgetSchema = createInsertSchema(arcWidgets).omit({ id: true, createdAt: true });
export type ArcWidget = typeof arcWidgets.$inferSelect;
export type InsertArcWidget = z.infer<typeof insertArcWidgetSchema>;
export const insertArcScriptSchema = createInsertSchema(arcScripts).omit({ id: true, createdAt: true, updatedAt: true });
export type ArcScript = typeof arcScripts.$inferSelect;
export type InsertArcScript = z.infer<typeof insertArcScriptSchema>;
export const insertArcWorkflowSchema = createInsertSchema(arcWorkflows).omit({ id: true, createdAt: true, updatedAt: true });
export type ArcWorkflow = typeof arcWorkflows.$inferSelect;
export type InsertArcWorkflow = z.infer<typeof insertArcWorkflowSchema>;
// ========== 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<any>(),
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<any>(), // 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<number[]>(),
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<any>(),
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<any>(),
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<typeof insertRetailStoreSchema>;
export const insertRetailWarehouseSchema = createInsertSchema(retailWarehouses).omit({ id: true, createdAt: true });
export type RetailWarehouse = typeof retailWarehouses.$inferSelect;
export type InsertRetailWarehouse = z.infer<typeof insertRetailWarehouseSchema>;
export const insertMobileDeviceSchema = createInsertSchema(mobileDevices).omit({ id: true, createdAt: true, updatedAt: true });
export type MobileDevice = typeof mobileDevices.$inferSelect;
export type InsertMobileDevice = z.infer<typeof insertMobileDeviceSchema>;
export const insertDeviceEvaluationSchema = createInsertSchema(deviceEvaluations).omit({ id: true, createdAt: true, updatedAt: true });
export type DeviceEvaluation = typeof deviceEvaluations.$inferSelect;
export type InsertDeviceEvaluation = z.infer<typeof insertDeviceEvaluationSchema>;
export const insertServiceOrderSchema = createInsertSchema(serviceOrders).omit({ id: true, createdAt: true, updatedAt: true });
export type ServiceOrder = typeof serviceOrders.$inferSelect;
export type InsertServiceOrder = z.infer<typeof insertServiceOrderSchema>;
export const insertServiceOrderItemSchema = createInsertSchema(serviceOrderItems).omit({ id: true, createdAt: true });
export type ServiceOrderItem = typeof serviceOrderItems.$inferSelect;
export type InsertServiceOrderItem = z.infer<typeof insertServiceOrderItemSchema>;
export const insertPosSessionSchema = createInsertSchema(posSessions).omit({ id: true, createdAt: true });
export type PosSession = typeof posSessions.$inferSelect;
export type InsertPosSession = z.infer<typeof insertPosSessionSchema>;
export const insertPosSaleSchema = createInsertSchema(posSales).omit({ id: true, createdAt: true });
export type PosSale = typeof posSales.$inferSelect;
export type InsertPosSale = z.infer<typeof insertPosSaleSchema>;
export const insertPosSaleItemSchema = createInsertSchema(posSaleItems).omit({ id: true, createdAt: true });
export type PosSaleItem = typeof posSaleItems.$inferSelect;
export type InsertPosSaleItem = z.infer<typeof insertPosSaleItemSchema>;
export const insertPaymentPlanSchema = createInsertSchema(paymentPlans).omit({ id: true, createdAt: true, updatedAt: true });
export type PaymentPlan = typeof paymentPlans.$inferSelect;
export type InsertPaymentPlan = z.infer<typeof insertPaymentPlanSchema>;
export const insertPaymentPlanInstallmentSchema = createInsertSchema(paymentPlanInstallments).omit({ id: true, createdAt: true });
export type PaymentPlanInstallment = typeof paymentPlanInstallments.$inferSelect;
export type InsertPaymentPlanInstallment = z.infer<typeof insertPaymentPlanInstallmentSchema>;
export const insertLeaseAgreementSchema = createInsertSchema(leaseAgreements).omit({ id: true, createdAt: true, updatedAt: true });
export type LeaseAgreement = typeof leaseAgreements.$inferSelect;
export type InsertLeaseAgreement = z.infer<typeof insertLeaseAgreementSchema>;
export const insertLeasePaymentSchema = createInsertSchema(leasePayments).omit({ id: true, createdAt: true });
export type LeasePayment = typeof leasePayments.$inferSelect;
export type InsertLeasePayment = z.infer<typeof insertLeasePaymentSchema>;
export const insertStockTransferSchema = createInsertSchema(stockTransfers).omit({ id: true, createdAt: true, updatedAt: true });
export type StockTransfer = typeof stockTransfers.$inferSelect;
export type InsertStockTransfer = z.infer<typeof insertStockTransferSchema>;
export const insertStockTransferItemSchema = createInsertSchema(stockTransferItems).omit({ id: true, createdAt: true });
export type StockTransferItem = typeof stockTransferItems.$inferSelect;
export type InsertStockTransferItem = z.infer<typeof insertStockTransferItemSchema>;
export const insertReturnExchangeSchema = createInsertSchema(returnExchanges).omit({ id: true, createdAt: true });
export type ReturnExchange = typeof returnExchanges.$inferSelect;
export type InsertReturnExchange = z.infer<typeof insertReturnExchangeSchema>;
export const insertReturnExchangeItemSchema = createInsertSchema(returnExchangeItems).omit({ id: true, createdAt: true });
export type ReturnExchangeItem = typeof returnExchangeItems.$inferSelect;
export type InsertReturnExchangeItem = z.infer<typeof insertReturnExchangeItemSchema>;
export const insertDeviceHistorySchema = createInsertSchema(deviceHistory).omit({ id: true, createdAt: true });
export type DeviceHistory = typeof deviceHistory.$inferSelect;
export type InsertDeviceHistory = z.infer<typeof insertDeviceHistorySchema>;
// 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<typeof insertTradeInChecklistTemplateSchema>;
export const insertTradeInChecklistItemSchema = createInsertSchema(tradeInChecklistItems).omit({ id: true, createdAt: true });
export type TradeInChecklistItem = typeof tradeInChecklistItems.$inferSelect;
export type InsertTradeInChecklistItem = z.infer<typeof insertTradeInChecklistItemSchema>;
export const insertTradeInEvaluationResultSchema = createInsertSchema(tradeInEvaluationResults).omit({ id: true, createdAt: true });
export type TradeInEvaluationResult = typeof tradeInEvaluationResults.$inferSelect;
export type InsertTradeInEvaluationResult = z.infer<typeof insertTradeInEvaluationResultSchema>;
export const insertTradeInTransferDocumentSchema = createInsertSchema(tradeInTransferDocuments).omit({ id: true, createdAt: true, updatedAt: true });
export type TradeInTransferDocument = typeof tradeInTransferDocuments.$inferSelect;
export type InsertTradeInTransferDocument = z.infer<typeof insertTradeInTransferDocumentSchema>;
// 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<typeof insertPosCashMovementSchema>;
// 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<typeof insertServiceWarrantySchema>;
// 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<typeof insertCustomerCreditSchema>;
// ========== 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<Record<string, any>>(),
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<typeof insertPersonSchema>;
export const insertPersonRoleSchema = createInsertSchema(personRoles).omit({ id: true, createdAt: true, updatedAt: true });
export type PersonRole = typeof personRoles.$inferSelect;
export type InsertPersonRole = z.infer<typeof insertPersonRoleSchema>;
export const insertImeiHistorySchema = createInsertSchema(imeiHistory).omit({ id: true, createdAt: true });
export type ImeiHistory = typeof imeiHistory.$inferSelect;
export type InsertImeiHistory = z.infer<typeof insertImeiHistorySchema>;
// ========== 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<Record<string, any>>(),
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<typeof insertMarketplaceModuleSchema>;
export const insertModuleSubscriptionSchema = createInsertSchema(moduleSubscriptions).omit({ id: true, createdAt: true, updatedAt: true });
export type ModuleSubscription = typeof moduleSubscriptions.$inferSelect;
export type InsertModuleSubscription = z.infer<typeof insertModuleSubscriptionSchema>;
export const insertModuleUsageSchema = createInsertSchema(moduleUsage).omit({ id: true, createdAt: true });
export type ModuleUsage = typeof moduleUsage.$inferSelect;
export type InsertModuleUsage = z.infer<typeof insertModuleUsageSchema>;
// ========== 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<Record<string, any>>(),
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<Record<string, any>>(),
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<Record<string, any>>(),
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<Record<string, any>>(),
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<Array<{ url: string; type: string; name: string }>>(),
metadata: jsonb("metadata").$type<Record<string, any>>(),
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<Record<string, any>>(),
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<Record<string, any>>(), // 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<Record<string, any>>(),
actions: jsonb("actions").$type<Array<{ type: string; config: Record<string, any> }>>(),
conditions: jsonb("conditions").$type<Array<{ field: string; operator: string; value: any }>>(),
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<Array<{ dayOfWeek: number; startTime: string; endTime: string }>>(),
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<typeof insertXosContactSchema>;
export const insertXosCompanySchema = createInsertSchema(xosCompanies).omit({ id: true, createdAt: true, updatedAt: true });
export type XosCompany = typeof xosCompanies.$inferSelect;
export type InsertXosCompany = z.infer<typeof insertXosCompanySchema>;
export const insertXosPipelineSchema = createInsertSchema(xosPipelines).omit({ id: true, createdAt: true, updatedAt: true });
export type XosPipeline = typeof xosPipelines.$inferSelect;
export type InsertXosPipeline = z.infer<typeof insertXosPipelineSchema>;
export const insertXosPipelineStageSchema = createInsertSchema(xosPipelineStages).omit({ id: true, createdAt: true });
export type XosPipelineStage = typeof xosPipelineStages.$inferSelect;
export type InsertXosPipelineStage = z.infer<typeof insertXosPipelineStageSchema>;
export const insertXosDealSchema = createInsertSchema(xosDeals).omit({ id: true, createdAt: true, updatedAt: true });
export type XosDeal = typeof xosDeals.$inferSelect;
export type InsertXosDeal = z.infer<typeof insertXosDealSchema>;
export const insertXosConversationSchema = createInsertSchema(xosConversations).omit({ id: true, createdAt: true, updatedAt: true });
export type XosConversation = typeof xosConversations.$inferSelect;
export type InsertXosConversation = z.infer<typeof insertXosConversationSchema>;
export const insertXosMessageSchema = createInsertSchema(xosMessages).omit({ id: true, createdAt: true });
export type XosMessage = typeof xosMessages.$inferSelect;
export type InsertXosMessage = z.infer<typeof insertXosMessageSchema>;
export const insertXosTicketSchema = createInsertSchema(xosTickets).omit({ id: true, createdAt: true, updatedAt: true });
export type XosTicket = typeof xosTickets.$inferSelect;
export type InsertXosTicket = z.infer<typeof insertXosTicketSchema>;
export const insertXosCampaignSchema = createInsertSchema(xosCampaigns).omit({ id: true, createdAt: true, updatedAt: true });
export type XosCampaign = typeof xosCampaigns.$inferSelect;
export type InsertXosCampaign = z.infer<typeof insertXosCampaignSchema>;
export const insertXosAutomationSchema = createInsertSchema(xosAutomations).omit({ id: true, createdAt: true, updatedAt: true });
export type XosAutomation = typeof xosAutomations.$inferSelect;
export type InsertXosAutomation = z.infer<typeof insertXosAutomationSchema>;
export const insertXosActivitySchema = createInsertSchema(xosActivities).omit({ id: true, createdAt: true, updatedAt: true });
export type XosActivity = typeof xosActivities.$inferSelect;
export type InsertXosActivity = z.infer<typeof insertXosActivitySchema>;
// 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<typeof insertXosQueueSchema>;
export const insertXosInternalNoteSchema = createInsertSchema(xosInternalNotes).omit({ id: true, createdAt: true });
export type XosInternalNote = typeof xosInternalNotes.$inferSelect;
export type InsertXosInternalNote = z.infer<typeof insertXosInternalNoteSchema>;
export const insertXosQuickMessageSchema = createInsertSchema(xosQuickMessages).omit({ id: true, createdAt: true, updatedAt: true });
export type XosQuickMessage = typeof xosQuickMessages.$inferSelect;
export type InsertXosQuickMessage = z.infer<typeof insertXosQuickMessageSchema>;
export const insertXosScheduledMessageSchema = createInsertSchema(xosScheduledMessages).omit({ id: true, createdAt: true });
export type XosScheduledMessage = typeof xosScheduledMessages.$inferSelect;
export type InsertXosScheduledMessage = z.infer<typeof insertXosScheduledMessageSchema>;
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<typeof insertMigrationJobSchema>;
export const insertMigrationMappingSchema = createInsertSchema(migrationMappings).omit({ id: true, createdAt: true });
export type MigrationMapping = typeof migrationMappings.$inferSelect;
export type InsertMigrationMapping = z.infer<typeof insertMigrationMappingSchema>;
export const insertMigrationLogSchema = createInsertSchema(migrationLogs).omit({ id: true, createdAt: true });
export type MigrationLog = typeof migrationLogs.$inferSelect;
export type InsertMigrationLog = z.infer<typeof insertMigrationLogSchema>;
export const insertMigrationTemplateSchema = createInsertSchema(migrationTemplates).omit({ id: true, createdAt: true, updatedAt: true });
export type MigrationTemplate = typeof migrationTemplates.$inferSelect;
export type InsertMigrationTemplate = z.infer<typeof insertMigrationTemplateSchema>;
// ============================================================
// 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<typeof insertBlackboardTaskSchema>;
export const insertBlackboardArtifactSchema = createInsertSchema(blackboardArtifacts).omit({ id: true, createdAt: true });
export type BlackboardArtifact = typeof blackboardArtifacts.$inferSelect;
export type InsertBlackboardArtifact = z.infer<typeof insertBlackboardArtifactSchema>;
export const insertBlackboardAgentLogSchema = createInsertSchema(blackboardAgentLogs).omit({ id: true, createdAt: true });
export type BlackboardAgentLog = typeof blackboardAgentLogs.$inferSelect;
export type InsertBlackboardAgentLog = z.infer<typeof insertBlackboardAgentLogSchema>;
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<typeof insertContractRegistrySchema>;
export const insertToolRegistrySchema = createInsertSchema(xosToolRegistry).omit({ id: true, registeredAt: true });
export type XosTool = typeof xosToolRegistry.$inferSelect;
export type InsertXosTool = z.infer<typeof insertToolRegistrySchema>;
export const insertSkillRegistrySchema = createInsertSchema(xosSkillRegistry).omit({ id: true, createdAt: true });
export type XosSkill = typeof xosSkillRegistry.$inferSelect;
export type InsertXosSkill = z.infer<typeof insertSkillRegistrySchema>;
export const insertPolicyRuleSchema = createInsertSchema(xosPolicyRules).omit({ id: true, createdAt: true });
export type XosPolicyRule = typeof xosPolicyRules.$inferSelect;
export type InsertXosPolicyRule = z.infer<typeof insertPolicyRuleSchema>;
export const insertAuditTrailSchema = createInsertSchema(xosAuditTrail).omit({ id: true, createdAt: true });
export type XosAuditEntry = typeof xosAuditTrail.$inferSelect;
export type InsertXosAuditEntry = z.infer<typeof insertAuditTrailSchema>;
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<typeof insertJobQueueSchema>;
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<typeof insertAgentMetricsSchema>;
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<Record<string, { status: string; taskId?: number; startedAt?: string; completedAt?: string; result?: any }>>(),
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<typeof insertDevPipelineSchema>;
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<typeof insertStagingChangeSchema>;
// ========================================================================
// 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<Record<string, any>>(), // channel-specific settings
greetingMessage: text("greeting_message"),
outOfHoursMessage: text("out_of_hours_message"),
schedules: jsonb("schedules").$type<Array<{ dayOfWeek: number; startTime: string; endTime: string }>>(),
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<Record<string, any>>(),
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<Record<string, any>>(),
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<Array<{ url: string; type: string; name: string }>>(),
metadata: jsonb("metadata").$type<Record<string, any>>(),
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<Array<{ dayOfWeek: number; startTime: string; endTime: string }>>(),
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<Record<string, any>>(),
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(),
});