{
  "sistema": {
    "nome": "Sistema Financeiro Trainee Estágios",
    "versao": "1.0.0",
    "descricao": "Sistema completo de gestão financeira e administrativa para empresa de estágios",
    "tecnologias": {
      "backend": "PHP 8.2+",
      "banco_dados": "MySQL 5.7+ / MariaDB 10.3+",
      "frontend": "Bootstrap 5, JavaScript (Vanilla)",
      "bibliotecas": "mPDF (geração de PDFs), Composer (autoload)"
    },
    "arquitetura": "MVC (Model-View-Controller)",
    "padrao_codigo": "PSR-12"
  },
  
  "estrutura_projeto": {
    "diretorios_principais": {
      "src/Controllers": "Controladores que processam requisições e lógica de negócio",
      "src/Models": "Modelos que interagem com o banco de dados",
      "src/Utils": "Utilitários (Database, Security, Session, Router, Logger)",
      "templates": "Views/Templates HTML/PHP",
      "public": "Ponto de entrada da aplicação (index.php e .htaccess)",
      "config": "Configurações (database.php, constants.php)",
      "sql": "Scripts SQL para criação e migração de tabelas"
    },
    "arquivo_entrada": "public/index.php",
    "roteamento": "App\\Utils\\Router (custom router)"
  },

  "funcionalidades_principais": {
    "autenticacao": {
      "descricao": "Sistema de login com hash de senhas (BCRYPT) e controle de sessão",
      "arquivos": ["src/Controllers/AuthController.php", "templates/auth/login.php"],
      "seguranca": ["CSRF tokens", "Prepared statements", "Password hashing"]
    },
    
    "dashboard": {
      "descricao": "Painéis principais com resumos financeiros e estatísticas",
      "tipos": [
        "Dashboard Principal (geral)",
        "Dashboard Financeiro",
        "Dashboard Relatórios",
        "Dashboard Parâmetros"
      ],
      "arquivos": ["src/Controllers/DashboardController.php", "templates/dashboard/index.php"],
      "organizacao": {
        "ordem_blocos": [
          "1. Clientes (Vermelho - gradiente): Alunos, Empresas, Serviços",
          "2. Financeiro (Verde - mesma cor): Contas a Pagar, Contas a Receber, Fornecedores, Contas, Plano de Contas, Parâmetros, Transferência",
          "3. Relatórios (Azul - mesma cor): Contas a Receber, Contas a Pagar, Gerenciais, Vagas Preenchidas, Vendas, Carteira por Consultor, Estagiários Ativos",
          "4. Vagas (Laranja - mesma cor, exceto Agenda): Painel de Vagas, Cadastros Gerais, Agenda de Entrevistas (Amarelo)",
          "5. Documentos (Roxo - gradiente): Cadastros, Contrato de Parceria, TCE, Aditivo, Rescisão, Modelos",
          "6. Configurações (Cinza - mesma cor): Usuários, Níveis de Acesso, Personalizar Login (admin), E-mail (admin), Configurações Financeiras"
        ],
        "layout": {
          "padrao_atual": "LAYOUT PADRÃO DEFINITIVO - NÃO ALTERAR SEM CONSULTAR ESTE DOCUMENTO",
          "grid": "display: grid; grid-auto-flow: column; grid-auto-columns: minmax(150px, 1fr)",
          "comportamento": "Todos os blocos do mesmo grupo ficam na MESMA LINHA (horizontal), independente do tamanho da tela. Não quebra para próxima linha.",
          "overflow": "overflow-x: hidden em html, body, main-wrapper e main-content para evitar rolagem lateral",
          "responsivo": {
            "padrao": "grid-auto-columns: minmax(150px, 1fr)",
            "max-width_1400px": "grid-auto-columns: minmax(130px, 1fr)",
            "max-width_1200px": "grid-auto-columns: minmax(120px, 1fr)",
            "max-width_900px": "grid-auto-columns: minmax(100px, 1fr), gap: 15px, padding: 20px 15px",
            "max-width_600px": "grid-auto-columns: minmax(90px, 1fr), gap: 10px, padding: 15px 10px"
          },
          "espacamento": "gap: 20px, padding: 30px 20px",
          "arquivo_css": "templates/layout/header.php (linhas ~80-120)",
          "nota_importante": "Este layout foi definido como padrão definitivo. Qualquer alteração deve ser documentada aqui."
        },
        "cores": {
          "regra_geral": "Todos os blocos do mesmo grupo devem ter a MESMA cor (exceto gradientes especificados)",
          "clientes": {
            "padrao": "Gradiente vermelho (da esquerda para direita)",
            "block-cliente-1": "Vermelho escuro (#dc2626 → #b91c1c)",
            "block-cliente-2": "Vermelho médio (#ef4444 → #dc2626)",
            "block-cliente-3": "Vermelho claro (#f87171 → #ef4444)"
          },
          "financeiro": {
            "padrao": "Gradiente verde suave (da esquerda para direita: mais forte para mais fraco)",
            "blocos": "Contas a Pagar (block-financeiro-1), Contas a Receber (block-financeiro-2), Fornecedores (block-financeiro-3), Contas (block-financeiro-4), Plano de Contas (block-financeiro-5), Parâmetros (block-financeiro-6), Transferência (block-financeiro-7)",
            "cores_detalhadas": {
              "block-financeiro-1": "#38a169 → #2f855a (verde escuro)",
              "block-financeiro-2": "#48bb78 → #38a169 (verde médio-escuro)",
              "block-financeiro-3": "#52c48a → #48bb78 (verde médio)",
              "block-financeiro-4": "#68d391 → #52c48a (verde médio-claro)",
              "block-financeiro-5": "#7dd3a0 → #68d391 (verde claro)",
              "block-financeiro-6": "#9ae6b4 → #7dd3a0 (verde mais claro)",
              "block-financeiro-7": "#b2f5c8 → #9ae6b4 (verde muito claro)"
            }
          },
          "relatorios": {
            "padrao": "Gradiente azul suave (da esquerda para direita: mais forte para mais fraco)",
            "blocos": "Relatório Contas a Receber (block-relatorio-1), Relatório Contas a Pagar (block-relatorio-2), Gerenciais (block-relatorio-3), Vagas Preenchidas (block-relatorio-4), Vendas (block-relatorio-5), Carteira por Consultor (block-relatorio-6), Estagiários Ativos (block-relatorio-7), Controle de Entrevistas (block-relatorio-8)",
            "cores_detalhadas": {
              "block-relatorio-1": "#3182ce → #2c5282 (azul escuro)",
              "block-relatorio-2": "#4299e1 → #3182ce (azul médio-escuro)",
              "block-relatorio-3": "#4a9ce8 → #4299e1 (azul médio)",
              "block-relatorio-4": "#63b3ed → #4a9ce8 (azul médio-claro)",
              "block-relatorio-5": "#7bc4f0 → #63b3ed (azul claro)",
              "block-relatorio-6": "#90cdf4 → #7bc4f0 (azul mais claro)",
              "block-relatorio-7": "#a8d8f5 → #90cdf4 (azul muito claro)",
              "block-relatorio-8": "#bee3f8 → #a8d8f5 (azul extremamente claro)"
            }
          },
          "vagas": {
            "padrao": "TODOS LARANJA (#ed8936 → #dd6b20), exceto Agenda que é Amarelo",
            "block-vaga": "Laranja (#ed8936 → #dd6b20)",
            "block-cadastros": "Laranja (#ed8936 → #dd6b20)",
            "block-agenda": "Amarelo (#fbbf24 → #f59e0b)"
          },
          "documentos": {
            "padrao": "Gradiente roxo (da esquerda para direita)",
            "block-documento-1": "Roxo escuro (#7c3aed → #6d28d9)",
            "block-documento-2": "Roxo médio-escuro (#8b5cf6 → #7c3aed)",
            "block-documento-3": "Roxo médio (#a78bfa → #8b5cf6)",
            "block-documento-4": "Roxo médio-claro (#c4b5fd → #a78bfa)",
            "block-documento-5": "Roxo médio-claro (#c4b5fd → #a78bfa)",
            "block-documento-6": "Roxo claro (#ddd6fe → #c4b5fd)"
          },
          "configuracoes": {
            "padrao": "Gradiente cinza suave (da esquerda para direita: mais forte para mais fraco)",
            "blocos": "Usuários (block-config-1), Empresas (block-config-2), Níveis de Acesso (block-config-3), Logs (block-config-4), Personalizar Login (block-config-5, admin), E-mail (block-config-6, admin), Configurações Financeiras (block-config-7)",
            "cores_detalhadas": {
              "block-config-1": "#718096 → #4a5568 (cinza escuro)",
              "block-config-2": "#8a9aad → #718096 (cinza médio-escuro)",
              "block-config-3": "#a0aec0 → #8a9aad (cinza médio)",
              "block-config-4": "#b4c0d0 → #a0aec0 (cinza médio-claro)",
              "block-config-5": "#c8d1de → #b4c0d0 (cinza claro)",
              "block-config-6": "#dce2eb → #c8d1de (cinza mais claro)",
              "block-config-7": "#e8edf3 → #dce2eb (cinza muito claro)"
            }
          }
        },
        "permissoes": {
          "clientes": "Verifica permissões de alunos, empresas e serviços",
          "financeiro": "Verifica financeiro.view, create, edit, delete",
          "relatorios": "Verifica relatorios.view, receivables, payables, managerial, vacancies, portfolio, active_interns, entrevistas",
          "vagas": "Verifica vagas.view, create, edit, delete, cadastros.view, vagas.agenda",
          "documentos": "Verifica documentos.view",
          "configuracoes": "Verifica usuarios.view, roles.view, empresas_cliente.view ou role_id = 1 (admin)"
        },
        "arquivo_template": "templates/dashboard/index.php",
        "arquivo_css": "templates/layout/header.php (seção de estilos do dashboard)",
        "data_atualizacao": "2026-01-05",
        "nota_final": "ESTE É O LAYOUT PADRÃO DEFINITIVO. Qualquer alteração no layout, cores ou organização dos blocos deve ser documentada aqui primeiro. Em caso de perda ou alteração acidental, consultar este documento para restaurar o padrão."
      }
    },

    "financeiro": {
      "contas_receber": {
        "descricao": "Gerenciamento completo de contas a receber",
        "funcionalidades": [
          "CRUD completo",
          "Marcar como paga (atualiza saldo automaticamente)",
          "Filtros por status, data e fornecedor",
          "Ações em massa (bulk actions)",
          "Geração de recibo em PDF",
          "Cálculo automático de multa e juros",
          "Campo 'Conta' exibido apenas quando status = 'paid'",
          "Marcar como recebida com valor manual (permite inserir valor diferente do calculado com multa/juros)"
        ],
        "marcar_como_recebida": {
          "descricao": "Sistema permite marcar contas como recebidas com valor manual ou calculado automaticamente",
          "funcionamento": {
            "popup": "Ao marcar conta como recebida, abre popup mostrando valor calculado (com multa/juros)",
            "valor_manual": "Usuário pode alterar o valor no popup para um valor diferente do calculado",
            "prioridade": "Se valor manual for inserido, sistema usa valor manual em vez do calculado",
            "salvamento": "Valor inserido manualmente é salvo no campo 'valor_recebido' do banco de dados",
            "atualizacao_saldo": "Saldo da conta é atualizado com o valor inserido manualmente (não o calculado)",
            "preservacao_valor": "Se usuário alterar valor manualmente, esse valor é preservado mesmo se a data de recebimento for alterada"
          },
          "implementacao": {
            "frontend": "templates/receivables/index.php - Popup com campo de valor editável, preserva valor manual ao alterar data",
            "backend": "src/Controllers/ReceivablesController.php - Métodos markAsPaid() e bulkMarkAsPaid() capturam valor_recebido do POST",
            "model": "src/Models/Receivable.php - Método markAsPaid() aceita parâmetro opcional $valorRecebido",
            "verificacao": "Controller usa isset() e verifica se valor_recebido não é vazio (não usa !empty() para permitir valor 0)"
          },
          "fluxo": [
            "1. Usuário marca conta(s) como recebida(s)",
            "2. Sistema calcula valor com multa/juros e exibe no popup",
            "3. Usuário pode alterar o valor manualmente no campo 'Valor Recebido'",
            "4. Se data de recebimento for alterada após inserir valor manual, valor manual é preservado",
            "5. Ao confirmar, sistema envia valor_recebido via POST",
            "6. Controller captura valor_recebido usando isset() e verifica se não é vazio/null",
            "7. Se valor_recebido foi fornecido, markAsPaid() usa esse valor em vez de calcular",
            "8. Valor inserido manualmente é salvo no banco e usado para atualizar saldo da conta"
          ],
          "observacoes": [
            "Valor manual tem prioridade sobre valor calculado quando fornecido",
            "Se valor_recebido não for fornecido, sistema calcula automaticamente (comportamento padrão)",
            "Mesma funcionalidade disponível para ações individuais e em massa (bulk)",
            "Valor calculado ainda é exibido no log para referência, mas não é usado se valor manual foi fornecido",
            "JavaScript preserva valor manual mesmo quando data de recebimento é alterada (não recalcula automaticamente)",
            "Controller usa isset() em vez de !empty() para capturar valor_recebido, permitindo valores como 0"
          ],
          "correcao_bug": {
            "data": "2025-12-15",
            "problema": "Sistema continuava salvando valor calculado mesmo quando usuário inseriu valor manual diferente",
            "causa": "Controller usava !empty() para verificar valor_recebido, que retorna false para valores como '0' ou string vazia",
            "solucao": "Alterado para usar isset() e verificar explicitamente se valor não é vazio/null, permitindo capturar qualquer valor válido",
            "arquivos_modificados": [
              "src/Controllers/ReceivablesController.php - Métodos markAsPaid() e bulkMarkAsPaid()",
              "src/Models/Receivable.php - Logs adicionais para rastrear uso de valor manual vs calculado",
              "templates/receivables/index.php - Logs de debug e preservação de valor manual ao alterar data"
            ]
          }
        },
        "arquivos": [
          "src/Controllers/ReceivablesController.php",
          "templates/receivables/index.php",
          "templates/receivables/form.php"
        ],
        "observacoes": [
          "Filtro padrão: mês atual (data inicial e final pré-preenchidas)",
          "Campo 'account_name' só aparece quando status = 'paid' e não está vazio",
          "Relatórios de contas a receber consideram valor_recebido quando disponível, senão usam amount"
        ]
      },
      
      "contas_pagar": {
        "descricao": "Gerenciamento completo de contas a pagar",
        "funcionalidades": [
          "CRUD completo",
          "Marcar como paga (atualiza saldo automaticamente)",
          "Filtros por status e data",
          "Ações em massa"
        ],
        "arquivos": [
          "src/Controllers/PayablesController.php",
          "templates/payables/index.php",
          "templates/payables/form.php"
        ]
      },
      
      "fornecedores": {
        "descricao": "Cadastro de fornecedores/clientes",
        "arquivos": [
          "src/Controllers/SuppliersController.php",
          "templates/suppliers/index.php"
        ]
      },
      
      "contas": {
        "descricao": "Gerenciamento de contas bancárias e caixa",
        "tipos": ["bank", "cash"],
        "arquivos": [
          "src/Controllers/AccountsController.php",
          "templates/accounts/index.php"
        ]
      },
      
      "plano_contas": {
        "descricao": "Plano de contas contábil",
        "arquivos": [
          "src/Controllers/ChartOfAccountsController.php",
          "templates/chart_of_accounts/index.php"
        ]
      },
      
      "transferencias": {
        "descricao": "Transferências entre contas",
        "arquivos": [
          "src/Controllers/AccountTransfersController.php",
          "templates/account_transfers/index.php"
        ]
      },
      
      "configuracoes": {
        "descricao": "Configurações de multa e juros por serviço",
        "arquivos": [
          "src/Controllers/FinancialSettingsController.php",
          "templates/financial/settings.php"
        ]
      }
    },

    "clientes": {
      "estudantes": {
        "descricao": "Gerenciamento de estudantes",
        "funcionalidades": [
          "CRUD completo",
          "Perfil do estudante com histórico financeiro",
          "Vendas e parcelas",
          "Notas/avaliações",
          "Ações em massa nas parcelas",
          "Marcar parcelas como recebidas com valor manual (permite inserir valor diferente do calculado com multa/juros)"
        ],
        "marcar_parcela_recebida": {
          "descricao": "Sistema permite marcar parcelas como recebidas com valor manual ou calculado automaticamente",
          "funcionamento": {
            "popup": "Ao marcar parcela como recebida, abre popup mostrando valor calculado (com multa/juros)",
            "valor_manual": "Usuário pode alterar o valor no popup para um valor diferente do calculado",
            "prioridade": "Se valor manual for inserido, sistema usa valor manual em vez do calculado",
            "salvamento": "Valor inserido manualmente é salvo no campo 'valor_recebido' do banco de dados",
            "atualizacao_saldo": "Saldo da conta é atualizado com o valor inserido manualmente (não o calculado)"
          },
          "implementacao": {
            "frontend": "templates/clients/student_profile.php - Popup com campo de valor editável",
            "backend": "src/Controllers/ClientsController.php - Método bulkMarkInstallmentsReceived() captura valor_recebido do POST",
            "model": "src/Models/Receivable.php - Método markAsPaid() aceita parâmetro opcional $valorRecebido"
          },
          "fluxo": [
            "1. Usuário marca parcela(s) como recebida(s)",
            "2. Sistema calcula valor com multa/juros e exibe no popup",
            "3. Usuário pode alterar o valor manualmente no campo 'Valor Recebido'",
            "4. Ao confirmar, sistema envia valor_recebido via POST",
            "5. Controller captura valor_recebido e passa para markAsPaid()",
            "6. Se valor_recebido foi fornecido, markAsPaid() usa esse valor em vez de calcular",
            "7. Valor inserido manualmente é salvo no banco e usado para atualizar saldo da conta"
          ],
          "observacoes": [
            "Valor manual tem prioridade sobre valor calculado quando fornecido",
            "Se valor_recebido não for fornecido, sistema calcula automaticamente (comportamento padrão)",
            "Mesma funcionalidade disponível para empresas (bulkMarkCompanyInstallmentsReceived)",
            "Valor calculado ainda é exibido no log para referência, mas não é usado se valor manual foi fornecido"
          ]
        },
        "arquivos": [
          "src/Controllers/ClientsController.php",
          "templates/clients/students.php",
          "templates/clients/student_profile.php",
          "templates/clients/student_form.php"
        ]
      },
      
      "empresas": {
        "descricao": "Gerenciamento de empresas",
        "funcionalidades": [
          "CRUD completo",
          "Perfil da empresa com histórico financeiro",
          "Vendas e parcelas",
          "Estagiários vinculados",
          "Ações em massa nas parcelas",
          "Geração de recibo em PDF",
          "Marcar parcelas como recebidas com valor manual (permite inserir valor diferente do calculado com multa/juros)"
        ],
        "marcar_parcela_recebida": {
          "descricao": "Mesma funcionalidade de valor manual disponível para empresas",
          "implementacao": "src/Controllers/ClientsController.php - Método bulkMarkCompanyInstallmentsReceived()",
          "observacoes": "Funciona da mesma forma que para alunos - valor manual tem prioridade sobre calculado"
        },
        "arquivos": [
          "src/Controllers/ClientsController.php",
          "templates/clients/companies.php",
          "templates/clients/company_profile.php",
          "templates/clients/company_form.php"
        ]
      },
      
      "servicos": {
        "descricao": "Gerenciamento de serviços oferecidos",
        "funcionalidades": [
          "CRUD completo",
          "Planos de pagamento",
          "Configuração de multa e juros por serviço"
        ],
        "arquivos": [
          "src/Controllers/ClientsController.php",
          "templates/clients/services.php",
          "templates/clients/service_form.php"
        ]
      }
    },

    "relatorios": {
      "contas_receber": {
        "descricao": "Relatório de contas a receber com filtros",
        "funcionalidades": [
          "Filtros por status, data e fornecedor",
          "Exibe valor_recebido quando disponível (valor manual inserido), senão usa amount (valor calculado)"
        ],
        "arquivos": ["src/Controllers/ReportsController.php", "templates/reports/receivables.php"],
        "observacoes": [
          "Relatório considera valor_recebido quando conta foi marcada como recebida com valor manual",
          "Se valor_recebido não estiver disponível, usa amount (valor original da conta)",
          "Mesma lógica aplicada em DRE e Carteira por Consultor"
        ]
      },
      "contas_pagar": {
        "descricao": "Relatório de contas a pagar com filtros",
        "arquivos": ["src/Controllers/ReportsController.php", "templates/reports/payables.php"]
      },
      "gerenciais": {
        "descricao": "Relatórios gerenciais (Extrato e DRE)",
        "arquivos": ["src/Controllers/ReportsController.php", "templates/reports/managerial.php"]
      },
      "vagas": {
        "descricao": "Relatório de vagas preenchidas",
        "filtros": ["Data", "Consultor"],
        "arquivos": ["src/Controllers/ReportsController.php", "templates/reports/vacancies.php"]
      },
      "entrevistas": {
        "descricao": "Relatório de entrevistas agendadas",
        "arquivos": ["src/Controllers/ReportsController.php", "templates/reports/interviews.php"]
      },
      "vendas": {
        "descricao": "Relatório de vendas",
        "observacoes": [
          "Empresas recorrentes: conta como 1 venda",
          "Empresas avulsas: cada R$ 300,00 = 1 venda (apenas valor inteiro)"
        ],
        "arquivos": ["src/Controllers/ReportsController.php", "templates/reports/sales.php"]
      }
    },

    "vagas": {
      "descricao": "Sistema completo de gestão de vagas",
      "funcionalidades": [
        "CRUD completo",
        "Filtros por consultor",
        "Mudança de status",
        "Agenda de entrevistas"
      ],
      "arquivos": [
        "src/Controllers/VacanciesController.php",
        "templates/vacancies/index.php",
        "templates/vacancies/form.php"
      ],
      "observacoes": [
        "Status 'Preenchida' remove a vaga do painel",
        "Status 'Fazendo TCE' fica verde e sobe no painel"
      ]
    },

    "documentos": {
      "tce": {
        "descricao": "Geração e gerenciamento de Termos de Compromisso de Estágio",
        "funcionalidades": [
          "Geração de TCE em PDF",
          "Edição de TCE",
          "Ativação/Cancelamento",
          "Aditivos",
          "Encerramentos",
          "Envio para assinatura digital"
        ],
        "comportamento_assinatura": {
          "mudanca_status_automatica": "Quando TCE é enviado para assinatura, status muda automaticamente de 'rascunho' para 'ativo'",
          "implementacao": "Método startCollection() em SignaturesController atualiza status do TCE para 'ativo' ao criar sessão de assinatura",
          "botoes_automaticos": "Com status 'ativo', botões 'Fazer Aditivo' e 'Cancelar' aparecem automaticamente na visualização",
          "bloqueio_edicao": "TCE enviado para assinatura não pode mais ser editado (verificação de sessão de assinatura existente)"
        },
        "arquivos": [
          "src/Controllers/DocumentsController.php",
          "src/Controllers/SignaturesController.php (método startCollection)",
          "templates/documents/tce.php",
          "templates/documents/view_tce.php",
          "templates/documents/generate_tce.php"
        ]
      },
      "contratos": {
        "descricao": "Geração de contratos a partir de templates",
        "funcionalidades": [
          "Templates personalizáveis",
          "Geração de contratos em PDF",
          "Tags de substituição",
          "Seleção de modelo quando há múltiplos modelos de parceria disponíveis"
        ],
        "contrato_parceria": {
          "tipos": [
            "Contrato de Parceria - Estagiário",
            "Contrato de Parceria - CLT"
          ],
          "selecao_modelo": {
            "descricao": "Quando há múltiplos modelos de parceria cadastrados, o sistema exibe tela de seleção antes de gerar o contrato",
            "comportamento": {
              "um_modelo": "Se houver apenas 1 modelo, vai direto para o formulário de geração",
              "multiplos_modelos": "Se houver múltiplos modelos, exibe tela de seleção com cards mostrando cada modelo",
              "selecao_url": "Se template_id vier na URL, usa o modelo selecionado diretamente"
            },
            "arquivos": [
              "src/Controllers/DocumentsController.php (método generatePartnershipContract)",
              "templates/documents/select_partnership_template.php"
            ]
          },
          "tags_disponiveis": {
            "estagiario": [
              "@@Razão_Social_Empresa",
              "@@CNPJ_Empresa",
              "@@Telefone_Empresa",
              "@@Endereço_Empresa",
              "@@Bairro_Empresa",
              "@@Cidade_Empresa",
              "@@Estado_Empresa",
              "@@CEP_Empresa",
              "@@Email_Empresa",
              "@@dia de vencimento",
              "@@Nome Fantasia",
              "@@Dia_hoje",
              "@@Mês_extenso_hoje",
              "@@Ano_hoje"
            ],
            "clt": [
              "@@Razão_Social_Empresa",
              "@@CNPJ_Empresa",
              "@@Telefone_Empresa",
              "@@Endereço_Empresa",
              "@@Bairro_Empresa",
              "@@Cidade_Empresa",
              "@@Estado_Empresa",
              "@@CEP_Empresa",
              "@@Email_Empresa",
              "@@dia de vencimento",
              "@@Nome Fantasia",
              "@@Dia_hoje",
              "@@Mês_extenso_hoje",
              "@@Ano_hoje"
            ]
          }
        },
        "correcoes_editor_modelos": {
          "data": "2025-12-14",
          "problema": "Linha amarela piscando durante edição de modelos de documentos, impedindo edição e salvamento",
          "causa": {
            "verificacao_overflow_continua": "Sistema verificava overflow de páginas continuamente durante digitação",
            "mutation_observer": "MutationObserver disparava verificações de overflow a cada mudança no DOM",
            "linha_amarela": "Borda amarela (borderBottom) aparecia durante verificação de overflow, causando efeito de 'piscar'"
          },
          "solucao": {
            "remocao_verificacao_digitacao": "Removida verificação automática de overflow durante digitação (event listener 'input')",
            "mutation_observer_apenas_textarea": "MutationObserver agora apenas atualiza textarea, não verifica overflow",
            "verificacao_apenas_necessaria": "Verificação de overflow ocorre apenas quando necessário:",
            "quando_verificar": [
              "Ao colar conteúdo (paste event)",
              "Ao perder foco da página (blur event)",
              "Ao carregar página inicialmente (uma vez, sem mostrar linha amarela)"
            ],
            "remocao_linha_amarela": "Linha amarela removida completamente - verificações ocorrem sem feedback visual",
            "flag_controle": "Adicionada flag isCheckingOverflow para evitar verificações simultâneas",
            "salvamento_garantido": "Formulário atualiza textarea antes de enviar e remove qualquer borda amarela restante"
          },
          "arquivos_modificados": [
            "templates/documents/template_form.php"
          ],
          "funcoes_afetadas": {
            "checkPageOverflow": "Removida exibição de borda amarela, adicionada flag de controle",
            "checkAllPagesOverflow": "Garante remoção de bordas amarelas em todas as páginas",
            "removeEmptyPages": "Remove bordas amarelas ao limpar páginas vazias",
            "createPage": "Removida verificação automática no event listener 'input'",
            "MutationObserver": "Apenas atualiza textarea, não verifica overflow",
            "DOMContentLoaded": "Verificação inicial sem mostrar linha amarela"
          },
          "impacto": "Alto - Corrige bug crítico que impedia edição e salvamento de modelos de documentos",
          "observacoes": [
            "Sistema de divisão automática de páginas continua funcionando, mas sem feedback visual durante edição",
            "Verificação de overflow ainda ocorre quando necessário (colar, blur, carregar), mas de forma silenciosa",
            "Editor agora permite edição fluida sem interrupções visuais",
            "Salvamento funciona corretamente com atualização garantida do textarea antes do envio"
          ]
        },
        "correcao_truncamento_conteudo_modelos": {
          "data": "2025-12-14",
          "problema": "Conteúdo de modelos de documentos sendo truncado ao salvar - apenas primeira página era preservada ao recarregar",
          "sintomas": [
            "Ao salvar documento com 6 páginas (166.210 caracteres), apenas 2 páginas eram carregadas (64.353 caracteres)",
            "Quebras de página sendo perdidas - apenas 1 quebra encontrada em vez de 5",
            "Conteúdo sendo cortado em aproximadamente 65KB",
            "Logs mostravam salvamento correto mas carregamento truncado"
          ],
          "causa_raiz": {
            "tipo_coluna_banco": "Coluna 'conteudo' na tabela document_templates estava como TEXT (limite ~65KB)",
            "limite_text": "TEXT no MySQL tem limite de 65.535 bytes (~65KB)",
            "conteudo_excedente": "Documentos com múltiplas páginas facilmente ultrapassam 65KB",
            "truncamento_silencioso": "MySQL truncava silenciosamente o conteúdo sem erro aparente"
          },
          "solucao_final": {
            "descricao": "Alterar tipo de coluna de TEXT para LONGTEXT para suportar até 4GB de conteúdo",
            "script_sql": "sql/fix_document_templates_conteudo_size.sql",
            "alteracoes": [
              "ALTER TABLE document_templates MODIFY COLUMN conteudo LONGTEXT NOT NULL",
              "ALTER TABLE document_contracts MODIFY COLUMN conteudo_preenchido LONGTEXT NOT NULL",
              "ALTER TABLE tce_contracts MODIFY COLUMN conteudo_preenchido LONGTEXT DEFAULT NULL",
              "ALTER TABLE tce_addendums MODIFY COLUMN conteudo_preenchido LONGTEXT DEFAULT NULL"
            ],
            "tipo_anterior": "TEXT (65.535 bytes ~ 65KB)",
            "tipo_novo": "LONGTEXT (4.294.967.295 bytes ~ 4GB)",
            "impacto": "Permite salvar documentos com centenas de páginas sem truncamento"
          },
          "arquivos_envolvidos": [
            "sql/fix_document_templates_conteudo_size.sql - Script de correção",
            "sql/create_document_templates_tables.sql - Definição original (TEXT)",
            "src/Models/DocumentTemplate.php - Model que salva conteúdo",
            "templates/documents/template_form.php - Editor de modelos"
          ],
          "detecao_problema": {
            "logs_javascript": "Logs mostraram salvamento correto (6 páginas, 5 quebras) mas carregamento truncado (2 páginas, 1 quebra)",
            "comparacao_tamanhos": "Conteúdo salvo: 166.210 caracteres vs Carregado: 64.353 caracteres",
            "investigacao": "Verificação do tipo de coluna no banco revelou limite TEXT",
            "confirmacao": "Script SQL de criação mostrava TEXT em vez de LONGTEXT"
          },
          "prevencao": [
            "SEMPRE usar LONGTEXT para campos que armazenam conteúdo HTML de documentos",
            "NUNCA usar TEXT para campos que podem crescer indefinidamente",
            "Verificar tipo de coluna antes de criar tabelas que armazenam conteúdo HTML",
            "Testar salvamento e carregamento de documentos grandes (múltiplas páginas)",
            "Adicionar logs de tamanho de conteúdo ao salvar e carregar para detectar truncamento",
            "Documentar limites de tipos de dados no info.json",
            "Verificar estrutura de tabelas existentes antes de assumir tipos de dados"
          ],
          "observacoes": [
            "TEXT é suficiente para campos pequenos (nomes, descrições curtas)",
            "LONGTEXT é necessário para conteúdo HTML de documentos que pode ter múltiplas páginas",
            "Truncamento silencioso pode passar despercebido se não houver logs de tamanho",
            "Documentos com formatação HTML do Word podem facilmente ultrapassar 65KB",
            "Solução aplicada também em document_contracts.conteudo_preenchido para consistência",
            "Script SQL deve ser executado em todos os ambientes (desenvolvimento, produção)"
          ],
          "impacto": "Crítico - Corrige problema que impedia salvar documentos com múltiplas páginas, afetando funcionalidade core do sistema"
        },
        "correcao_pagina_branca_editar_modelo": {
          "data": "2025-12-15",
          "problema": "Página ficava completamente em branco ao tentar editar um modelo de documento, sem exibir erros visíveis",
          "sintomas": [
            "Página completamente em branco ao clicar em 'Editar' em um modelo",
            "Menu lateral ficava alterado/desconfigurado",
            "Nenhuma mensagem de erro visível na tela",
            "Console do navegador não mostrava erros JavaScript",
            "Logs do servidor mostravam erro de sintaxe PHP: 'syntax error, unexpected '<', expecting elseif (T_ELSEIF) or else (T_ELSE) or endif (T_ENDIF)'"
          ],
          "causa_raiz": {
            "bloco_if_false": "Bloco de código comentado com '<?php if (false):' na linha 169 continha código HTML/PHP dentro que causava erro de sintaxe",
            "problema_sintaxe": "Mesmo com if (false), o PHP ainda tenta processar código PHP dentro do bloco (como <?php foreach), causando erro de sintaxe",
            "variavel_template": "Variável $template poderia não estar definida ao criar novo modelo, causando erros ao acessar $template['nome']",
            "ordem_use": "Declaração 'use App\\Utils\\Security;' estava após outras declarações PHP, causando problemas de parsing"
          },
          "solucao_final": {
            "remocao_bloco_comentado": "Removido completamente o bloco '<?php if (false):' que continha código HTML/PHP problemático",
            "garantia_template_array": "Adicionada verificação no início do arquivo para garantir que $template sempre seja um array",
            "ordem_use_corrigida": "Movido 'use App\\Utils\\Security;' para o topo do arquivo, logo após <?php",
            "validacao_conteudo": "Adicionada validação no controller para garantir que template tem conteúdo válido antes de renderizar",
            "tratamento_erros": "Adicionado try-catch para capturar erros fatais (\\Exception e \\Error) no controller",
            "validacao_campos": "Garantido que todas as chaves necessárias existem antes de renderizar (nome, tipo, conteudo, id)",
            "textarea_seguro": "Alterado textarea para usar echo em vez de <?= para evitar problemas de sintaxe com conteúdo grande"
          },
          "arquivos_modificados": [
            "templates/documents/template_form.php - Removido bloco if (false), garantia de $template como array, ordem do use corrigida",
            "src/Controllers/DocumentsController.php - Validação de template, tratamento de erros, garantia de campos obrigatórios"
          ],
          "codigo_removido": {
            "antes": "<?php if (false): // Código antigo mantido comentado para referência\n    <div class=\"mb-3\">\n        <?php foreach ($camposDisponiveis['Empresa'] as $campo): ?>\n        ...\n        <?php endforeach; ?>\n    </div>\n<?php endif; ?>",
            "depois": "<!-- Código antigo removido - estava causando erro de sintaxe -->"
          },
          "codigo_adicionado": {
            "inicio_arquivo": "<?php\nuse App\\Utils\\Security;\n\n// Garante que $template sempre seja um array\nif (!isset($template) || !is_array($template)) {\n    $template = [];\n}\n\n$isEdit = !empty($template) && !empty($template['id']);",
            "controller_validacao": "// Verifica se o template tem conteúdo válido\nif (!isset($template['conteudo'])) {\n    Logger::error('Template sem campo conteudo', [...]);\n    Session::set('error', 'Template sem conteúdo válido.');\n    $this->redirect('/public/documents/templates');\n    return;\n}\n\n// Garante que todas as chaves necessárias existem\n$template['nome'] = $template['nome'] ?? '';\n$template['tipo'] = $template['tipo'] ?? '';\n$template['conteudo'] = $template['conteudo'] ?? '';\n$template['id'] = $template['id'] ?? $id;",
            "textarea_seguro": "<?php\n    if (isset($template['conteudo']) && is_string($template['conteudo'])) {\n        echo htmlspecialchars($template['conteudo'], ENT_QUOTES, 'UTF-8');\n    } else {\n        echo '';\n    }\n?>"
          },
          "observacoes": [
            "Blocos 'if (false)' com código HTML/PHP dentro ainda são processados pelo parser PHP, causando erros de sintaxe",
            "Sempre remover código comentado completamente em vez de usar 'if (false)'",
            "Variáveis passadas para views podem não estar definidas - sempre validar e garantir valores padrão",
            "Ordem das declarações PHP importa - 'use' statements devem vir logo após <?php",
            "Erros de sintaxe PHP podem causar página completamente em branco sem mensagens visíveis",
            "Logs do servidor são essenciais para identificar erros de sintaxe que não aparecem na tela",
            "Menu lateral alterado pode indicar que HTML foi quebrado antes de ser renderizado completamente"
          ],
          "prevencao": [
            "NUNCA usar '<?php if (false):' para comentar código - remover completamente ou usar comentários HTML",
            "SEMPRE garantir que variáveis passadas para views existam e tenham valores padrão",
            "SEMPRE colocar 'use' statements logo após <?php, antes de qualquer outro código",
            "SEMPRE validar dados no controller antes de passar para a view",
            "SEMPRE usar try-catch para capturar erros fatais (\\Exception e \\Error)",
            "SEMPRE verificar logs do servidor quando página fica em branco sem erros visíveis",
            "SEMPRE testar criação E edição de modelos para garantir que ambos funcionam",
            "SEMPRE garantir que todas as chaves necessárias existem antes de acessá-las (usar ?? ou isset())",
            "SEMPRE usar echo em vez de <?= para conteúdo grande ou complexo para evitar problemas de sintaxe"
          ],
          "impacto": "Crítico - Corrige problema que impedia completamente a edição de modelos de documentos, funcionalidade core do sistema"
        },
        "arquivos": [
          "src/Controllers/DocumentsController.php",
          "templates/documents/templates.php",
          "templates/documents/template_form.php",
          "templates/documents/generate_contract.php",
          "templates/documents/select_partnership_template.php"
        ]
      },
      "candidatos": {
        "descricao": "Cadastro de candidatos",
        "funcionalidades": [
          "CRUD completo",
          "Cadastro público (formulário externo)"
        ],
        "arquivos": [
          "src/Controllers/DocumentsController.php",
          "templates/documents/candidates.php",
          "templates/documents/public_candidate_registration.php"
        ]
      },
      "instituicoes": {
        "descricao": "Cadastro de instituições de ensino",
        "arquivos": [
          "src/Controllers/DocumentsController.php",
          "templates/documents/educational_institutions.php"
        ]
      },
      "assinaturas_digitais": {
        "descricao": "Sistema completo de coleta de assinaturas digitais para documentos",
        "funcionalidades": [
          "Iniciar coleta de assinaturas para documentos (TCE, Contratos)",
          "Adicionar múltiplos signatários com email e telefone",
          "Posicionamento interativo de assinaturas no documento (estilo Zapsign)",
          "Envio de emails com links únicos para cada signatário",
          "Página pública para assinatura com canvas de desenho",
          "Visualização de documento assinado com assinaturas posicionadas",
          "Download de PDF com assinaturas incorporadas",
          "Rastreamento de status (pending, sent, signed, completed)",
          "Histórico de emails enviados",
          "Controle de assinaturas coletadas (ex: 3/5)"
        ],
        "arquivos": [
          "src/Controllers/SignaturesController.php",
          "src/Models/DocumentSignature.php",
          "src/Models/DocumentSigner.php",
          "src/Utils/Email.php",
          "templates/signatures/configure.php",
          "templates/signatures/manage.php",
          "templates/signatures/sign.php",
          "templates/signatures/view_signed_document.php",
          "templates/documents/tce.php (botão de assinaturas)",
          "templates/documents/partnership_contracts.php (botão de assinaturas)"
        ],
        "tabelas": [
          "document_signatures - Sessões de assinatura",
          "document_signers - Signatários e suas assinaturas",
          "signature_emails - Histórico de emails enviados"
        ],
        "configuracao": {
          "email": "config/email.php - Configuração de SMTP ou mail()",
          "composer": "phpmailer/phpmailer para envio via SMTP"
        },
        "observacoes": [
          "Posicionamento feito pelo configurador (admin) antes de enviar",
          "Signatário apenas desenha a assinatura (não posiciona)",
          "Posições são salvas em porcentagem para responsividade",
          "Sistema converte automaticamente pixels para porcentagem",
          "Assinaturas são exibidas na posição configurada no documento final"
        ]
      }
    },

    "usuarios_permissoes": {
      "descricao": "Sistema de usuários e controle de acesso",
      "funcionalidades": [
        "CRUD de usuários",
        "Níveis de acesso (roles)",
        "Permissões granulares",
        "Campo condicional 'Consultor' (aparece apenas se nível = 'Consultor')"
      ],
      "arquivos": [
        "src/Controllers/UsersController.php",
        "src/Controllers/RolesController.php",
        "templates/users/form.php",
        "templates/roles/index.php"
      ],
      "observacoes": [
        "Campo 'consultor_id' na tabela users (FK para general_registrations)",
        "Validação: se role_id = 'Consultor', consultor_id é obrigatório"
      ]
    }
  },

  "melhores_praticas": {
    "seguranca": {
      "autenticacao": [
        "Sempre usar Security::requireAuth() em rotas protegidas",
        "Verificar permissões com Security::requirePermission() ou Security::hasPermission()",
        "Nunca armazenar senhas em texto plano - sempre usar password_hash() com PASSWORD_BCRYPT"
      ],
      "sql": [
        "SEMPRE usar prepared statements (PDO::prepare)",
        "NUNCA concatenar valores diretamente na query SQL",
        "Usar bindValue() ou bindParam() para parâmetros",
        "Atenção especial quando o mesmo parâmetro aparece múltiplas vezes na query"
      ],
      "csrf": [
        "Sempre validar CSRF token em formulários POST",
        "Usar Session::get('csrf_token') para gerar e validar"
      ],
      "xss": [
        "Sempre usar htmlspecialchars() ou Security::escape() ao exibir dados do banco",
        "Security::formatCurrency() e Security::formatDate() já fazem escape"
      ]
    },

    "banco_dados": {
      "collation": {
        "problema": "Erro 'Illegal mix of collations' quando tabelas têm collations diferentes",
        "solucao": "Usar COLLATE explicitamente nas comparações: campo1 COLLATE utf8mb4_unicode_ci = campo2 COLLATE utf8mb4_unicode_ci",
        "padrao": "Todas as tabelas devem usar utf8mb4_unicode_ci",
        "exemplo": "LEFT JOIN general_registrations gr ON comp.consultor COLLATE utf8mb4_unicode_ci = gr.value COLLATE utf8mb4_unicode_ci"
      },
      "parametros_pdo": {
        "problema": "SQLSTATE[HY093]: Invalid parameter number quando o mesmo parâmetro aparece múltiplas vezes",
        "solucao": "Usar nomes únicos para cada ocorrência (ex: :start_date, :start_date2, :start_date3) e fazer bindValue() individual",
        "exemplo_correto": [
          "$stmt->bindValue(':start_date', $startDate, PDO::PARAM_STR);",
          "$stmt->bindValue(':start_date2', $startDate, PDO::PARAM_STR);",
          "$stmt->bindValue(':start_date3', $startDate, PDO::PARAM_STR);",
          "$stmt->execute();"
        ],
        "exemplo_errado": [
          "$stmt->execute(['start_date' => $startDate, 'end_date' => $endDate]);",
          "// Isso falha se :start_date aparece múltiplas vezes na query"
        ]
      },
      "transacoes": [
        "Usar transações para operações que envolvem múltiplas queries relacionadas",
        "Exemplo: marcar conta como paga + atualizar saldo da conta"
      ]
    },

    "codigo": {
      "estrutura": [
        "Seguir padrão MVC rigorosamente",
        "Controllers: apenas lógica de negócio e orquestração",
        "Models: apenas interação com banco de dados",
        "Views: apenas apresentação (evitar lógica complexa)"
      ],
      "nomenclatura": [
        "Controllers: NomeController (ex: ReceivablesController)",
        "Models: Nome (ex: Receivable)",
        "Métodos: camelCase (ex: markAsPaid)",
        "Tabelas: snake_case (ex: accounts_receivable)"
      ],
      "tratamento_erros": [
        "Sempre usar try-catch em operações de banco de dados",
        "Logar erros com Logger::error() incluindo contexto",
        "Exibir mensagens amigáveis ao usuário (Session::set('error'))",
        "Nunca expor detalhes técnicos de erros ao usuário final"
      ],
      "logging": [
        "Usar Logger::info() para operações importantes",
        "Usar Logger::error() para erros com contexto completo",
        "Incluir dados relevantes no contexto (SQL, parâmetros, etc.)"
      ]
    },

    "frontend": {
      "bootstrap": [
        "Usar classes Bootstrap 5 para layout e componentes",
        "Manter consistência visual em todas as páginas"
      ],
      "javascript": [
        "Evitar jQuery - usar JavaScript vanilla",
        "Validar formulários no frontend E backend",
        "Usar eventos adequados (submit, click, change)"
      ],
      "formularios": [
        "Sempre incluir campo hidden com CSRF token",
        "Validar campos obrigatórios",
        "Feedback visual para ações (success/error messages)"
      ]
    }
  },

  "problemas_comuns_solucoes": {
    "erro_collation": {
      "erro": "SQLSTATE[HY000]: General error: 1267 Illegal mix of collations",
      "causa": "Tabelas com collations diferentes (utf8mb4_general_ci vs utf8mb4_unicode_ci)",
      "solucao": "Adicionar COLLATE utf8mb4_unicode_ci em ambos os lados da comparação",
      "exemplo": "comp.consultor COLLATE utf8mb4_unicode_ci = gr.value COLLATE utf8mb4_unicode_ci"
    },
    
    "erro_parametro_pdo": {
      "erro": "SQLSTATE[HY093]: Invalid parameter number",
      "causa": "Mesmo parâmetro nomeado usado múltiplas vezes na query",
      "solucao": "Usar nomes únicos para cada ocorrência e fazer bindValue() individual",
      "exemplo": {
        "query": "WHERE (date1 BETWEEN :start_date AND :end_date) OR (date2 BETWEEN :start_date2 AND :end_date2)",
        "binding": [
          "$stmt->bindValue(':start_date', $startDate, PDO::PARAM_STR);",
          "$stmt->bindValue(':end_date', $endDate, PDO::PARAM_STR);",
          "$stmt->bindValue(':start_date2', $startDate, PDO::PARAM_STR);",
          "$stmt->bindValue(':end_date2', $endDate, PDO::PARAM_STR);",
          "$stmt->execute();"
        ]
      }
    },
    
    "erro_500_geracao_pdf": {
      "erro": "net::ERR_HTTP_RESPONSE_CODE_FAILURE 500 ao gerar PDF",
      "causa": "Erro na geração do HTML ou conversão para PDF",
      "solucao": [
        "Adicionar try-catch completo",
        "Verificar se todas as variáveis estão definidas",
        "Usar Logger para rastrear o erro",
        "Validar dados antes de gerar PDF"
      ]
    },
    
    "campo_nao_exibido": {
      "problema": "Campo condicional não aparece quando deveria",
      "causa": "Lógica condicional incorreta ou JavaScript não executando",
      "solucao": [
        "Verificar se o JavaScript está carregado",
        "Verificar se o evento está sendo disparado",
        "Usar console.log() para debug",
        "Verificar se a condição está correta (case-sensitive, trim, etc.)"
      ],
      "exemplo": "Campo 'Consultor' só aparece se role selecionado = 'consultor' (lowercase)"
    },
    
    "carregamento_infinito_recebimento": {
      "problema": "Página fica apenas carregando ao tentar receber uma conta a receber",
      "sintomas": [
        "Processo trava em 'Atualizando saldo da conta'",
        "Não aparece 'Saldo atualizado' nos logs",
        "Não aparece 'Transação commitada' nos logs",
        "Não redireciona após processar"
      ],
      "causas_possiveis": [
        "Variável não definida antes de ser usada (ex: $valorRecebido)",
        "Método updateBalance usando conexão diferente da transação",
        "Deadlock no banco de dados",
        "Erro silencioso não capturado",
        "Token CSRF inválido ou ausente",
        "Query SQL com coluna inexistente (ex: cs.numero_parcelas)"
      ],
      "solucao": [
        "Definir todas as variáveis antes de usar (especialmente em logs)",
        "Usar a mesma conexão PDO da transação para todas as operações",
        "Adicionar logs detalhados em cada etapa do processo",
        "Usar try-catch específico para cada operação crítica",
        "Validar token CSRF antes de processar",
        "Verificar se todas as colunas usadas nas queries existem",
        "Usar $this->db diretamente em vez de criar novos models dentro de transações"
      ],
      "exemplo_errado": {
        "codigo": "// ❌ ERRADO: Variável usada antes de ser definida\nLogger::info('Dados', ['valor' => $valorRecebido]);\n$valorRecebido = $_POST['valor_recebido'] ?? null;\n\n// ❌ ERRADO: Novo model pode usar conexão diferente\n$accountModel = new Account();\n$accountModel->updateBalance($id, $amount);"
      },
      "exemplo_correto": {
        "codigo": "// ✅ CORRETO: Define variável antes de usar\n$valorRecebido = !empty($_POST['valor_recebido']) ? (float)$_POST['valor_recebido'] : null;\nLogger::info('Dados', ['valor' => $valorRecebido]);\n\n// ✅ CORRETO: Usa mesma conexão da transação\n$this->db->beginTransaction();\ntry {\n    $stmt = $this->db->prepare('UPDATE accounts SET balance = balance + :amount WHERE id = :id');\n    $stmt->execute(['id' => $id, 'amount' => $amount]);\n    Logger::info('Saldo atualizado', ['rows' => $stmt->rowCount()]);\n    $this->db->commit();\n} catch (\\Exception $e) {\n    if ($this->db->inTransaction()) {\n        $this->db->rollBack();\n    }\n    throw $e;\n}"
      },
      "logs_importantes": [
        "Adicionar log antes de cada operação crítica",
        "Adicionar log após cada operação (com resultado)",
        "Adicionar log antes e depois do commit",
        "Logar todos os dados POST recebidos",
        "Logar erros com trace completo"
      ],
      "arquivos_envolvidos": [
        "src/Controllers/ReceivablesController.php (método markAsPaid)",
        "src/Models/Receivable.php (método markAsPaid)",
        "src/Models/Account.php (método updateBalance)",
        "templates/receivables/index.php (JavaScript submitMarkAsPaid)"
      ],
      "prevencao": [
        "Sempre definir variáveis antes de usar em logs",
        "Usar mesma conexão PDO em transações",
        "Adicionar logs detalhados desde o início",
        "Validar dados de entrada antes de processar",
        "Verificar estrutura de tabelas antes de fazer queries",
        "Testar recebimento após cada mudança"
      ]
    },
    
    "erro_coluna_inexistente": {
      "erro": "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'X' in 'field list'",
      "causa": "Query SQL tentando acessar coluna que não existe na tabela",
      "exemplo_especifico": "cs.numero_parcelas em company_sales (coluna não existe)",
      "solucao": [
        "Verificar estrutura real da tabela antes de fazer query",
        "Usar SHOW COLUMNS ou DESCRIBE para verificar colunas",
        "Remover referências a colunas inexistentes",
        "Usar COUNT(*) em vez de buscar coluna que não existe",
        "Se necessário, calcular valor em vez de buscar da tabela"
      ],
      "exemplo_errado": "SELECT COUNT(*) as total, cs.numero_parcelas FROM receivables r LEFT JOIN company_sales cs ON r.company_sale_id = cs.id",
      "exemplo_correto": "SELECT COUNT(*) as total FROM receivables r WHERE r.company_sale_id = :sale_id",
      "prevencao": [
        "Documentar estrutura de tabelas",
        "Verificar queries antes de executar",
        "Usar try-catch e logar erros SQL",
        "Testar queries em ambiente de desenvolvimento primeiro"
      ]
    },
    
    "erro_assinatura_nao_aparece": {
      "problema": "Assinatura não aparece na visualização do documento assinado",
      "causas_possiveis": [
        "Filtro muito restritivo (exigindo signature_data quando status = signed)",
        "Posição não sendo salva corretamente ao assinar",
        "Posição sendo salva em pixels mas visualização espera porcentagem",
        "Variável container declarada múltiplas vezes causando erro JavaScript"
      ],
      "solucao": [
        "Filtrar apenas por status = 'signed' (não exigir signature_data no filtro)",
        "Usar COALESCE no SQL para preservar posição existente ao assinar",
        "SEMPRE converter pixels para porcentagem ao salvar posição",
        "Renomear variáveis JavaScript para evitar conflitos (ex: docContainer)"
      ],
      "exemplo_errado": {
        "filtro": "$signedSigners = array_filter($signers, function($s) { return $s['status'] === 'signed' && !empty($s['signature_data']); });",
        "javascript": "const container = ...; const container = ...; // ❌ Erro: Identifier already declared"
      },
      "exemplo_correto": {
        "filtro": "$signedSigners = array_filter($signers, function($s) { return $s['status'] === 'signed'; });",
        "sql": "UPDATE document_signers SET signature_x = COALESCE(:signature_x, signature_x) WHERE id = :id",
        "javascript": "const docContainer = ...; // ✅ Nome único"
      }
    },
    
    "erro_posicao_assinatura_incorreta": {
      "problema": "Assinatura aparece em local diferente do configurado",
      "causa": "Posição sendo salva em pixels mas visualização usa porcentagem (ou vice-versa)",
      "solucao": [
        "SEMPRE salvar posição em porcentagem no banco de dados",
        "Converter pixels para porcentagem antes de salvar: (x / containerWidth) * 100",
        "Usar porcentagem na visualização: left: X%, top: Y%",
        "Não tentar detectar se valor está em pixels ou porcentagem - sempre converter"
      ],
      "exemplo_errado": {
        "codigo": "// ❌ ERRADO: Tenta detectar se é pixel ou porcentagem\nif (x < 100 && y < 100) {\n    xPercent = (x / containerRect.width) * 100;\n} else {\n    xPercent = x; // Assume que já é porcentagem\n}"
      },
      "exemplo_correto": {
        "codigo": "// ✅ CORRETO: Sempre converte de pixels para porcentagem\nlet xPercent = (x / containerRect.width) * 100;\nlet yPercent = (y / containerRect.height) * 100;\nformData.append('signature_x', xPercent.toFixed(2));"
      },
      "arquivos_envolvidos": [
        "templates/signatures/configure.php (função savePosition)",
        "src/Models/DocumentSigner.php (método recordSignature)",
        "templates/signatures/view_signed_document.php (exibição)"
      ]
    },
    
    "erro_javascript_identifier_already_declared": {
      "erro": "Uncaught SyntaxError: Identifier 'container' has already been declared",
      "causa": "Variável const/let declarada múltiplas vezes no mesmo escopo",
      "solucao": [
        "Renomear variáveis para nomes únicos e descritivos",
        "Usar nomes específicos como docContainer, markersContainer, etc.",
        "Verificar todas as declarações de variáveis no arquivo",
        "Usar escopos diferentes (funções) para variáveis com mesmo nome"
      ],
      "exemplo_errado": "const container = ...; const container = ...; // ❌",
      "exemplo_correto": "const docContainer = ...; const markersContainer = ...; // ✅",
      "prevencao": [
        "Usar nomes descritivos e únicos para variáveis",
        "Verificar se variável já existe antes de declarar",
        "Usar ferramentas de linting para detectar duplicações"
      ]
    },
    
    "erro_clique_nao_funciona_posicionamento": {
      "problema": "Clique no documento não abre seletor de signatários",
      "causas_possiveis": [
        "CSS com pointer-events: none bloqueando cliques",
        "JavaScript não inicializado corretamente",
        "Container não encontrado",
        "Event listener não sendo anexado"
      ],
      "solucao": [
        "Adicionar pointer-events: none no conteúdo do documento, mas auto nos marcadores",
        "Verificar se DOMContentLoaded está sendo executado",
        "Adicionar logs de debug para rastrear inicialização",
        "Verificar se container existe antes de anexar listener"
      ],
      "css_correto": {
        "codigo": "#documentContent { pointer-events: none; }\n#signatureMarkersContainer { pointer-events: none; }\n#signatureMarkersContainer .signature-marker { pointer-events: auto; }"
      }
    },
    
    "erro_posicao_assinatura_desalinhada_multiplas_paginas": {
      "problema": "Assinatura aparece em posição diferente entre configuração e visualização quando documento tem múltiplas páginas",
      "causa_raiz": "Container do documento tem tamanhos diferentes entre configuração e visualização (altura variável)",
      "solucao_final": {
        "descricao": "Usar largura A4 fixa (794px) e altura variável (mínimo 1123px, cresce conforme necessário)",
        "implementacao": {
          "css": {
            "largura": "794px (A4 fixo) - width, min-width, max-width: 794px",
            "altura": "Variável - min-height: 1123px (uma página), sem max-height ou height fixo",
            "overflow": "Removido overflow: hidden para permitir crescimento vertical",
            "box_sizing": "border-box para incluir padding no cálculo"
          },
          "javascript": {
            "salvar_posicao": "Sempre salvar como porcentagem do container atual (x / containerWidth * 100)",
            "aplicar_posicao": "Aplicar via CSS usando porcentagem (left: X%, top: Y%)",
            "vantagem": "Porcentagem funciona independente da altura do documento"
          }
        }
      },
      "arquivos_envolvidos": [
        "templates/signatures/configure.php - CSS e JavaScript de salvamento",
        "templates/signatures/view_signed_document.php - CSS e JavaScript de aplicação"
      ],
      "observacoes": [
        "Largura A4 fixa garante consistência horizontal",
        "Altura variável permite múltiplas páginas A4 quando necessário",
        "Porcentagem funciona perfeitamente porque largura é sempre 794px",
        "Porcentagem em Y funciona porque é relativa ao documento completo",
        "Solução funciona tanto para documentos de uma página quanto múltiplas páginas"
      ],
      "exemplo_css": {
        "codigo": ".document-container {\n  width: 794px;\n  min-width: 794px;\n  max-width: 794px;\n  min-height: 1123px; /* Mínimo uma página A4 */\n  /* Altura cresce conforme necessário */\n  box-sizing: border-box;\n}"
      },
      "exemplo_javascript": {
        "salvar": "let xPercent = (x / containerRect.width) * 100; // Sempre 794px\nlet yPercent = (y / containerRect.height) * 100; // Variável conforme documento",
        "aplicar": "sig.style.left = dbX + '%';\nsig.style.top = dbY + '%';"
      },
      "prevencao": [
        "SEMPRE usar largura A4 fixa (794px) para documentos",
        "NUNCA fixar altura do container (permitir crescimento vertical)",
        "SEMPRE salvar posições em porcentagem (não pixels)",
        "SEMPRE aplicar posições via CSS usando porcentagem",
        "Testar com documentos de diferentes tamanhos (1 página, múltiplas páginas)"
      ]
    },
    
    "desafio_centralizacao_assinatura_pdf_tela": {
      "problema": "Assinatura não aparece na mesma posição entre a visualização na tela e o PDF gerado",
      "causa_raiz": "Diferenças de estrutura HTML, padding e margens entre a tela e o PDF gerado",
      "sintomas": [
        "Assinatura posicionada corretamente na tela, mas desalinhada no PDF",
        "Margem superior diferente entre tela e PDF",
        "Padding inconsistente entre visualização e geração de PDF"
      ],
      "solucao_final": {
        "descricao": "Padronizar completamente a estrutura HTML, CSS e configurações A4 entre tela e PDF",
        "implementacao": {
          "tela": {
            "container": "width: 794px (210mm), padding: 30px (7.94mm) em todos os lados",
            "altura": "min-height: 1123px (297mm), altura variável para múltiplas páginas",
            "estrutura": "<div class='document-container'><div id='documentContent'>conteúdo</div></div>",
            "font": "'Times New Roman', serif, font-size padrão, line-height: 1.6"
          },
          "pdf": {
            "mpdf": "margin: 0 (todos os lados), format: 'A4'",
            "container": "width: 210mm, padding: 7.94mm (equivalente a 30px), min-height: 297mm",
            "@page": "size: A4, margin: 0",
            "estrutura": "Mesma estrutura HTML da tela, com mesmos IDs e classes",
            "font": "'Times New Roman', serif, font-size: 12pt, line-height: 1.6"
          },
          "padronizacao": {
            "todos_documentos": [
              "SignaturesController - Documentos assinados",
              "DocumentsController - TCE e Contratos",
              "ReceivablesController - Recibos"
            ],
            "todos_templates": [
              "templates/signatures/configure.php",
              "templates/signatures/view_signed_document.php",
              "templates/documents/view_tce.php",
              "templates/documents/view_contract.php"
            ]
          }
        }
      },
      "arquivos_envolvidos": [
        "src/Controllers/SignaturesController.php - downloadSignedPdf() e generateSignedDocumentHtml()",
        "src/Controllers/DocumentsController.php - downloadTcePdf() e downloadContractPdf()",
        "src/Controllers/ReceivablesController.php - downloadReceiptPdf()",
        "templates/signatures/configure.php - Visualização na tela",
        "templates/signatures/view_signed_document.php - Visualização na tela",
        "templates/documents/view_tce.php - Visualização na tela",
        "templates/documents/view_contract.php - Visualização na tela"
      ],
      "equivalencias_importantes": {
        "largura": "794px (tela) = 210mm (PDF) - A4",
        "altura_minima": "1123px (tela) = 297mm (PDF) - A4",
        "padding": "30px (tela) = 7.94mm (PDF) - cálculo: 30px / 96 DPI * 25.4mm = 7.9375mm"
      },
      "observacoes": [
        "A estrutura HTML deve ser IDÊNTICA entre tela e PDF para garantir posicionamento correto",
        "Padding de 30px na tela corresponde exatamente a 7.94mm no PDF",
        "Margens do mPDF devem ser 0 para que o container ocupe toda a página A4",
        "Font-family, font-size e line-height devem ser os mesmos em ambos",
        "Box-sizing: border-box garante que padding está incluído no cálculo de dimensões",
        "Indicadores de divisão de páginas ajudam a visualizar onde o documento será quebrado no PDF"
      ],
      "prevencao": [
        "SEMPRE usar a mesma estrutura HTML na tela e no PDF",
        "SEMPRE usar padding de 30px na tela e 7.94mm no PDF (equivalente)",
        "SEMPRE configurar mPDF com margin: 0 para ocupar toda a página A4",
        "SEMPRE usar container de 794px (tela) / 210mm (PDF) para largura",
        "SEMPRE usar min-height: 1123px (tela) / 297mm (PDF) para altura mínima",
        "SEMPRE padronizar font-family, font-size e line-height entre tela e PDF",
        "Testar visualmente comparando tela e PDF lado a lado",
        "Adicionar indicadores de divisão de páginas para facilitar visualização"
      ],
      "data": "2025-12-14",
      "impacto": "Alto - Afeta a precisão do posicionamento de assinaturas em todos os documentos gerados"
    },
    
    "solucao_editor_paginas_a4_modelos": {
      "problema": "Modelos de documentos criados sem visualização de páginas A4, causando problemas de formatação entre diferentes telas (visualização, PDF, assinaturas)",
      "causa_raiz": "Editor de templates não mostrava visualmente as páginas A4, dificultando o controle de quebras de página e formatação",
      "solucao_final": {
        "descricao": "Implementar editor de templates com visualização de páginas A4 separadas, similar ao Microsoft Word",
        "implementacao": {
          "estrutura": {
            "container": "Editor com fundo cinza (#e5e5e5) mostrando páginas A4 brancas separadas",
            "pagina": "Cada página tem 794px de largura, 1123px de altura mínima, padding de 30px",
            "visualizacao": "Páginas empilhadas verticalmente com espaçamento de 20px entre elas",
            "indicadores": "Apenas quebras reais do modelo são exibidas (tracejado), sem labels ou linhas extras"
          },
          "funcionalidades": {
            "criar_pagina": "Botão 'Adicionar Página' cria nova página A4 manualmente",
            "quebra_manual": "Botão 'Quebra de Página' insere div com page-break-before: always",
            "divisao_automatica": "Sistema divide automaticamente em páginas quando conteúdo ultrapassa altura (similar ao Word)",
            "colagem_inteligente": "Ao colar texto, sistema divide automaticamente em múltiplas páginas se necessário",
            "detecao_overflow": "Monitora continuamente altura do conteúdo e move excedente para próxima página",
            "preservacao": "Conteúdo salvo preserva quebras de página usando divs invisíveis",
            "multi_pagina": "Suporta múltiplas páginas, cada uma editável independentemente",
            "recursivo": "Se próxima página também ultrapassar, continua dividindo automaticamente"
          },
          "css": {
            "largura_pagina": "794px (A4 em pixels a 96 DPI)",
            "altura_pagina": "1123px (A4 em pixels a 96 DPI)",
            "padding": "30px (equivalente a 7.94mm no PDF)",
            "fonte": "'Times New Roman', serif, 12pt, line-height: 1.6",
            "box_sizing": "border-box para incluir padding no cálculo",
            "sombra": "box-shadow: 0 0 10px rgba(0,0,0,0.1) para efeito de página física"
          },
          "javascript": {
            "criar_pagina": "Função createPage() cria nova página com event listeners",
            "atualizar_conteudo": "Função updateHiddenTextarea() combina conteúdo de todas as páginas",
            "detectar_overflow": "Função checkPageOverflow() verifica se conteúdo ultrapassou altura e chama divisão automática",
            "mover_overflow": "Função moveOverflowToNextPage() move conteúdo excedente para próxima página automaticamente",
            "extrair_overflow": "Função extractOverflowContent() extrai conteúdo que ultrapassou de forma inteligente (por elementos)",
            "inserir_quebra": "Função insertPageBreak() insere div de quebra de página manual",
            "renumerar": "Função renumberPages() renumerar páginas após remoção",
            "mutation_observer": "MutationObserver monitora mudanças no DOM para detectar overflow em tempo real",
            "prevencao_recursao": "Flag isProcessingOverflow evita recursão infinita durante divisão automática"
          }
        }
      },
      "arquivos_envolvidos": [
        "templates/documents/template_form.php - Editor de templates com páginas A4"
      ],
      "vantagens": [
        "Visualização clara de como o documento será impresso/gerado em PDF",
        "Controle preciso de quebras de página desde a criação do modelo",
        "Formatação consistente entre editor, visualização e PDF",
        "Facilita posicionamento de assinaturas (usuário vê exatamente onde está cada página)",
        "Reduz problemas de formatação entre diferentes telas"
      ],
      "equivalencias": {
        "largura": "794px (editor) = 210mm (PDF) = A4",
        "altura": "1123px (editor) = 297mm (PDF) = A4",
        "padding": "30px (editor) = 7.94mm (PDF)",
        "area_conteudo": "734px x 1063px (794-60 x 1123-60)"
      },
      "observacoes": [
        "Editor mostra páginas A4 físicas, facilitando visualização do resultado final",
        "Quebras de página são preservadas usando divs com page-break-before: always",
        "Sistema divide automaticamente em páginas quando conteúdo ultrapassa altura (similar ao Microsoft Word)",
        "Ao colar texto longo, sistema cria páginas automaticamente sem intervenção manual",
        "Divisão automática funciona de forma recursiva: se próxima página também ultrapassar, continua dividindo",
        "Sistema tenta dividir por elementos completos (parágrafos, divs) para manter formatação",
        "Cada página é editável independentemente, mas conteúdo é combinado ao salvar",
        "Indicadores visuais (borda amarela) aparecem temporariamente durante divisão automática",
        "MutationObserver monitora mudanças no DOM para detectar overflow em tempo real",
        "IMPORTANTE: Removidos labels 'Página X' e linhas extras do editor - apenas quebras reais são exibidas",
        "Consistência visual: todas as páginas mostram apenas as quebras reais do modelo (tracejado)"
      ],
      "prevencao": [
        "SEMPRE criar modelos usando o editor com visualização de páginas A4",
        "Usar botão 'Quebra de Página' para controlar onde o documento quebra",
        "Verificar visualmente se o conteúdo cabe em cada página",
        "Testar geração de PDF após criar/editar modelo para garantir consistência",
        "Manter padding de 30px e dimensões A4 fixas (794px x 1123px)",
        "NUNCA adicionar indicadores automáticos de página que conflitem com quebras reais",
        "Manter apenas as quebras reais do modelo visíveis (tracejado), sem labels ou linhas extras"
      ],
      "data": "2025-12-14",
      "impacto": "Alto - Padroniza formatação desde a criação dos modelos, evitando problemas de formatação em diferentes telas"
    },
    
    "problema_indicadores_automaticos_pagina": {
      "problema": "Indicadores automáticos de página (labels 'Página X' e linhas azuis) aparecendo incorretamente em todas as páginas de visualização, além das quebras reais do modelo",
      "causa_raiz": "Sistema criava indicadores automáticos de página baseados na altura do container, gerando labels e linhas que conflitavam com as quebras reais do modelo",
      "sintomas": [
        "Labels 'Página X' em azul aparecendo em todas as páginas",
        "Linhas azuis de divisão automática aparecendo além das quebras reais",
        "Conflito visual entre indicadores automáticos e quebras reais do modelo",
        "Usuário confuso com múltiplos indicadores de página"
      ],
      "solucao_final": {
        "descricao": "Remover completamente os indicadores automáticos de página, mantendo apenas as quebras reais do modelo (tracejado)",
        "implementacao": {
          "css_removido": [
            "background-image: repeating-linear-gradient (linhas de divisão automática)",
            ".page-break-indicator (linhas azuis de divisão)",
            ".page-number-label (labels 'Página X' em azul)"
          ],
          "javascript_removido": [
            "Função createPageBreakIndicators() que criava labels e linhas",
            "ResizeObserver que observava mudanças no container",
            "Chamadas à função no DOMContentLoaded",
            "Atualizações automáticas quando marcadores eram adicionados"
          ],
          "mantido": [
            "Quebras de página reais do modelo (divs com page-break-before: always)",
            "Visual tracejado das quebras reais (border-top: 2px dashed)",
            "CSS para preservar quebras de página do modelo",
            "Formatação completa do documento"
          ]
        }
      },
      "arquivos_envolvidos": [
        "templates/documents/view_contract.php - Visualização de contratos",
        "templates/documents/view_tce.php - Visualização de TCE",
        "templates/documents/template_form.php - Editor de modelos (removidos labels e linhas extras)",
        "templates/signatures/configure.php - Configuração de assinaturas",
        "templates/signatures/view_signed_document.php - Visualização de documento assinado"
      ],
      "observacoes": [
        "Indicadores automáticos eram calculados baseados na altura do container, não nas quebras reais do modelo",
        "Isso gerava conflito visual quando o modelo tinha quebras de página definidas",
        "A solução foi remover completamente os indicadores automáticos e confiar apenas nas quebras reais do modelo",
        "As quebras reais são preservadas através de divs com page-break-before: always inseridas no editor",
        "O visual tracejado das quebras reais é suficiente para identificar onde o documento quebra",
        "Consistência: todas as páginas agora mostram apenas as quebras reais, sem indicadores extras"
      ],
      "prevencao": [
        "NUNCA criar indicadores automáticos de página baseados apenas na altura do container",
        "SEMPRE usar as quebras reais do modelo (divs com page-break-before) como única fonte de verdade",
        "Se precisar mostrar indicadores, usar apenas as quebras reais do modelo, não calcular automaticamente",
        "Manter consistência: todas as páginas devem usar a mesma lógica de exibição de quebras",
        "Testar visualmente: verificar se não há conflito entre indicadores automáticos e quebras reais"
      ],
      "data": "2025-12-14",
      "impacto": "Médio - Melhora a experiência do usuário ao remover indicadores confusos e manter apenas as quebras reais do modelo"
    },
    
    "implementacao_rodape_assinatura_digital": {
      "problema": "Necessidade de adicionar informações de assinatura digital (nome e IP) em todas as páginas do documento assinado para garantir validade jurídica",
      "requisitos": [
        "Rodapé deve aparecer em todas as páginas do documento, inclusive na última",
        "Rodapé deve aparecer acima das divisões de página (linha tracejada)",
        "Rodapé deve aparecer tanto na visualização web quanto no PDF baixado",
        "Informação deve incluir nome do signatário e IP do dispositivo",
        "Rodapé deve ficar próximo ao rodapé da página (não muito acima)",
        "Não deve haver duplicação de informações"
      ],
      "solucao_final": {
        "descricao": "Inserir rodapé diretamente no HTML antes de cada quebra de página e no final do documento",
        "implementacao": {
          "geracao_rodape": {
            "metodo": "generateSignatureFooter() - gera HTML do rodapé com informações de todos os signatários",
            "formato": "Documento assinado por [Nome] pelo IP [IP]",
            "separador_multiplos": " | (pipe) para separar múltiplos signatários",
            "estilo": "font-size: 8pt, padding: 3px, margin: 2px 0 0 0, border-top: 1px solid #ddd"
          },
          "insercao_rodape": {
            "localizacao_quebras": "Usa regex para encontrar divs com page-break-before ou classe manual-page-break",
            "insercao_antes_quebras": "Insere rodapé antes de cada quebra de página encontrada",
            "insercao_final": "Adiciona rodapé no final do documento (última página)",
            "prevencao_duplicacao": "Verifica se rodapé já existe nos últimos 200 caracteres antes de adicionar no final",
            "regex_unica": "Uma única regex captura todos os casos: style com page-break-before OU classe manual-page-break"
          },
          "aplicacao": {
            "pdf": "Rodapé inserido no HTML antes de gerar PDF com mPDF",
            "visualizacao_web": "Rodapé inserido no conteúdo antes de renderizar na página view_signed_document.php",
            "consistencia": "Mesma lógica usada tanto para PDF quanto para visualização web"
          }
        }
      },
      "arquivos_envolvidos": [
        "src/Controllers/SignaturesController.php - Métodos generateSignatureFooter() e generateSignedDocumentHtml()",
        "src/Controllers/SignaturesController.php - Método viewSignedDocument() para visualização web",
        "templates/signatures/view_signed_document.php - CSS para .signature-footer"
      ],
      "problemas_encontrados_e_solucoes": [
        {
          "problema": "Rodapé não aparecia na visualização web, apenas no PDF",
          "solucao": "Aplicar mesma lógica de inserção do rodapé no método viewSignedDocument()"
        },
        {
          "problema": "Rodapé aparecia muito acima do rodapé da página",
          "solucao": "Reduzir margem superior de 15px para 2px e padding de 8px para 3px"
        },
        {
          "problema": "Informação duplicada (rodapé aparecendo duas vezes)",
          "solucao": "Usar regex única em vez de múltiplas, verificar antes de adicionar no final"
        },
        {
          "problema": "Rodapé não aparecia na última página",
          "solucao": "Adicionar rodapé explicitamente no final do documento após processar quebras"
        }
      ],
      "observacoes": [
        "Rodapé é inserido diretamente no HTML, não usando SetHTMLFooter() do mPDF",
        "Isso permite controle total sobre posicionamento e evita problemas com margens do mPDF",
        "Regex única é mais eficiente e evita duplicação que ocorria com múltiplas regex",
        "Verificação nos últimos 200 caracteres previne duplicação no final do documento",
        "Rodapé aparece acima da linha tracejada de quebra de página, como solicitado",
        "Formato simples e direto: 'Documento assinado por [Nome] pelo IP [IP]'",
        "Múltiplos signatários são separados por ' | ' (pipe com espaços)"
      ],
      "prevencao": [
        "SEMPRE inserir rodapé no final do documento além de antes das quebras de página",
        "Usar regex única para evitar processamento múltiplo e duplicação",
        "Verificar se rodapé já existe antes de adicionar no final",
        "Aplicar mesma lógica tanto para PDF quanto para visualização web",
        "Manter margens e padding pequenos para rodapé ficar próximo ao rodapé da página",
        "Testar com documentos de múltiplas páginas para garantir que aparece em todas"
      ],
      "data": "2025-12-14",
      "impacto": "Alto - Garante validade jurídica dos documentos assinados digitalmente, fornecendo rastreabilidade completa (nome e IP) em todas as páginas"
    },
    
    "correcao_exibicao_assinaturas_coletadas": {
      "problema": "Mensagem de assinaturas coletadas exibindo valor incorreto (ex: '1 de 0 signatários' em vez de '1 de 1')",
      "causa_raiz": "Uso de campo 'total_signers' do array signature que pode não estar sendo passado corretamente ou estar como 0",
      "solucao_final": {
        "descricao": "Calcular total de signatários e assinaturas coletadas diretamente do array de signatários",
        "implementacao": {
          "calculo_total": "count($signers) - conta todos os signatários cadastrados",
          "calculo_assinados": "Iterar sobre $signers e contar apenas os com status === 'signed'",
          "exibicao": "Exibir 'Assinaturas coletadas: X de Y signatários' onde X = assinados, Y = total"
        }
      },
      "arquivos_envolvidos": [
        "templates/signatures/view_signed_document.php - Cálculo e exibição da mensagem"
      ],
      "codigo_antes": {
        "problema": "<?= count($signers) ?> de <?= (int)$signature['total_signers'] ?? count($signers) ?> signatários",
        "explicacao": "Usava $signature['total_signers'] que poderia estar como 0 ou não estar definido"
      },
      "codigo_depois": {
        "solucao": "<?php\n$totalSigners = count($signers);\n$signedCount = 0;\nforeach ($signers as $signer) {\n    if ($signer['status'] === 'signed') {\n        $signedCount++;\n    }\n}\n?>\n<?= $signedCount ?> de <?= $totalSigners ?> signatários",
        "explicacao": "Calcula diretamente do array de signatários, garantindo valores corretos"
      },
      "observacoes": [
        "Sempre calcular estatísticas diretamente dos dados disponíveis em vez de confiar em campos que podem não estar atualizados",
        "O array $signers sempre contém todos os signatários cadastrados, então count($signers) é a fonte confiável para o total",
        "Para contar assinados, iterar e verificar status === 'signed' é mais confiável que usar campos calculados",
        "Esta abordagem garante que a mensagem sempre reflete o estado real dos dados"
      ],
      "prevencao": [
        "SEMPRE calcular estatísticas diretamente dos arrays/dados disponíveis",
        "NUNCA confiar apenas em campos calculados que podem estar desatualizados",
        "SEMPRE iterar sobre os dados para contar status específicos",
        "Testar com diferentes cenários (0 assinados, alguns assinados, todos assinados)",
        "Verificar se os dados passados para a view estão completos e atualizados"
      ],
      "data": "2025-12-14",
      "impacto": "Médio - Melhora a precisão das informações exibidas ao usuário sobre o status das assinaturas"
    }
  },

  "configuracoes_importantes": {
    "banco_dados": {
      "charset": "utf8mb4",
      "collation": "utf8mb4_unicode_ci",
      "arquivo": "config/database.php",
      "opcoes_pdo": [
        "PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION",
        "PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC",
        "PDO::ATTR_EMULATE_PREPARES => false"
      ]
    },
    
    "sessao": {
      "inicio": "Session::start() no index.php",
      "seguranca": "Configurações de cookie seguro em produção"
    },
    
    "rotas": {
      "arquivo": "public/index.php",
      "padrao": "Todas as rotas começam com /public/",
      "roteador": "App\\Utils\\Router (custom)"
    }
  },

  "padroes_implementacao": {
    "controller": {
      "estrutura": [
        "1. Security::requireAuth()",
        "2. Security::requirePermission() (se aplicável)",
        "3. Processar dados de entrada (GET/POST)",
        "4. Validar dados",
        "5. Chamar Model para operações de banco",
        "6. Preparar dados para view",
        "7. Renderizar view ou redirecionar"
      ],
      "exemplo": "ReceivablesController::index()"
    },
    
    "model": {
      "estrutura": [
        "1. Estender classe Model",
        "2. Definir propriedade $table",
        "3. Métodos: findById(), findAll(), create(), update(), delete()",
        "4. Métodos customizados conforme necessidade"
      ],
      "exemplo": "Receivable extends Model"
    },
    
    "view": {
      "estrutura": [
        "1. Incluir header (templates/layout/header.php)",
        "2. Conteúdo da página",
        "3. Incluir footer (templates/layout/footer.php)",
        "4. Exibir mensagens de sucesso/erro da sessão"
      ]
    },
    
    "filtros_documentos": {
      "descricao": "Padrão de filtros para páginas de listagem de documentos (TCE, Contratos, Aditivos, Rescisões)",
      "estrutura_html": {
        "container": "<div class='card mb-3'><div class='card-body'><form method='GET' action='...'>",
        "linha_filtros": "<div class='row g-3 mb-3'> - Todos os campos de filtro na mesma linha",
        "linha_botoes": "<div class='row'><div class='col-12'> - Botões em linha separada abaixo"
      },
      "layout_colunas": {
        "regra": "Todos os filtros devem somar 12 colunas (Bootstrap grid) para ficarem na mesma linha",
        "exemplo_tce": [
          "Empresa: col-md-2",
          "Candidato: col-md-2",
          "Instituição: col-md-2",
          "Data Inicial: col-md-2",
          "Data Final: col-md-2",
          "Status de Assinatura: col-md-2",
          "Total: 12 colunas (todos na mesma linha)"
        ],
        "exemplo_aditivos": [
          "Empresa: col-md-3",
          "Aluno: col-md-3",
          "Data Inicial: col-md-2",
          "Data Final: col-md-2",
          "Status de Assinatura: col-md-2",
          "Total: 12 colunas (todos na mesma linha)"
        ],
        "exemplo_contratos": [
          "Empresa: col-md-3",
          "Data Inicial: col-md-3",
          "Data Final: col-md-2",
          "Status de Assinatura: col-md-4",
          "Total: 12 colunas (todos na mesma linha)"
        ]
      },
      "botoes": {
        "posicao": "Linha separada abaixo dos filtros",
        "estrutura": "<div class='row'><div class='col-12'><button>Filtrar</button><a>Limpar</a></div></div>",
        "botao_filtrar": "btn btn-primary com ícone bi-search",
        "botao_limpar": "btn btn-secondary com ícone bi-x-circle, link para URL sem parâmetros"
      },
      "controller": {
        "processamento": "Ler parâmetros GET, aplicar filtros no Model, buscar informações de assinatura se necessário",
        "filtro_assinatura": "Verificar signatureStatus ('', 'completed', 'pending') e filtrar documentos baseado em signed_count vs total_signers",
        "passar_dados": "Passar todos os parâmetros de filtro para a view para manter valores selecionados"
      },
      "arquivos_exemplo": [
        "templates/documents/tce.php",
        "templates/documents/partnership_contracts.php",
        "templates/documents/addendums.php",
        "templates/documents/terminations.php",
        "src/Controllers/DocumentsController.php (métodos tce(), partnershipContracts(), addendums(), terminations())"
      ],
      "observacoes": [
        "Todos os campos de filtro devem estar na mesma linha (somar 12 colunas)",
        "Botões 'Filtrar' e 'Limpar' ficam em linha separada abaixo dos filtros",
        "Filtro de 'Status' foi removido do TCE (não é mais necessário com bloqueio por assinatura)",
        "Filtro 'Status de Assinatura' é comum a todas as páginas de documentos",
        "Layout deve ser consistente entre todas as páginas de listagem de documentos",
        "Valores selecionados devem ser preservados ao recarregar a página (usar selected no option)"
      ],
      "prevencao": [
        "SEMPRE verificar que a soma das colunas é 12 (todos na mesma linha)",
        "NUNCA colocar botões na mesma linha dos filtros (ficam abaixo)",
        "SEMPRE manter valores selecionados ao recarregar (usar isset() e selected)",
        "SEMPRE passar todos os parâmetros de filtro para a view",
        "SEMPRE usar mesma estrutura HTML em todas as páginas de documentos",
        "Testar layout em diferentes tamanhos de tela (responsivo)"
      ]
    },
    
    "organizacao_permissoes_niveis_acesso": {
      "descricao": "Padrão de organização de permissões na página de níveis de acesso seguindo a estrutura hierárquica do menu lateral",
      "estrutura_hierarquica": {
        "nivel_1": "Seção do Menu (ex: Financeiro, Relatórios, Vagas, Documentos, Configurações)",
        "nivel_2": "Subseção/Funcionalidade (ex: Dashboard, Contas a Pagar, TCE, Usuários)",
        "nivel_3": "Permissões individuais (checkboxes com nome da permissão)"
      },
      "implementacao": {
        "model": {
          "metodo": "Permission::findAllGroupedByMenuStructure()",
          "descricao": "Agrupa permissões conforme estrutura do menu lateral, mapeando key_name das permissões para seções e subseções",
          "mapeamento": "Define estrutura do menu com array associativo: Seção => [Subseção => [permission_keys]]",
          "fallback": "Permissões não mapeadas são agrupadas pelo módulo original (campo 'module' da tabela)"
        },
        "controller": {
          "metodo": "RolesController::create() e RolesController::edit()",
          "uso": "Chama Permission::findAllGroupedByMenuStructure() em vez de findAllGroupedByModule()",
          "passagem_dados": "Passa array hierárquico para a view: ['Seção' => ['Subseção' => [permissions]]]"
        },
        "view": {
          "arquivo": "templates/roles/form.php",
          "estrutura_html": {
            "secao": "<h5> com ícone bi-folder-fill e classe text-primary",
            "subsecao": "<h6> com ícone bi-folder, classe text-secondary, indentação ms-4",
            "permissoes": "<div class='row ms-2'> com checkboxes em col-md-6"
          },
          "estilo": {
            "container": "border rounded p-3 com max-height: 500px e overflow-y: auto",
            "espacamento": "mb-4 entre seções, mb-3 entre subseções, mb-2 entre permissões"
          }
        }
      },
      "estrutura_menu_mapeada": {
        "Clientes": [
          "Dashboard (dashboard.view)",
          "Alunos (financeiro.view, create, edit, delete)",
          "Empresas (financeiro.view, create, edit, delete)",
          "Serviços (financeiro.view, create, edit, delete)"
        ],
        "Financeiro": [
          "Dashboard (dashboard.financial)",
          "Contas a Pagar (financeiro.view, create, edit, delete)",
          "Contas a Receber (financeiro.view, create, edit, delete)",
          "Fornecedores (financeiro.view apenas - não tem opções de criar/editar/excluir)",
          "Contas (financeiro.view apenas - não tem opções de criar/editar/excluir)",
          "Plano de Contas (financeiro.view apenas - não tem opções de criar/editar/excluir)"
        ],
        "Relatórios": [
          "Dashboard (dashboard.reports)",
          "Contas a Receber (relatorios.receivables)",
          "Contas a Pagar (relatorios.payables)",
          "Gerenciais (relatorios.managerial)",
          "Vagas Preenchidas (relatorios.vacancies)",
          "Vendas (relatorios.view)",
          "Carteira por Consultor (relatorios.portfolio)",
          "Estagiários Ativos (relatorios.active_interns)"
        ],
        "Vagas": [
          "Painel de Vagas (vagas.view, create, edit, delete, close, fill)",
          "Cadastros Gerais (cadastros.view, create, delete)",
          "Agenda de Entrevistas (vagas.view)"
        ],
        "Documentos": [
          "Dashboard (documentos.view)",
          "Cadastros (documentos.view)",
          "Contrato de Parceria (documentos.view, create, edit, delete)",
          "TCE (documentos.view, create, edit, delete)",
          "Aditivo (documentos.view, create, edit, delete)",
          "Rescisão (documentos.view, create, edit, delete)",
          "Modelos (documentos.modelos.view, create, edit, delete)",
          "Assinaturas Digitais (assinaturas.view, create, manage, send_email, remove_signer, view_signed, download_pdf) - inclui 'Enviar Links de Assinatura' (assinaturas.send_email)"
        ],
        "Configurações": [
          "Usuários (usuarios.view, create, edit, delete)",
          "Níveis de Acesso (roles.view, create, edit, delete)"
        ]
      },
      "vantagens": [
        "Organização clara e intuitiva seguindo a estrutura do menu",
        "Facilita localização de permissões pelos usuários",
        "Mantém consistência visual com o restante do sistema",
        "Estrutura hierárquica facilita navegação e compreensão",
        "Permite que a mesma permissão apareça em múltiplas subseções quando faz sentido (ex: financeiro.view em todas as funcionalidades financeiras)"
      ],
      "observacoes": [
        "Permissões são mapeadas por key_name, não por módulo",
        "Uma mesma permissão pode aparecer em múltiplas subseções quando faz sentido (ex: financeiro.view em Contas a Pagar, Contas a Receber, etc.)",
        "A lógica permite duplicação intencional de permissões em diferentes subseções",
        "Permissões não mapeadas aparecem agrupadas pelo módulo original como fallback, exceto módulos já mapeados em outras seções (ex: 'Assinaturas' não cria seção própria)",
        "Estrutura do menu deve ser atualizada no método findAllGroupedByMenuStructure() quando novas seções forem adicionadas",
        "Seção 'Clientes' usa permissões financeiro.* temporariamente até ter permissões específicas",
        "Permissões de assinatura digital estão agrupadas na seção Documentos como subseção 'Assinaturas Digitais'",
        "Subseções 'Fornecedores', 'Contas' e 'Plano de Contas' usam apenas financeiro.view (não têm opções de criar/editar/excluir)",
        "Todas as permissões de assinatura (incluindo 'Enviar Links de Assinatura' = assinaturas.send_email) devem estar dentro de 'Assinaturas Digitais', não em 'Geral'"
      ],
      "prevencao": [
        "SEMPRE atualizar mapeamento em findAllGroupedByMenuStructure() quando adicionar novas seções ao menu",
        "SEMPRE usar key_name das permissões no mapeamento (não usar IDs)",
        "SEMPRE verificar se todas as permissões existentes estão mapeadas ou aparecem no fallback",
        "SEMPRE manter estrutura hierárquica consistente: Seção > Subseção > Permissões",
        "SEMPRE usar mesma estrutura HTML na view (h5 para seções, h6 para subseções)",
        "Testar visualmente se a organização está clara e intuitiva",
        "Verificar se permissões não estão duplicadas na visualização"
      ],
      "arquivos_envolvidos": [
        "src/Models/Permission.php - Método findAllGroupedByMenuStructure()",
        "src/Controllers/RolesController.php - Métodos create() e edit()",
        "templates/roles/form.php - Estrutura HTML hierárquica"
      ],
      "data": "2025-12-14",
      "impacto": "Alto - Melhora significativamente a usabilidade da página de níveis de acesso, facilitando a configuração de permissões pelos administradores"
    }
  },

  "licoes_aprendidas": {
    "evitar_retrabalho": [
      "SEMPRE testar queries SQL complexas antes de implementar",
      "Verificar collations das tabelas antes de fazer JOINs",
      "Testar binding de parâmetros PDO quando o mesmo parâmetro aparece múltiplas vezes",
      "Validar dados de entrada antes de processar",
      "Usar try-catch em todas as operações de banco de dados",
      "Logar operações importantes para facilitar debug",
      "Documentar decisões importantes e padrões estabelecidos",
      "SEMPRE definir variáveis antes de usar (especialmente em logs)",
      "Usar a mesma conexão PDO em transações (não criar novos models dentro de transações)",
      "Verificar estrutura de tabelas antes de fazer queries (SHOW COLUMNS)",
      "Adicionar logs detalhados em cada etapa de processos críticos",
      "Testar recebimento/pagamento após cada mudança no código",
      "Ao marcar parcelas como recebidas, SEMPRE capturar valor_recebido do POST se fornecido e passar para markAsPaid()",
      "Valor manual inserido pelo usuário tem prioridade sobre valor calculado automaticamente",
      "Quando valor_recebido é fornecido, markAsPaid() usa esse valor em vez de calcular multa/juros",
      "Valor calculado ainda pode ser exibido no log para referência, mas não é usado se valor manual foi fornecido",
      "NUNCA usar !empty() para verificar valor_recebido - usar isset() e verificar explicitamente se não é vazio/null",
      "!empty() retorna false para valores como '0' ou string vazia, impedindo captura de valores válidos",
      "SEMPRE usar isset() e verificação explícita: isset($_POST['valor_recebido']) && $_POST['valor_recebido'] !== '' && $_POST['valor_recebido'] !== null",
      "JavaScript deve preservar valor manual mesmo quando data de recebimento é alterada (não recalcular automaticamente)",
      "Adicionar logs detalhados no controller e model para rastrear se valor manual ou calculado está sendo usado"
    ],
    
    "desenvolvimento": [
      "Implementar uma funcionalidade por vez",
      "Testar cada funcionalidade antes de passar para a próxima",
      "Não modificar código que já está funcionando sem necessidade",
      "Fazer backup antes de mudanças grandes",
      "Usar versionamento (git) para rastrear mudanças"
    ],
    
    "banco_dados": [
      "Padronizar collation desde o início (utf8mb4_unicode_ci)",
      "Usar nomes descritivos para tabelas e colunas",
      "Adicionar índices em campos frequentemente consultados",
      "Usar FOREIGN KEY constraints para integridade referencial",
      "Documentar estrutura de tabelas em comentários SQL",
      "Verificar se colunas existem antes de usar em queries (SHOW COLUMNS)",
      "Usar mesma conexão PDO em transações para evitar deadlocks",
      "Fazer commit/rollback explícito em todas as transações",
      "Logar todas as operações de transação para debug",
      "SEMPRE usar LONGTEXT para campos que armazenam conteúdo HTML de documentos (não TEXT)",
      "TEXT tem limite de ~65KB, LONGTEXT suporta até 4GB",
      "Verificar tipo de coluna antes de criar tabelas que armazenam conteúdo HTML",
      "Testar salvamento e carregamento de documentos grandes para detectar truncamento"
    ],
    
    "codigo": [
      "Manter código DRY (Don't Repeat Yourself)",
      "Criar métodos reutilizáveis em Models",
      "Usar constantes para valores mágicos",
      "Comentar código complexo",
      "Seguir padrões estabelecidos no projeto",
      "NUNCA usar variável antes de defini-la (especialmente em logs)",
      "Sempre validar dados de entrada (POST/GET) antes de usar",
      "Adicionar logs detalhados em processos críticos (recebimento, pagamento)",
      "Usar try-catch específico para cada operação crítica",
      "Garantir que todas as variáveis estão definidas antes de usar em logs",
      "Em transações, usar $this->db diretamente em vez de criar novos models",
      "Em JavaScript, usar nomes únicos para variáveis (evitar 'container' genérico)",
      "Sempre converter unidades (pixels/porcentagem) de forma consistente",
      "Usar COALESCE no SQL para preservar valores existentes ao atualizar",
      "Adicionar pointer-events corretamente no CSS para permitir cliques onde necessário"
    ],
    
    "assinaturas_digitais": [
      "Sempre salvar posições em porcentagem (não pixels) para responsividade",
      "Converter pixels para porcentagem antes de salvar: (pixels / containerSize) * 100",
      "Usar COALESCE no SQL ao atualizar posição para preservar valor existente",
      "Filtrar signatários apenas por status, não exigir signature_data no filtro",
      "Adicionar logs detalhados em cada etapa (salvar posição, processar assinatura)",
      "Usar nomes únicos para variáveis JavaScript (docContainer, markersContainer)",
      "Configurar pointer-events no CSS corretamente (none no conteúdo, auto nos marcadores)",
      "Validar se container existe antes de anexar event listeners",
      "Testar posicionamento em diferentes tamanhos de tela",
      "Garantir que posição é preservada quando signatário assina (usar COALESCE)",
      "IMPORTANTE: Usar largura A4 fixa (794px) e altura variável para suportar múltiplas páginas",
      "Container deve ter width: 794px fixo, min-height: 1123px, sem max-height ou height fixo",
      "Porcentagem funciona perfeitamente porque largura é sempre consistente (794px)",
      "Altura variável permite que documento cresça para múltiplas páginas A4 quando necessário",
      "CRÍTICO: Padronizar completamente estrutura HTML, CSS e configurações entre tela e PDF",
      "Tela e PDF devem ter EXATAMENTE a mesma estrutura HTML (mesmos IDs, classes, padding)",
      "Padding de 30px na tela = 7.94mm no PDF (30px / 96 DPI * 25.4mm)",
      "mPDF deve ter margin: 0 para que container ocupe toda a página A4",
      "Container no PDF: width: 210mm, padding: 7.94mm (equivalente a 30px da tela)",
      "Font-family, font-size e line-height devem ser IDÊNTICOS entre tela e PDF",
      "Testar visualmente comparando tela e PDF lado a lado para garantir alinhamento",
      "Adicionar indicadores de divisão de páginas para facilitar visualização e controle",
      "IMPORTANTE: Criar modelos usando editor com visualização de páginas A4 desde o início",
      "Editor de templates mostra páginas A4 separadas (794px x 1123px) para controle preciso",
      "Usar botão 'Quebra de Página' no editor para controlar onde o documento quebra",
      "Modelos criados com visualização de páginas A4 garantem formatação consistente em todas as telas",
      "NUNCA criar indicadores automáticos de página que conflitem com quebras reais do modelo",
      "Remover indicadores automáticos (labels, linhas) e manter apenas quebras reais (tracejado)",
      "Consistência visual: todas as páginas devem mostrar apenas as quebras reais do modelo",
      "Rodapé de assinatura digital: inserir antes de cada quebra de página E no final do documento",
      "Usar regex única para inserir rodapé e verificar antes de adicionar no final para evitar duplicação",
      "Rodapé deve aparecer tanto na visualização web quanto no PDF baixado com mesma lógica",
      "Manter margens e padding pequenos no rodapé para ficar próximo ao rodapé da página",
      "Capturar IP do signatário considerando proxies (HTTP_X_FORWARDED_FOR, HTTP_CLIENT_IP, REMOTE_ADDR)",
      "Exibir IP abaixo da assinatura na visualização para rastreabilidade",
      "Rodapé deve aparecer em todas as páginas, inclusive na última, para validade jurídica completa",
      "NUNCA usar '<?php if (false):' para comentar código - remover completamente ou usar comentários HTML",
      "Blocos 'if (false)' com código HTML/PHP dentro ainda são processados pelo parser PHP, causando erros de sintaxe",
      "SEMPRE garantir que variáveis passadas para views existam e tenham valores padrão antes de acessá-las",
      "SEMPRE colocar 'use' statements logo após <?php, antes de qualquer outro código PHP",
      "Erros de sintaxe PHP podem causar página completamente em branco sem mensagens visíveis - verificar logs do servidor",
      "Menu lateral alterado pode indicar que HTML foi quebrado antes de ser renderizado completamente",
      "SEMPRE usar echo em vez de <?= para conteúdo grande ou complexo para evitar problemas de sintaxe"
    ]
  },

  "checklist_implementacao": {
    "nova_funcionalidade": [
      "1. Criar/atualizar Model se necessário",
      "2. Criar/atualizar Controller com métodos necessários",
      "3. Adicionar rotas em public/index.php",
      "4. Criar/atualizar views (templates)",
      "5. Adicionar permissões se necessário (SQL)",
      "6. Adicionar item no menu (templates/layout/header.php) se necessário",
      "7. Testar funcionalidade completa",
      "8. Verificar tratamento de erros",
      "9. Verificar segurança (auth, permissions, CSRF)"
    ],
    
    "nova_tabela": [
      "1. Criar script SQL em sql/",
      "2. Usar charset utf8mb4 e collation utf8mb4_unicode_ci",
      "3. Adicionar índices apropriados",
      "4. Adicionar FOREIGN KEY constraints se necessário",
      "5. Criar Model correspondente",
      "6. Testar CRUD básico"
    ],
    
    "novo_relatorio": [
      "1. Criar método no ReportsController",
      "2. Criar view em templates/reports/",
      "3. Adicionar rota em public/index.php",
      "4. Adicionar permissão (SQL)",
      "5. Adicionar item no menu",
      "6. Testar com diferentes filtros",
      "7. Verificar performance com grandes volumes de dados"
    ]
  },

  "multi_tenancy": {
    "descricao": "Sistema preparado para rede de franquias com múltiplos bancos de dados",
    "estrategia": "Database-per-Tenant (cada franquia tem seu próprio banco)",
    "estrutura": {
      "banco_master": "trainees_master (ou banco atual se não tiver permissão)",
      "bancos_franquias": "trainees_franquia_001, trainees_franquia_002, etc.",
      "identificacao": "Login único - sistema identifica automaticamente pelo usuário"
    },
    "documentacao": [
      "docs/GUIA_MULTI_TENANCY.md - Guia completo e consolidado (PRINCIPAL)",
      "docs/ESTRUTURA_BANCOS_MULTI_TENANCY.md - Explicação da estrutura",
      "docs/COMO_CRIAR_SEGUNDA_FRANQUIA.md - Guia para criar novas franquias",
      "CHECKLIST_QUANDO_VOLTAR.md - Checklist quando provedor criar bancos"
    ],
    "arquivos_importantes": {
      "sql": [
        "sql/create_master_database.sql - Cria banco master (se tiver permissão)",
        "sql/create_master_tables_only.sql - Cria tabelas no banco atual (se não tiver permissão)",
        "sql/schema.sql - Schema para cada banco de franquia"
      ],
      "scripts": [
        "scripts/sync_users_to_master.php - Sincroniza usuários",
        "scripts/create_franchise.php - Cria nova franquia automaticamente"
      ],
      "codigo": [
        "src/Utils/Database.php - Suporta múltiplos bancos",
        "src/Utils/FranchiseUserFinder.php - Busca usuário e identifica franquia",
        "src/Controllers/AuthController.php - Login único implementado"
      ]
    },
    "consideracoes": [
      "Isolamento total de dados por franquia",
      "Segurança e conformidade com LGPD",
      "Backup individual por franquia",
      "Escalabilidade independente",
      "Migrações precisam ser aplicadas em todos os bancos",
      "Sistema funciona mesmo sem permissão para criar bancos (usa banco atual)"
    ]
  },

  "acesso_empresas_clientes": {
    "descricao": "Sistema de acesso restrito para empresas clientes (usuários criados em Configurações > Empresas)",
    "data_implementacao": "2026-01-07",
    "funcionalidade": {
      "objetivo": "Permitir que empresas clientes acessem o sistema com visão limitada apenas da sua própria empresa",
      "reconhecimento": "Usuário de empresa cliente é identificado pela presença de company_id na sessão (Session::has('company_id'))",
      "email_validacao": "O email de login deve corresponder EXATAMENTE ao email cadastrado nos dados da empresa"
    },
    "permissoes_empresa_cliente": {
      "descricao": "Empresas clientes têm permissões fixas definidas no código, não configuráveis por role",
      "permissoes": [
        "dashboard.principal - Acesso ao dashboard (mas é redirecionado para lista de empresas)",
        "empresas.dados.visualizar - Visualizar dados cadastrais da empresa",
        "empresas.estagiarios.visualizar - Visualizar estagiários",
        "empresas.backoffice.visualizar - Visualizar backoffice",
        "empresas.documentos.visualizar - Visualizar documentos"
      ],
      "arquivo_permissoes": "src/Utils/Security.php - método hasPermission() e isCompanyClient()"
    },
    "telas_visiveis": {
      "menu_lateral": "Apenas 'Clientes > Empresas' (sem Dashboard Principal visível)",
      "lista_empresas": "Mostra apenas a própria empresa do usuário logado",
      "ficha_empresa_abas": [
        "Dados Cadastrais (somente visualização)",
        "Estagiários (somente visualização, sem botões de ação)",
        "Backoffice (somente visualização)",
        "Documentos (somente visualização, sem botão de gerar contrato)"
      ],
      "abas_ocultas": ["Vendas", "Financeiro", "Follow"]
    },
    "restricoes": {
      "edicao": "Empresas clientes NÃO podem editar nenhum dado",
      "criacao": "Empresas clientes NÃO podem criar registros",
      "exclusao": "Empresas clientes NÃO podem excluir registros",
      "outras_empresas": "Empresas clientes NÃO podem ver dados de outras empresas"
    },
    "arquivos_modificados": [
      "src/Utils/Security.php - Adicionados métodos isCompanyClient(), getCompanyId() e lógica de permissões para empresas",
      "src/Controllers/AuthController.php - Carrega permissões específicas de empresa cliente no login",
      "src/Controllers/DashboardController.php - Redireciona empresas clientes para /clients/companies",
      "src/Controllers/ClientsController.php - Bloqueia edição para empresas clientes",
      "templates/layout/header.php - Menu lateral restrito para empresas clientes",
      "templates/clients/company_profile.php - Abas e botões restritos para empresas clientes",
      "templates/clients/companies.php - Lista apenas a empresa do usuário"
    ],
    "verificacao_permissao_financeiro": {
      "data": "2026-01-07",
      "problema": "Aba Financeiro aparecia para usuários sem permissão (ex: estagiários role_id=7)",
      "causa": "Verificação era apenas !$isCompanyClient, sem checar permissão específica",
      "solucao": "Adicionada verificação de permissão empresas.financeiro.visualizar ou empresas.financeiro.completo",
      "arquivo": "templates/clients/company_profile.php",
      "linhas_alteradas": ["326 - Aba/botão do Financeiro", "665-813 - Conteúdo da aba Financeiro"]
    }
  },

  "observacoes_finais": {
    "importante": [
      "Este sistema foi desenvolvido ao longo de muito tempo com várias iterações",
      "Muitas funcionalidades foram ajustadas após problemas encontrados em produção",
      "As melhores práticas documentadas aqui foram aprendidas através de erros e correções",
      "Seguir estas práticas evita retrabalho e problemas comuns",
      "Sempre consultar este arquivo antes de implementar novas funcionalidades",
      "Sistema preparado para multi-tenancy (rede de franquias) - ver documentação em docs/"
    ],
    "manutencao": [
      "Atualizar este arquivo quando novas funcionalidades forem adicionadas",
      "Documentar novos problemas encontrados e suas soluções",
      "Manter lista de lições aprendidas atualizada"
    ]
  }
}

