# 🚀 Sistema Compute - Campos Calculados Automáticos ## 📚 Índice 1. [Visão Geral](#visão-geral) 2. [Arquitetura](#arquitetura) 3. [Implementação](#implementação) 4. [Como Usar](#como-usar) 5. [Exemplos Práticos](#exemplos-práticos) 6. [Configuração Avançada](#configuração-avançada) 7. [Troubleshooting](#troubleshooting) 8. [Melhorias Futuras](#melhorias-futuras) --- ## 🎯 Visão Geral O sistema `compute` é uma solução **dinâmica e reutilizável** para campos calculados automaticamente em formulários Angular. Ele permite que campos sejam calculados em tempo real baseado em outros campos do formulário, sem necessidade de código específico por domínio. ### ✨ Características Principais - ✅ **100% Reutilizável** para qualquer domínio - ✅ **Detecção automática** de dependências - ✅ **Recálculo em tempo real** quando campos relacionados mudam - ✅ **Configuração declarativa** por campo - ✅ **Performance otimizada** com listeners inteligentes - ✅ **Tratamento de erros** robusto --- ## 🏗️ Arquitetura ### 📁 Estrutura de Arquivos ``` projects/idt_app/src/app/shared/ ├── interfaces/ │ └── generic-tab-form.interface.ts # Interface TabFormField estendida └── components/ └── generic-tab-form/ └── generic-tab-form.component.ts # Lógica de campos computados ``` ### 🔧 Componentes do Sistema 1. **Interface Estendida**: `TabFormField` com propriedades `compute` e `computeDependencies` 2. **Detector de Dependências**: Analisa funções `compute` automaticamente 3. **Listener Dinâmico**: Escuta mudanças apenas nos campos relevantes 4. **Calculador**: Executa funções `compute` e atualiza valores 5. **Gerenciador de Estado**: Preserva valores calculados durante edição --- ## 🚀 Implementação ### 1. Interface TabFormField Estendida ```typescript // projects/idt_app/src/app/shared/interfaces/generic-tab-form.interface.ts export interface TabFormField { // ... propriedades existentes ... // 🎯 NOVA: Propriedade para campos calculados compute?: (model: any) => any; // 🎯 NOVA: Dependências para campos computados computeDependencies?: string[]; } ``` **Propriedades:** - `compute`: Função que calcula o valor do campo - `computeDependencies`: Array de nomes dos campos que afetam o cálculo ### 2. Sistema de Detecção Automática ```typescript // projects/idt_app/src/app/shared/components/generic-tab-form/generic-tab-form.component.ts private getAllComputedFieldDependencies(): string[] { const allFields = this.getAllFieldsFromSubTabs(); const dependencies = new Set(); allFields.forEach(field => { if (field.compute) { // 🎯 Dependências explícitas (declaradas pelo desenvolvedor) if (field.computeDependencies) { field.computeDependencies.forEach(dep => dependencies.add(dep)); } // 🚀 DETECÇÃO AUTOMÁTICA: Analisar a função compute const autoDependencies = this.detectDependenciesFromComputeFunction(field); autoDependencies.forEach(dep => dependencies.add(dep)); } }); return Array.from(dependencies); } ``` ### 3. Detecção Inteligente de Dependências ```typescript private detectDependenciesFromComputeFunction(field: TabFormField): string[] { if (!field.compute) return []; try { const functionString = field.compute.toString(); const dependencies: string[] = []; // 🎯 Padrões comuns de acesso a propriedades const patterns = [ /model\.(\w+)/g, // model.propertyName /model\?\.(\w+)/g, // model?.propertyName /model\[['"`](\w+)['"`]\]/g, // model['propertyName'] /model\[`(\w+)`\]/g // model[`propertyName`] ]; patterns.forEach(pattern => { let match; while ((match = pattern.exec(functionString)) !== null) { const propertyName = match[1]; if (propertyName && !dependencies.includes(propertyName)) { dependencies.push(propertyName); } } }); return dependencies; } catch (error) { console.warn(`⚠️ Erro ao detectar dependências automáticas para ${field.key}:`, error); return []; } } ``` ### 4. Listener Dinâmico ```typescript private setupComputedFieldsListener(): void { if (!this.form) return; // 🚀 DINÂMICO: Detectar automaticamente todas as dependências const allDependencies = this.getAllComputedFieldDependencies(); if (allDependencies.length === 0) { console.log('ℹ️ [COMPUTE] Nenhum campo computado encontrado'); return; } console.log('🎯 [COMPUTE] Dependências detectadas:', allDependencies); // Escutar mudanças em TODOS os campos que afetam cálculos this.form.valueChanges.subscribe(value => { const hasRelevantChange = allDependencies.some(field => value[field] !== undefined && value[field] !== null ); if (hasRelevantChange) { this.updateComputedFields(); } }); } ``` ### 5. Atualização de Campos Computados ```typescript private updateComputedFields(): void { if (!this.form) return; const formValue = this.form.value; this.getAllFieldsFromSubTabs().forEach(field => { if (field.compute) { try { const computedValue = field.compute(formValue); if (computedValue !== undefined && computedValue !== null) { this.form.get(field.key)?.setValue(computedValue, { emitEvent: false }); } } catch (error) { console.warn(`⚠️ Erro ao recalcular campo ${field.key}:`, error); } } }); } ``` --- ## 📝 Como Usar ### 1. Configuração Básica de Campo Computado ```typescript { key: 'campo_calculado', label: 'Campo Calculado', type: 'number', readOnly: true, compute: (model: any) => { // Lógica de cálculo aqui return resultado; }, computeDependencies: ['campo1', 'campo2'] } ``` ### 2. Tipos de Campo Suportados ```typescript // ✅ Tipos que funcionam bem com compute type: 'number' // Para valores numéricos type: 'currency-input' // Para valores monetários type: 'text' // Para texto calculado type: 'date' // Para datas calculadas ``` ### 3. Estrutura da Função Compute ```typescript compute: (model: any) => { // 🎯 model contém todos os valores do formulário // 🎯 Acessar campos específicos const valor1 = Number(model?.campo1) || 0; const valor2 = Number(model?.campo2) || 0; // 🎯 Lógica de cálculo const resultado = valor1 + valor2; // 🎯 Retornar o valor calculado return resultado; } ``` --- ## 💡 Exemplos Práticos ### 1. Cálculo Financeiro (Veículos) ```typescript { key: 'calc_total_pago', label: 'Valor Total Pago', type: 'currency-input', readOnly: true, compute: (model: any) => { const parcela = Number(model?.alienation_payment_installment_value) || 0; const pagas = Number(model?.alienation_payment_installment_total) || 0; return parcela * pagas; }, computeDependencies: ['alienation_payment_installment_value', 'alienation_payment_installment_total'], formatOptions: { locale: 'pt-BR', useGrouping: true, suffix: ' R$' } } ``` ### 2. Cálculo de Idade (Motoristas) ```typescript { key: 'idade_calculada', label: 'Idade', type: 'number', readOnly: true, compute: (model: any) => { if (!model?.data_nascimento) return null; const nascimento = new Date(model.data_nascimento); const hoje = new Date(); const idade = hoje.getFullYear() - nascimento.getFullYear(); // Ajustar para mês/dia const mesAtual = hoje.getMonth(); const mesNascimento = nascimento.getMonth(); if (mesAtual < mesNascimento || (mesAtual === mesNascimento && hoje.getDate() < nascimento.getDate())) { return idade - 1; } return idade; }, computeDependencies: ['data_nascimento'] } ``` ### 3. Formatação de CNPJ (Empresas) ```typescript { key: 'cnpj_formatado', label: 'CNPJ Formatado', type: 'text', readOnly: true, compute: (model: any) => { const cnpj = model?.cnpj; if (!cnpj) return ''; // Remover caracteres não numéricos const cleanCnpj = cnpj.replace(/\D/g, ''); if (cleanCnpj.length !== 14) return cnpj; // Formatar: XX.XXX.XXX/XXXX-XX return cleanCnpj.replace( /(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/, '$1.$2.$3/$4-$5' ); }, computeDependencies: ['cnpj'] } ``` ### 4. Cálculo de Distância (Rotas) ```typescript { key: 'distancia_total', label: 'Distância Total', type: 'number', readOnly: true, compute: (model: any) => { const paradas = model?.stops || []; if (paradas.length < 2) return 0; let distanciaTotal = 0; for (let i = 1; i < paradas.length; i++) { const anterior = paradas[i - 1]; const atual = paradas[i]; const distancia = this.calcularDistancia( anterior.latitude, anterior.longitude, atual.latitude, atual.longitude ); distanciaTotal += distancia; } return Math.round(distanciaTotal * 100) / 100; // 2 casas decimais }, computeDependencies: ['stops'] } ``` --- ## ⚙️ Configuração Avançada ### 1. Dependências Múltiplas ```typescript { key: 'campo_complexo', compute: (model: any) => { // Campo depende de vários outros campos const valor1 = model?.campo1 || 0; const valor2 = model?.campo2 || 0; const valor3 = model?.campo3 || 0; return (valor1 + valor2) * valor3; }, // 🎯 Declarar TODAS as dependências computeDependencies: ['campo1', 'campo2', 'campo3'] } ``` ### 2. Validação Condicional ```typescript { key: 'campo_validado', compute: (model: any) => { const valor = model?.campo_base; // 🎯 Validação antes do cálculo if (!valor || valor <= 0) { return null; // Retornar null para indicar valor inválido } return valor * 2; }, computeDependencies: ['campo_base'] } ``` ### 3. Cálculos com Formatação ```typescript { key: 'valor_formatado', type: 'text', readOnly: true, compute: (model: any) => { const valor = Number(model?.valor_base) || 0; if (valor === 0) return 'R$ 0,00'; // 🎯 Formatação brasileira return valor.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL', minimumFractionDigits: 2 }); }, computeDependencies: ['valor_base'] } ``` --- ## 🔧 Troubleshooting ### ❌ Problemas Comuns e Soluções #### 1. Campo não está sendo recalculado **Sintomas:** - Campo computado não atualiza quando dependências mudam - Valor inicial não é calculado **Soluções:** ```typescript // ✅ Verificar se computeDependencies está correto computeDependencies: ['campo1', 'campo2'] // ✅ Verificar se a função compute retorna valor válido compute: (model: any) => { const valor = model?.campo1 || 0; return valor > 0 ? valor * 2 : 0; // Sempre retornar valor válido } ``` #### 2. Erro "Cannot read property of undefined" **Sintomas:** - Erro no console ao tentar calcular campo - Campo fica vazio **Soluções:** ```typescript // ✅ Usar operador de coalescência nula compute: (model: any) => { const valor = model?.campo1 ?? 0; // Usar ?? em vez de || return valor * 2; } // ✅ Verificar se campos existem antes de usar compute: (model: any) => { if (!model?.campo1 || !model?.campo2) return 0; return model.campo1 + model.campo2; } ``` #### 3. Performance lenta com muitos campos computados **Sintomas:** - Formulário lento ao digitar - Muitos recálculos desnecessários **Soluções:** ```typescript // ✅ Limitar dependências apenas aos campos essenciais computeDependencies: ['campo_essencial'] // Não incluir campos opcionais // ✅ Usar debounce para campos que mudam frequentemente // (Implementar no futuro se necessário) ``` ### 🔍 Debug e Logs ```typescript // ✅ Adicionar logs para debug compute: (model: any) => { console.log('🔍 [DEBUG] Modelo recebido:', model); const valor1 = Number(model?.campo1) || 0; const valor2 = Number(model?.campo2) || 0; console.log('🔍 [DEBUG] Valores:', { valor1, valor2 }); const resultado = valor1 + valor2; console.log('🔍 [DEBUG] Resultado:', resultado); return resultado; } ``` --- ## 🚀 Melhorias Futuras ### 1. Sistema de Cache Inteligente ```typescript // 🎯 Cache configurável por campo computeCache?: { enabled?: boolean; ttl?: number; // Time to live em ms strategy?: 'memory' | 'localStorage'; } ``` ### 2. Debounce para Campos Frequentes ```typescript // 🎯 Debounce para campos que mudam muito computeDebounce?: { enabled?: boolean; delay?: number; // ms } ``` ### 3. Validação de Dependências ```typescript // 🎯 Validação automática de dependências computeValidation?: { required?: boolean; minValue?: number; maxValue?: number; pattern?: RegExp; } ``` ### 4. Métricas de Performance ```typescript // 🎯 Monitoramento de performance computeMetrics?: { trackExecutionTime?: boolean; trackDependencyChanges?: boolean; logPerformance?: boolean; } ``` --- ## ✅ Checklist de Implementação ### ✅ Para Desenvolvedores - [ ] Campo tem propriedade `compute` definida - [ ] Campo tem `computeDependencies` declaradas - [ ] Campo é `readOnly: true` - [ ] Função `compute` retorna valor válido - [ ] Dependências estão corretas - [ ] Tratamento de erros implementado - [ ] Testado com valores vazios/nulos - [ ] Performance aceitável ### ✅ Para Testes - [ ] Campo é calculado ao carregar formulário - [ ] Campo é recalculado quando dependências mudam - [ ] Campo não é recalculado desnecessariamente - [ ] Tratamento de erros funciona - [ ] Performance é aceitável - [ ] Funciona em diferentes domínios --- ## 🎯 Conclusão O sistema `compute` para campos calculados é uma solução **robusta, escalável e reutilizável** que elimina a necessidade de código específico por domínio para cálculos automáticos. ### 🌟 Benefícios - **Produtividade**: Desenvolvimento mais rápido - **Manutenibilidade**: Código centralizado e limpo - **Reutilização**: Funciona em qualquer domínio - **Performance**: Otimizado e eficiente - **Flexibilidade**: Configurável por campo ### 🎯 Próximos Passos 1. Implementar em novos domínios 2. Adicionar testes automatizados 3. Monitorar performance em produção 4. Coletar feedback dos usuários 5. Implementar melhorias baseadas em uso real --- ## 📚 Referências ### Arquivos do Sistema - **Interface**: `projects/idt_app/src/app/shared/interfaces/generic-tab-form.interface.ts` - **Componente**: `projects/idt_app/src/app/shared/components/generic-tab-form/generic-tab-form.component.ts` - **Exemplo de Uso**: `projects/idt_app/src/app/domain/vehicles/vehicles.component.ts` ### Padrões Utilizados - **Registry Pattern**: Para configuração de formulários - **Observer Pattern**: Para listeners de mudanças - **Strategy Pattern**: Para diferentes tipos de cálculo - **Factory Pattern**: Para criação de campos computados --- **Autor**: PraFrota Development Team **Data**: $(date) **Versão**: 2.0.0 - Padronização Completa