Schema do banco

PostgreSQL via Prisma. Modelos em PascalCase, campos em snake_case (para espelhar os contratos de API). Todo modelo de negócio carrega tenant_id e passa pelo middleware de isolamento: exceto os modelos-filho que herdam o tenant via relação com o pai (ex: AnimalWeightLog por animal_id) e as duas tabelas do painel interno (PlatformUser, SubscriptionStatusLog).

Núcleo multi-tenant

Tenant

Uma empresa cliente. Raiz de todo o isolamento: toda tabela abaixo carrega tenant_id apontando para cá (direta ou indiretamente).

id, name, document (CNPJ/CPF, único), phone, emaildados cadastrais
plancampo | fazenda | grupo
statustrial | active | suspended | canceled
trial_ends_atfim do período de teste grátis (14 dias a partir do cadastro)
lead_source_utm_source/medium/campaignorigem do lead, se veio de campanha

PlatformUser

Equipe da Pleno Digital (painel interno, Módulo 6). NÃO carrega tenant_id: vive inteiramente fora do isolamento multi-tenant, por desenho. Autenticado por uma instância NextAuth própria (cookie e secret diferentes dos de tenant).

id, name, email (único), password_hashcredenciais
roleMASTER_ADMIN | EQUIPE: equipe não vê KPIs financeiros nem executa ações administrativas
activedesativação sem exclusão

TenantProfile

Quais 'perfis' de negócio um tenant tem ativos. Um tenant pode ter fazenda e prestador simultaneamente: não são exclusivos.

tenant_id, profile_typefazenda | prestador
activeperfil pode ser desativado sem perder histórico

User

Uma pessoa com acesso ao painel de um tenant. Email é globalmente único no sistema (não só por tenant): o login recebe apenas email+senha, sem seletor de empresa.

tenant_id, name, email (único), password_hashcredenciais
roleOWNER | ADMIN | OPERADOR | VISUALIZADOR
phoneusado para vincular o WhatsAppContact
activedesativação preserva histórico de ações

Property

Uma propriedade rural ('fazenda') do tenant. Um tenant pode ter várias.

tenant_id, name, address, area_hectaresdados cadastrais
archived_atarquivamento: nunca deletada, para preservar histórico de animais/talhões vinculados

Módulo Rebanho

Animal

Um animal individual, identificado por brinco (ear_tag), único por tenant (não por propriedade).

tenant_id, property_id, ear_tag, breed, sex, birth_datedados cadastrais
statusactive | sold | deceased
current_weightatualizado a cada novo AnimalWeightLog

AnimalWeightLog

Histórico de pesagens de um animal. Usado para calcular o GMD (ganho médio diário) entre as duas últimas pesagens.

tenant_id, animal_id, weight, measured_atuma pesagem

Vaccine

Catálogo de vacinas do tenant. Populado com aftosa, brucelose, raiva e clostridiose ao ativar o perfil fazenda.

tenant_id, name, default_interval_daysintervalo padrão até a próxima dose

AnimalVaccination

Uma aplicação de vacina em um animal. next_due_at é calculado automaticamente (applied_at + intervalo).

tenant_id, animal_id, vaccine_id, applied_at, next_due_ataplicação e próximo vencimento
costopcional: se preenchido, gera uma FinancialEntry de despesa

AnimalMovement

Compra, venda, transferência entre propriedades ou morte de um animal.

tenant_id, animal_id, movement_typepurchase | sale | transfer | death
from_property_id, to_property_idusados em transferência
valueopcional: venda gera receita, compra gera despesa em FinancialEntry

Módulo Lavoura

Plot

Um talhão dentro de uma propriedade.

tenant_id, property_id, name, area_hectares, current_cropdados cadastrais

CropCycle

Um ciclo de plantio→colheita em um talhão. Regra: um talhão só pode ter um ciclo planted/growing ativo por vez.

tenant_id, plot_id, crop_name, planted_at, expected_harvest_atinício do ciclo
statusplanted | growing | harvested
harvested_at, yield_amount, yield_unitpreenchidos ao registrar a colheita

PlotInput

Um insumo aplicado durante um ciclo (fertilizante, defensivo, semente).

tenant_id, cycle_id, input_typefertilizer | pesticide | seed
name, quantity, unit, cost, applied_atdetalhes da aplicação: cost gera FinancialEntry

Módulo Prestador de Serviço

ServiceClient

Um cliente do prestador de serviço (não confundir com o tenant: este é o cliente DO tenant).

tenant_id, name, document, phone, email, notesdados cadastrais

Service

Um tipo de serviço no catálogo do tenant, com precificação.

tenant_id, name, pricing_typehour | day | fixed
unit_priceeditar não afeta ordens já criadas

ServiceOrder

Um serviço prestado a um cliente. total_value = quantity × unit_price do serviço no momento da criação.

tenant_id, service_client_id, service_id, quantity, total_valueo que foi prestado e o valor
statusscheduled → completed → invoiced (sequencial, sem pular etapa)
performed_atinvoiced gera uma FinancialEntry de receita pendente

Financeiro (compartilhado)

FinancialEntry

Um lançamento financeiro: manual ou gerado automaticamente por outro módulo (venda de animal, insumo com custo, ordem faturada).

tenant_id, entry_typeincome | expense
category, amount, due_date, paid_at, notesdetalhes do lançamento
related_module, related_idrebanho | lavoura | servico | geral: origem do lançamento
statuspending | paid | overdue

Agente WhatsApp

WhatsAppContact

Vínculo entre um número de telefone e um User de um tenant. Criado automaticamente no primeiro contato reconhecido.

tenant_id, phone, user_id, last_interaction_atúnico por (tenant_id, phone)

AgentConversationLog

Toda mensagem recebida ou enviada pelo agente. As últimas 5 de cada contato viram contexto de curto prazo enviado ao LLM.

tenant_id, whatsapp_contact_id, directionin | out
message_type, content, intent_detected, action_takenconteúdo e o que foi entendido/feito

Alertas

Alert

Um alerta automático gerado pelo job diário. Idempotente por (related_module + related_id + alert_type): não duplica para o mesmo evento.

tenant_id, alert_typevaccine_due | harvest_near | bill_due | low_balance | trial_ending
related_module, related_id, messageorigem e texto pronto para envio
statuspending | sent | dismissed
scheduled_for, sent_atcontrole de envio via WhatsApp

Cobrança (Asaas)

Subscription

A assinatura recorrente de um tenant no Asaas. Um tenant tem no máximo uma (unique tenant_id).

tenant_id (único), plancampo | fazenda | grupo
asaas_customer_id, asaas_subscription_idIDs no Asaas
statusactive | overdue | canceled
next_due_dateatualizado pelo webhook a cada pagamento confirmado

SubscriptionStatusLog

Histórico de toda transição de status de uma assinatura (Módulo 6): automática (webhook do Asaas) ou manual (master_admin forçando via /plataforma). Sem tenant_id (só consumida pelo painel interno).

subscription_id, from_status, to_statusa transição em si (from_status nulo na primeira linha)
changed_by_platform_user_idnulo = automática (webhook); preenchido = forçada manualmente
reasonmotivo, só preenchido em mudanças manuais