Guia RegistreTudo

Conceitos, Padrões e Convenções do Projeto

Conceitos do Projeto RegistreTudo

Este documento serve como referência para entender a arquitetura, padrões e convenções do projeto RegistreTudo.

Visão Geral

O projeto RegistreTudo é composto por múltiplos repositórios e bibliotecas. É importante entender quais são editáveis e quais são apenas para uso.

Projetos Editáveis

Estes são os únicos projetos que devem ser modificados durante o desenvolvimento:

  1. @registretudo-api (Backend - Java)
    • Localização: C:/dotum/projetos/registretudo/registretudo-api
    • Tecnologia: Java 21, Maven, Framework DotumCore
    • É a API REST principal do sistema
    • Contém toda a lógica de negócio e exemplos de implementação
  2. @registretudo-web (Frontend - Next.js)
    • Localização: C:/dotum/projetos/dot/apps/registretudo-web
    • Tecnologia: Next.js 14, React 18, TypeScript, TailwindCSS
    • Aplicação web em migração do backend
    • ATENÇÃO: Contém componentes feitos às pressas e mal implementados. Sempre verifique a qualidade antes de usar como referência.

Bibliotecas e Frameworks (NÃO Editáveis)

Estes projetos são bibliotecas internas que podem ser usadas, mas não devem ser editados:

  1. @dot (Monorepo Root)
    • Localização: C:/dotum/projetos/dot
    • Gerenciador de monorepo (Turborepo)
    • Contém os pacotes @dot/core e @dot/ui
  2. @dot/ui (Biblioteca de Componentes)
    • Localização: C:/dotum/projetos/dot/packages/ui
    • Baseado em shadcn/ui
    • Componentes React reutilizáveis
    • Inclui: Form, Field, Table, Window, Sidebar, etc.
  3. @dot/core (Framework Core)
    • Localização: C:/dotum/projetos/dot/packages/core
    • Framework interno da Dotum
    • Serviços de API, autenticação, utilitários
    • Configurações e constantes
  4. @dotumcore (Framework Backend)
    • Localização: C:/dotum/projetos/registretudo/dotumcore
    • Framework Java interno
    • Usado pelo @registretudo-api
    • Não deve ser editado diretamente
  5. @dotumcommons (Biblioteca de Conexões com APIs Externas)
    • Localização: C:/dotum/projetos/registretudo/dotumcommons
    • Projeto separado para conexões com APIs externas
    • Integrações com serviços externos (Mailjet, Mercado Pago, Cielo)
    • Usado pelo @registretudo-api e outros projetos
    • Não deve ser editado diretamente
  6. @dotumfiscal (Biblioteca Fiscal)
    • Localização: C:/dotum/projetos/registretudo/dotumfiscal
    • Biblioteca Java para funcionalidades fiscais
    • Integração com NF-e, CT-e, MDF-e
    • Usado pelo @registretudo-api
    • Não deve ser editado diretamente

Arquitetura do Backend (@registretudo-api)

Estrutura de Pastas

src/main/java/dotumcloud/
├── api/              # Controllers REST (endpoints da API)
│   └── rt001commons/ # Endpoints de autocomplete (padrão novo)
├── bo/               # Business Objects (lógica de negócio)
├── model/
│   ├── bean/        # Entidades do Framework (tipo Bean próprio)
│   ├── dd/          # Default Data (tabelas com dados padrões)
│   └── json/        # DTOs JSON
├── screen###/       # Módulos de tela (screen005 = unidade, screen100 = venda, etc.)
│   └── [modulo]/
│       ├── dto/     # Data Transfer Objects (usam Record)
│       └── *.java   # Actions/Controllers
└── service/         # Classes para selects utilizados com frequência

Padrões de Nomenclatura

Controllers/Actions

Endpoints REST

DTOs (Data Transfer Objects)

Exemplo de DTO usando Record:

// screen100/venda/dto/DoProcessCancelarVendaRequest.java
public record DoProcessCancelarVendaRequest(String motivo) {
}

// api/rt001commons/dto/DoDataCommonUnidadeResponse.java
public record DoDataCommonUnidadeResponse(Integer id, String descricao) {
    public static DoDataCommonUnidadeResponse convert(UnidadeBean uniBean) {
        return new DoDataCommonUnidadeResponse(uniBean.getId(), uniBean.getDescricao());
    }
}

Exemplo de Controller

Padrão Antigo (em migração)

@UrlPath("/unidade/${id}/ocorrencia")
@Menu(label="Ocorrência", icon=CssIconEnum.COMMENT)
public class Unidade055Ocorrencia extends AbstractAction {

    @Endpoint
    public List<Bean> doDataTableOcorrencia(Integer id) throws Exception {
        // Retorna dados para tabela
    }

    @Endpoint(method = MethodHttp.POST)
    public void doProcess(Integer id) throws Exception {
        // Processa ação
    }
}

Padrão Novo (preferencial)

@UrlPath("/unidade/${id}/ocorrencia")
@Menu(label="Ocorrência", icon=CssIconEnum.COMMENT)
public class Unidade055Ocorrencia extends AbstractAction {

    @Endpoint
    public List<DoDataTableOcorrenciaResponse> doDataTableOcorrencia(@PathParam Integer id) throws Exception {
        // Retorna dados para tabela
    }

    @Endpoint(method = MethodHttp.POST)
    public void doProcess(@PathParam Integer id, @RequestBody DoProcessRequest body) throws Exception {
        // Processa ação
    }
}
⚠️ ATENÇÃO: O projeto está em processo de migração. Sempre use o padrão novo com @PathParam e @RequestBody para novos endpoints.

Beans (Entidades do Framework)

Default Data (DDs)

UserContext (Contexto do Usuário)

A classe UserContext é do framework DotumCore e resolve o problema "Who User" - identifica quem está fazendo a requisição. Ela contém todas as informações do usuário autenticado e é fundamental para segurança, auditoria e filtragem de dados.

Obtenção do UserContext

// Em classes que estendem AbstractAction
UserContext userContext = super.getUserContext();

Informações Disponíveis no UserContext

O UserContext contém informações sobre:

Métodos Principais

UserContext userContext = super.getUserContext();

// Obter ID da unidade do usuário logado
Integer unidadeId = userContext.getUnidade().getId();

// Obter ID da empresa do usuário logado
Integer empresaId = userContext.getEmpresa().getId();

// Obter ID da pessoa do usuário logado
Integer pessoaId = userContext.getPessoa().getId();

// Obter informações do usuário
UsuarioBean user = userContext.getUser();
String nomeUsuario = user.getNome();
String emailUsuario = user.getEmail();

// Obter ID do desenvolvedor (quando aplicável)
Integer devId = userContext.getUserDeveloperId();

Importância do UserContext

  1. Segurança e Isolamento de Dados: Garante que usuários só vejam/modifiquem dados da sua unidade/empresa
  2. Auditoria: Registra quem fez cada ação no sistema
  3. Filtragem Automática: Facilita filtrar dados por unidade/empresa do usuário
  4. Contexto de Negócio: Fornece contexto necessário para regras de negócio

Exemplos de Uso

Exemplo 1: Filtrar dados pela unidade do usuário
@Endpoint
public List<DoDataTableOcorrenciaResponse> doDataTableOcorrencia(@PathParam Integer id) throws Exception {
    // ✅ CORRETO: UserContext ANTES de TransactionDB
    UserContext userContext = super.getUserContext();
    TransactionDB trans = super.getTransaction();
    
    // ✅ CORRETO: Vincular tratamento de dados a variáveis
    Integer empresaId = userContext.getEmpresa().getId();
    Integer unidadeId = userContext.getUnidade().getId();
    
    WhereDB where = new WhereDB();
    // ✅ CORRETO: Ordem de filtros - empresa primeiro, depois unidade, depois outros
    where.add("empresa.id", Condition.EQUALS, empresaId);
    where.add("unidade.id", Condition.EQUALS, unidadeId);
    where.add("pessoa.id", Condition.EQUALS, id);
    
    OrderDB order = new OrderDB();
    order.add("data", Orientation.DESC);
    
    List<OcorrenciaBean> beanList = trans.select(OcorrenciaBean.class, where, order);
    
    return DoDataTableOcorrenciaResponse.convert(beanList);
}
Exemplo 2: Filtrar dados pela empresa do usuário
@Endpoint
public List<DoDataCommonUnidadeResponse> doDataCommonUnidade(
    @QueryParam String term, 
    @QueryParam Integer defaultValue
) throws Exception {
    // ✅ CORRETO: UserContext ANTES de TransactionDB
    UserContext userContext = super.getUserContext();
    TransactionDB trans = super.getTransaction();
    
    // ✅ CORRETO: Vincular tratamento de dados a variáveis
    Integer empId = userContext.getEmpresa().getId();
    
    WhereDB where = new WhereDB();
    // ✅ CORRETO: Ordem de filtros - empresa primeiro
    where.add("empresa.id", Condition.EQUALS, empId);
    
    // ✅ CORRETO: Para null pointer, pode usar != null diretamente
    if (term != null) {
        where.add("descricao", Condition.LIKE, term);
    }
    
    List<UnidadeBean> uniList = trans.select()
        .from("unidade")
        .where(where)
        .limit(FWConstants.JSON_LIMIT)
        .list();
    
    return DoDataCommonUnidadeResponse.convert(uniList);
}
Exemplo 3: Registrar quem fez uma ação (Auditoria) - CORRETO
⚠️ IMPORTANTE: Controllers NÃO devem fazer inserts/updates/deletes diretamente. Essas operações devem ser feitas SOMENTE em Business Objects (BOs).
@Endpoint(method = MethodHttp.POST)
public void doProcess(@PathParam Integer id, @RequestBody DoProcessRequest body) throws Exception {
    UserContext userContext = super.getUserContext();
    TransactionDB trans = super.getTransaction();
    
    // ✅ CORRETO: Chamar BO para fazer o insert
    OcorrenciaBO ocorrenciaBO = new OcorrenciaBO(userContext, trans);
    ocorrenciaBO.criarOcorrencia(id, body.descricao());
}
Exemplo 4: Usar informações do usuário em mensagens - CORRETO
⚠️ IMPORTANTE: SEMPRE vincule tratamento de dados a variáveis. NUNCA faça tratamento diretamente em chamadas de função ou ao setar em objetos.
public static void liberarUnidade(
    UserContext userContext, 
    TransactionDB trans, 
    UnidadeBean uniBean, 
    Date liberadoAte
) throws Exception {
    // ✅ CORRETO: Vincular tratamento de dados a variáveis
    String primeiroNome = StringUtils.getFirstWord(userContext.getUser().getNome());
    String nomeUsuario = StringUtils.toUpperCaseFirstLetter(primeiroNome);
    
    String dataFormatada = FormatUtils.formatDate(liberadoAte);
    String descricaoUnidade = uniBean.getDescricao();
    
    // ✅ CORRETO: Usar variáveis já tratadas na concatenação
    String descricao = "Liberado o sistema da unidade \"" + descricaoUnidade 
        + "\" até " + dataFormatada 
        + " pelo usuario " + nomeUsuario;
    

    OcorrenciaBO ocoBO = new OcorrenciaBO(userContext, trans);
    ocoBO.gerarOcorenciaPessoa(
        uniBean.getId(), 
        uniBean.getPessoaCliente().getId(), 
        OcotDD.SISTEMA, 
        trans.getDataAtual(), 
        trans.getHoraAtual(), 
        "Liberação Sistema", 
        descricao);
  
}
Exemplo 5: Passar UserContext para Business Objects
// Business Objects geralmente recebem UserContext no construtor
public class UnidadeBO extends AbstractBO {
    public UnidadeBO(UserContext userContext, TransactionDB trans) {
        super(userContext, trans);
    }
    
    public void informarDadosCadastro(String nome, String email) throws Exception {
        // UserContext já está disponível através do super
        // Pode ser usado para validações e regras de negócio
    }
}

⚠️ Boas Práticas com UserContext

  1. SEMPRE filtre dados pela unidade/empresa do usuário - Nunca retorne dados de outras unidades/empresas
  2. Use UserContext para auditoria - Sempre registre quem fez cada ação importante
  3. Não confie em parâmetros - Use userContext.getUnidade().getId() ao invés de receber unidadeId como parâmetro
  4. Passe UserContext para BOs e Services - Facilita acesso ao contexto em toda a camada de negócio
  5. Use para validações de acesso - Verifique se o usuário tem permissão antes de executar ações

Exemplo de Segurança (O que NÃO fazer)

// ❌ ERRADO: Aceitar unidadeId como parâmetro sem validar
@Endpoint
public void doProcess(@PathParam Integer unidadeId) throws Exception {
    // Usuário poderia passar ID de outra unidade!
    UnidadeBean uniBean = trans.selectById(UnidadeBean.class, unidadeId);
    // ...
}

// ✅ CORRETO: Usar UserContext para garantir segurança
@Endpoint
public void doProcess(@PathParam Integer id) throws Exception {
    UserContext userContext = super.getUserContext();
    
    // Sempre usar a unidade do usuário logado
    Integer empresaId = userContext.getEmpresa().getId();
    
    WhereDB where = new WhereDB();
    where.add("empresa.id", Condition.EQUALS, empresaId);  // Garantir que é da empresa do usuário
    where.add("id", Condition.EQUALS, id);
    
    UnidadeBean uniBean = trans.select().from("unidade").where(where).one();
    // ...
}

TransactionDB (Transações de Banco de Dados)

A classe TransactionDB é do framework DotumCore e representa uma transação no banco de dados. É obtida através de super.getTransaction() em classes que estendem AbstractAction.

Obtenção da Transação

TransactionDB trans = super.getTransaction();

Métodos de Select (Consultas)

1. Select por ID (Mais Simples)
// ⚠️ NÃO RECOMENDADO: Este padrão é mais pesado e traz objetos completos com todos os joins aninhados
// Busca um registro único pelo ID
PessoaBean pesBean = trans.selectById(PessoaBean.class, id);
UnidadeBean uniBean = trans.selectById(UnidadeBean.class, unidadeId);
2. Select com WhereDB e OrderDB (Padrão Tradicional - ⚠️ EVITAR)
// ⚠️ NÃO RECOMENDADO: Este padrão é mais pesado e traz objetos completos com todos os joins aninhados
WhereDB where = new WhereDB();
where.add("pessoa.id", Condition.EQUALS, id);
where.add("unidade.id", Condition.EQUALS, unidadeId);

OrderDB order = new OrderDB();
order.add("data", Orientation.DESC);
order.add("hora", Orientation.DESC);

List<OcorrenciaBean> beanList = trans.select(OcorrenciaBean.class, where, order);
⚠️ IMPORTANTE: Evite este padrão tradicional. Ele é mais pesado e traz o objeto completo com todos os objetos aninhados oriundos de joins, o que pode impactar significativamente a performance.
3. Select com Builder Pattern (Padrão Novo - Preferencial ✅)
// Select com where e limit
List<UnidadeBean> uniList = trans.select()
    .from("unidade")
    .where(where)
    .limit(FWConstants.JSON_LIMIT)
    .list();

// Select único (one)
PessoaBean pesBean = trans.select()
    .from("pessoa")
    .where(where)
    .one();

// Select com addWhere (encadeado)
CodigoPromocionalBean codpBean = trans.select()
    .from("codigoPromocional")
    .addWhere("codigo", Condition.EQUALS, codIndicacao)
    .one();

// Select com join() para trazer objetos aninhados completos
// Por padrão, o builder pattern só traz IDs dos objetos aninhados
// Use join() quando precisar do objeto completo
List<OcorrenciaBean> ocoList = trans.select()
    .from("ocorrencia")
    .join("pessoa")  // Traz o objeto PessoaBean completo
    .join("unidade") // Traz o objeto UnidadeBean completo
    .where(where)
    .list();
✅ VANTAGENS do Builder Pattern:
4. Select com ORM Customizado (Para queries complexas)
// ⚠️ IMPORTANTE: Use StringBuilder ao invés de String para queries complexas (mais rápido)
// Mantenha a indentação para facilitar copiar o select para debug
StringBuilder sb = new StringBuilder();
sb.append("                  select uni.uni_id,                                                                       ");
sb.append("                         uni.emp_id,                                                                       ");
sb.append("                         uni.uni_liberadoate,                                                              ");
sb.append("                         uni.uni_diavencimento,                                                            ");
sb.append("                         uni.uni_descricao,                                                                ");
sb.append("                         pes.pes_id,                                                                       ");
sb.append("                         pes.pes_nomerazao                                                                 ");
sb.append("                    from pessoa pes                                                                        ");
sb.append("                    join unidade uni on (uni.uni_id = pes.uni_id)                                          ");
sb.append("                   where pes.pes_principal = " + FWConstants.SIM + "                                       ");
sb.append("                     and uni.uni_ativo = " + FWConstants.ATIVO + "                                         ");
sb.append("                     and uni.uni_diavencimento is not null                                                 ");

// Mapeamento ORM para campos customizados
// ⚠️ IMPORTANTE: Mantenha a indentação alinhada (observe o padrão nos services)
// As vírgulas ficam no final da linha anterior, não indentadas
ORM orm = new ORM();
orm.add(Integer.class,                "empresa.id",                 "emp_id");
orm.add(Integer.class,                "id",                         "uni_id");
orm.add(Integer.class,                "pessoaCliente.id",           "pes_id");
orm.add(Date.class,                   "liberadoAte",                "uni_liberadoate");
orm.add(String.class,                 "descricao",                  "uni_descricao");
orm.add(Integer.class,                "diaVencimento",              "uni_diavencimento");
orm.add(String.class,                 "pessoaCliente.nomeRazao",    "pes_nomerazao");

WhereDB where = new WhereDB();
where.add("liberadoAte", Condition.GREATOREQUALS, dataLimite);
where.add("empresa.id", Condition.EQUALS, empresaId);

List<UnidadeBean> uniList = trans.select(
    UnidadeBean.class, 
    orm, 
    sb.toString(),  // StringBuilder convertido para String
    where
);
⚠️ IMPORTANTE para queries complexas:

Métodos de Insert (Inserção)

// Inserir um Bean
trans.insert(pesBean);
trans.insert(uniBean);

// Inserir uma Lista
trans.insert(pesList);

Métodos de Update (Atualização)

// Atualizar um Bean (após modificar seus atributos)
uniBean.setDescricao("Novo nome");
trans.update(uniBean);

Métodos de Delete (Exclusão)

// Deletar um Bean
trans.delete(pesBean);

Métodos Utilitários

// Obter data atual do banco
Date dataAtual = trans.getDataAtual();

// Obter hora atual do banco
String horaAtual = trans.getHoraAtual();

Exemplos Completos de Uso

Exemplo 1: Select simples com where e Constants - CORRETO

⚠️ ORDEM OBRIGATÓRIA:
@Endpoint
public List<DoDataTableOcorrenciaResponse> doDataTableOcorrencia(@PathParam Integer id) throws Exception {
    // ✅ CORRETO: UserContext ANTES de TransactionDB
    UserContext userContext = super.getUserContext();
    TransactionDB trans = super.getTransaction();
    
    Integer empresaId = userContext.getEmpresa().getId();
    Integer unidadeId = userContext.getUnidade().getId();
    
    WhereDB where = new WhereDB();
    // ✅ CORRETO: Ordem de filtros - empresa primeiro, depois unidade, depois outros
    where.add("empresa.id", Condition.EQUALS, empresaId);
    where.add("unidade.id", Condition.EQUALS, unidadeId);
    where.add("pessoa.id", Condition.EQUALS, id);
    // Usando DD para filtrar situações
    where.add("situacao.id", Condition.NOTEQUALS, SitDD.CANCELADO);
    
    OrderDB order = new OrderDB();
    order.add("data", Orientation.DESC);
    order.add("hora", Orientation.DESC);
    
    List<OcorrenciaBean> beanList = trans.select(OcorrenciaBean.class, where, order);
    
    return DoDataTableOcorrenciaResponse.convert(beanList);
}

Exemplo 2: Select com builder pattern e Constants (padrão novo) - CORRETO

⚠️ IMPORTANTE sobre Condicionais:
@Endpoint
public List<DoDataCommonUnidadeResponse> doDataCommonUnidade(
    @QueryParam String term, 
    @QueryParam Integer defaultValue
) throws Exception {
    // ✅ CORRETO: UserContext ANTES de TransactionDB
    UserContext userContext = super.getUserContext();
    TransactionDB trans = super.getTransaction();
    
    Integer empId = userContext.getEmpresa().getId();
    
    WhereDB where = new WhereDB();
    // ✅ CORRETO: Ordem de filtros - empresa primeiro
    where.add("empresa.id", Condition.EQUALS, empId);
    // Usando FWConstants para filtrar apenas ativos
    where.add("ativo", Condition.EQUALS, FWConstants.ATIVO);
    
    // ✅ CORRETO: Para null pointer, pode usar != null diretamente
    if (term != null) {
        where.add("descricao", Condition.LIKE, term);
    }
    
    if (defaultValue != null) {
        where.add("id", Condition.EQUALS, defaultValue);
    }
    
    List<UnidadeBean> uniList = trans.select()
        .from("unidade")
        .where(where)
        .limit(FWConstants.JSON_LIMIT)  // Usando FWConstants para limite
        .list();
    
    return DoDataCommonUnidadeResponse.convert(uniList);
}

Exemplo 3: Select único (one) com Constants

WhereDB where = new WhereDB();
where.add("id", Condition.EQUALS, id);
// Usando FWConstants para garantir que está ativo
where.add("ativo", Condition.EQUALS, FWConstants.ATIVO);

PessoaBean pesBean = trans.select()
    .from("pessoa")
    .where(where)
    .one();

Exemplo 4: Insert e Update com Constants - CORRETO (em BOs, não em Controllers)

⚠️ IMPORTANTE: Inserts, updates e deletes devem ser feitos SOMENTE em Business Objects (BOs), NUNCA em Controllers.
// ✅ CORRETO: Em um Business Object (BO)
public class OcorrenciaBO extends AbstractBO {
    
    public void criarOcorrencia(Integer uniId, String descricao) throws Exception {
        UserContext userContext = super.getUserContext();
        TransactionDB trans = super.getTransaction();
        
        // ✅ CORRETO: Vincular tratamento de dados a variáveis
        Date dataAtual = trans.getDataAtual();
        Time horaAtual = trans.getHoraAtual();
        
        // Criar novo registro usando Constants
        OcorrenciaBean ocoBean = new OcorrenciaBean();
        ocoBean.getUnidade().setId(uniId);
        ocoBean.setDescricao(descricao);
        ocoBean.setData(dataAtual);
        ocoBean.setHora(horaAtual);
        // Usando DD para situação
        ocoBean.getSituacao().setId(SitDD.PENDENTE);
        // Usando DD para tipo de ocorrência
        ocoBean.getOcorrenciaTipo().setId(OcotDD.SISTEMA);
        trans.insert(ocoBean);
    }
    
    public void atualizarOcorrencia(Integer id, String novaDescricao) throws Exception {
        TransactionDB trans = super.getTransaction();
        
        // Atualizar registro existente
        OcorrenciaBean ocoBean = trans.selectById(OcorrenciaBean.class, id);
        ocoBean.setDescricao(novaDescricao);
        
        // Usando DD para marcar como concluído
        ocoBean.getSituacao().setId(SitDD.CONCLUIDO);
        trans.update(ocoBean);
    }
}

Condições WhereDB

⚠️ IMPORTANTE: Leia adequadamente a classe WhereDB. Alguns exemplos estão usando operações incorretas. Sempre verifique a documentação ou exemplos corretos no código.
⚠️ ORDEM DE FILTROS: Sempre siga a ordem de grandeza (maior para menor):
  1. Empresa (sempre primeiro)
  2. Unidade (quando tem, depois da empresa)
  3. Outros dados (depois de empresa/unidade)
⚠️ CONDICIONAIS COM BOOLEAN:
WhereDB where = new WhereDB();

// ✅ CORRETO: Ordem de filtros - empresa primeiro, depois unidade, depois outros
where.add("empresa.id", Condition.EQUALS, empresaId);
where.add("unidade.id", Condition.EQUALS, unidadeId);
where.add("id", Condition.EQUALS, id);

// Igualdade
where.add("id", Condition.EQUALS, id);

// Diferente
where.add("situacao.id", Condition.NOTEQUALS, SitDD.CANCELADO);

// LIKE (busca parcial)
where.add("descricao", Condition.LIKE, term);

// Exemplo de condicionais corretas:
// ✅ CORRETO: Para null pointer, pode usar != null diretamente
if (term != null) {
    where.add("descricao", Condition.LIKE, term);
}

// ✅ CORRETO: Quando já tem um boolean tratado, use == true ou == false
boolean isAtivo = uniBean.getAtivo() == FWConstants.SIM;
if (isAtivo == true) {
    where.add("ativo", Condition.EQUALS, FWConstants.ATIVO);
}

// ✅ CORRETO: Em chamada de função que retorna boolean, use == true ou == false
if (uniBean.isLiberado() == true) {
    where.add("liberado", Condition.EQUALS, FWConstants.SIM);
}

// ❌ ERRADO: Usar classe Boolean (wrapper)
Boolean isAtivo = uniBean.getAtivo(); // NUNCA faça isso

// ❌ ERRADO: Usar negação em boolean tratado
if (!isAtivo) { // NUNCA faça isso, use isAtivo == false

// Maior ou igual / Menor ou igual
where.add("data", Condition.GREATOREQUALS, dataInicio);
where.add("valor", Condition.LESSOREQUALS, valorMaximo);

// IN (lista de valores)
where.add("id", Condition.IN, listaIds);

// IS NULL / IS NOT NULL
where.add("dataCancelamento", Condition.ISNULL, null);
where.add("ativo", Condition.ISNOTNULL, null);

⚠️ Uso de Constants nos Filtros (IMPORTANTE)

SEMPRE use Constants ao invés de valores literais quando elas existirem. Isso melhora a manutenibilidade e evita erros.

FWConstants (Constantes Gerais do Framework)
WhereDB where = new WhereDB();

// ✅ CORRETO: Usar FWConstants
where.add("principal", Condition.EQUALS, FWConstants.SIM);
where.add("ativo", Condition.EQUALS, FWConstants.ATIVO);
where.add("pagouMensalidade", Condition.EQUALS, FWConstants.NAO);

// ❌ ERRADO: Usar valores literais
where.add("principal", Condition.EQUALS, 1);  // Evitar!
where.add("ativo", Condition.EQUALS, 1);     // Evitar!

FWConstants comuns:

ParametroConstants (Parâmetros do Sistema)
WhereDB where = new WhereDB();

// ✅ CORRETO: Usar ParametroConstants
where.add("unidade.id", Condition.EQUALS, ParametroConstants.UNIDADE_DOTUM);
where.add("pessoa.id", Condition.EQUALS, ParametroConstants.PESSOA_PRINCIPAL_DOTUM);

// Usar em cálculos de datas
Date dataLimite = DateUtils.addDay(dataAtual, (ParametroConstants.CARENCIA_USO * -1));
where.add("liberadoAte", Condition.GREATOREQUALS, dataLimite);

// ❌ ERRADO: Usar valores literais
where.add("unidade.id", Condition.EQUALS, 1);  // Evitar!

ParametroConstants comuns:

DDs (Default Data) - Para Situações e Tipos
WhereDB where = new WhereDB();

// ✅ CORRETO: Usar DDs para situações e tipos
where.add("situacao.id", Condition.EQUALS, SitDD.ATIVO);
where.add("situacao.id", Condition.EQUALS, SitDD.CONCLUIDO);
where.add("situacao.id", Condition.NOTEQUALS, SitDD.CANCELADO);
where.add("situacao.id", Condition.EQUALS, SitDD.PENDENTE);

// Para tipos de ocorrência
where.add("ocorrenciaTipo.id", Condition.EQUALS, OcotDD.SISTEMA);
where.add("ocorrenciaTipo.id", Condition.EQUALS, OcotDD.FEEDBACK_DOTUM);

// Para planos de sistema
PlanoSistemaBean psisBean = trans.selectById(PlanoSistemaBean.class, PsiDD.BRONZE);

// ❌ ERRADO: Usar valores literais
where.add("situacao.id", Condition.EQUALS, 90);  // Evitar!
where.add("situacao.id", Condition.EQUALS, 91);  // Evitar!

DDs comuns:

Exemplo Completo com Constants
WhereDB where = new WhereDB();

// Usando FWConstants
where.add("principal", Condition.EQUALS, FWConstants.SIM);
where.add("ativo", Condition.EQUALS, FWConstants.ATIVO);

// Usando DDs
where.add("situacao.id", Condition.EQUALS, SitDD.CONCLUIDO);

// Usando ParametroConstants
where.add("unidade.id", Condition.EQUALS, ParametroConstants.UNIDADE_DOTUM);

// Combinando com outras condições
where.add("data", Condition.GREATOREQUALS, DateUtils.addDay(
    trans.getDataAtual(), 
    (ParametroConstants.CARENCIA_USO * -1)
));
⚠️ REGRA DE OURO:

Ordenação (OrderDB)

OrderDB order = new OrderDB();

// Ordenação ascendente
order.add("nome", Orientation.ASC);

// Ordenação descendente
order.add("data", Orientation.DESC);
order.add("hora", Orientation.DESC); // Múltiplas ordenações

⚠️ Importante - Boas Práticas

Comparação de Performance

❌ Padrão Tradicional (Evitar):

// Traz objeto completo com TODOS os joins aninhados carregados
List<OcorrenciaBean> list = trans.select(OcorrenciaBean.class, where, order);
// OcorrenciaBean.pessoa está completo, OcorrenciaBean.unidade está completo, etc.

✅ Builder Pattern (Preferencial):

// Traz apenas IDs dos objetos aninhados (mais leve)
List<OcorrenciaBean> list = trans.select()
    .from("ocorrencia")
    .where(where)
    .list();
// OcorrenciaBean.pessoa.id está preenchido, mas pessoa completa não está carregada

// Use join() apenas quando precisar do objeto completo
List<OcorrenciaBean> list = trans.select()
    .from("ocorrencia")
    .join("pessoa")  // Agora pessoa está completo
    .where(where)
    .list();

Classes Utilitárias (Utils) do DotumCore

O DotumCore fornece diversas classes utilitárias no pacote br.com.dotum.jedi.util que facilitam operações comuns. SEMPRE prefira usar essas classes ao invés de implementar lógica manual, pois elas são testadas, otimizadas e seguem padrões do framework.

⚠️ Recomendação Importante

SEMPRE busque e utilize as classes Utils do DotumCore antes de implementar lógica manual. Elas são:

DateUtils (Manipulação de Datas)

Classe para operações com datas: cálculos, formatação e parsing.

Import:

import br.com.dotum.jedi.util.DateUtils;

Métodos Principais:

Adicionar/Subtrair Dias
// Adicionar dias a uma data
Date dataFutura = DateUtils.addDay(dataAtual, 30);
Date dataPassada = DateUtils.addDay(dataAtual, -10);  // Subtrair dias

// Exemplo: Calcular data de vencimento
Date primeiroVencimento = DateUtils.addDay(trans.getDataAtual(), 30);

// Exemplo: Calcular data limite com carência
Date dataLimite = DateUtils.addDay(dataAtual, (ParametroConstants.CARENCIA_USO * -1));
Adicionar/Subtrair Meses
// Adicionar meses a uma data
Date proximoMes = DateUtils.addMonth(dataAtual, 1);
Date proximos3Meses = DateUtils.addMonth(dataAtual, 3);

// Exemplo: Calcular data de liberação
Date liberadoAte = DateUtils.addMonth(trans.getDataAtual(), qtdMesLiberado);
if (qtdMesLiberado == null) {
    liberadoAte = DateUtils.addMonth(trans.getDataAtual(), 1);
}
Calcular Diferença entre Datas
// Calcular dias entre duas datas
Integer diasEntre = DateUtils.daysBetween(dataInicio, dataFim);

// Exemplo: Verificar quantos dias faltam para vencimento
Integer diff = DateUtils.daysBetween(dataAtual, liberadoAte);
if (diff > 0 && diff <= 3) {
    // Avisar que está próximo do vencimento
}
Formatar Data
// Formatar data para string
String dataFormatada = DateUtils.formatDate(dataAtual, "dd/MM/yyyy");
String mesAno = DateUtils.formatDate(dataAtual, "MM/yyyy");

// Exemplo: Formatar data para exibição
String mesAno = DateUtils.formatDate(liberadoTemp, "MM/yyyy");
Parse de Data (String para Date)
// Converter string para Date
Date data = DateUtils.parseDate("31/12/2024");
Date dataCompleta = DateUtils.parseDate(diaVencimento + "/" + mesAno);

// Exemplo: Parse de data com dia e mês/ano
String mesAno = DateUtils.formatDate(liberadoTemp, "MM/yyyy");
Date dataCalculada = DateUtils.parseDate(diaVencimento + "/" + mesAno);

Casos de Uso Comuns:

StringUtils (Manipulação de Strings)

Classe para operações com strings: validação, transformação e tratamento.

Import:

import br.com.dotum.jedi.util.StringUtils;

Métodos Principais:

Verificar se String tem Valor
// Verificar se string não é null, vazia ou apenas espaços
if (StringUtils.hasValue(senha) == true) {
    // String tem valor
}

if (StringUtils.hasValue(pessoaNomeRazao) == false) {
    // String está vazia ou null
}

// Exemplo: Validar senha antes de processar
if (StringUtils.hasValue(senha) == true) {
    // Processar alteração de senha
}
Coalesce (Valor Padrão)
// Retorna o primeiro valor não nulo/vazio, ou valor padrão
String observacao = StringUtils.coalesce(observacao, "");
String nome = StringUtils.coalesce(nome, "Não informado");

// Exemplo: Garantir que observação nunca seja null
observacao = StringUtils.coalesce(observacao, "");
String observacaoTemp = "Cliente: " + pessoaNomeRazao + " Obs: " + observacao;
Primeira Letra Maiúscula
// Transformar primeira letra em maiúscula
String nomeFormatado = StringUtils.toUpperCaseFirstLetter("joão");
// Retorna: "João"

// Exemplo: Formatar nome do usuário
String nomeUsuario = StringUtils.toUpperCaseFirstLetter(
    StringUtils.getFirstWord(userContext.getUser().getNome())
);
Primeira Palavra
// Obter primeira palavra de uma string
String primeiraPalavra = StringUtils.getFirstWord("João Silva Santos");
// Retorna: "João"

// Exemplo: Usar apenas primeiro nome
String nomeUsuario = StringUtils.toUpperCaseFirstLetter(
    StringUtils.getFirstWord(userContext.getUser().getNome())
);

Casos de Uso Comuns:

FormatUtils (Formatação)

Classe para formatação de valores: datas, números, moedas, etc.

Import:

import br.com.dotum.jedi.util.FormatUtils;

Métodos Principais:

Formatar Data
// Formatar data para exibição
String dataFormatada = FormatUtils.formatDate(liberadoAte);
// Retorna data formatada no padrão brasileiro (dd/MM/yyyy)

// Exemplo: Incluir data formatada em mensagem
String descricao = "Liberado o sistema da unidade \"" 
    + pesClienteBean.getNomeRazao() 
    + "\" até " + FormatUtils.formatDate(liberadoAte) 
    + " pelo usuario " + nomeUsuario;

Casos de Uso Comuns:

MathUtils (Operações Matemáticas)

Classe para operações matemáticas seguras: multiplicação, divisão, comparações, etc.

Import:

import br.com.dotum.jedi.util.MathUtils;

Métodos Principais:

Multiplicação Segura
// Multiplicação que trata nulls e valores inválidos
BigDecimal resultado = MathUtils.multiply(valorBase, qtdMesLiberado);

// Exemplo: Calcular valor total
if (qtdMesLiberado != null) {
    valorTotal = MathUtils.multiply(valorBase, qtdMesLiberado);
}
Verificar se Valor é Maior que Zero
// Verificar se BigDecimal é maior que zero
if (MathUtils.greatZero(valorFrete) == true) {
    // Processar frete
}

// Exemplo: Validar valores antes de processar
if (MathUtils.greatZero(valorFrete) == true) {
    // Adicionar frete ao total
}

Casos de Uso Comuns:

Exemplos Completos de Uso

Exemplo 1: Calcular Data de Liberação com Validações

Date liberadoAte = null;
Integer diaVencimento = uniBean.getDiaVencimento();

if (diaVencimento != null) {
    Date liberadoTemp = trans.getDataAtual();
    String mesAno = DateUtils.formatDate(liberadoTemp, "MM/yyyy");
    liberadoTemp = DateUtils.parseDate(diaVencimento + "/" + mesAno);
    
    Integer qtdMesLiberado = psisBean.getQuantidadeMesLiberado();
    if (qtdMesLiberado != null) {
        liberadoAte = DateUtils.addMonth(liberadoTemp, qtdMesLiberado);
    } else {
        liberadoAte = DateUtils.addMonth(liberadoTemp, 1);
    }
} else {
    liberadoAte = DateUtils.addMonth(trans.getDataAtual(), 1);
}

Exemplo 2: Formatar Mensagem com Dados do Usuário

String nomeUsuario = StringUtils.toUpperCaseFirstLetter(
    StringUtils.getFirstWord(userContext.getUser().getNome())
);

String descricao = "Liberado o sistema da unidade \"" 
    + pesClienteBean.getNomeRazao() 
    + "\" até " + FormatUtils.formatDate(liberadoAte) 
    + " pelo usuario " + nomeUsuario;

Exemplo 3: Validar e Processar Dados de Entrada

// Validar senha
if (StringUtils.hasValue(senha) == true) {
    // Processar alteração de senha
    pesBean.setSenha(senha);
}

// Garantir que observação não seja null
observacao = StringUtils.coalesce(observacao, "");

// Validar valor de frete
if (MathUtils.greatZero(valorFrete) == true) {
    // Adicionar frete ao cálculo
}

Exemplo 4: Calcular Diferença de Dias para Alertas

Date dataAtual = trans.getDataAtual();
Date liberadoAte = uniBean.getLiberadoAte();

// Calcular dias até vencimento
Integer diff = DateUtils.daysBetween(dataAtual, liberadoAte);

if (diff > 0 && diff <= 3) {
    String titulo = "Pagamento não identificado";
    String mensagem = "Não identificamos o pagamento, seu plano vence em " 
        + diff + " dias. Evite o bloqueio do sistema";
    
    // Enviar notificação
    ntfBO.informaDados(
        pesBean.getUsuario(), 
        titulo, 
        mensagem, 
        null, 
        FwnType.WARNING, 
        DateUtils.addDay(dataAtual, 1)
    );
}

⚠️ Boas Práticas com Utils

  1. SEMPRE prefira Utils ao invés de implementar lógica manual
  2. Consulte a documentação das Utils antes de criar métodos próprios
  3. Use métodos específicos - cada Utils tem métodos otimizados para casos específicos
  4. Trate nulls adequadamente - Utils geralmente tratam nulls, mas verifique a documentação
  5. Combine Utils - Use múltiplas Utils juntas para operações complexas
  6. Mantenha consistência - Use as mesmas Utils em todo o projeto

Lista de Classes Utils Disponíveis

As principais classes Utils do DotumCore são:

⚠️ IMPORTANTE: Sempre verifique se existe uma classe Utils antes de implementar lógica manual. Consulte a documentação do DotumCore ou explore o pacote br.com.dotum.jedi.util para ver todas as classes disponíveis.

Services

Autocompletes (Padrão Novo)

No padrão novo, os dados de autocompletes são oriundos de endpoints específicos:

Exemplo de Autocomplete

Backend:

// api/rt001commons/UnidadeApi030.java
@UrlPath("/commons/unidade")
public class UnidadeApi030 extends AbstractAction {
    
    @Endpoint
    public List<DoDataCommonUnidadeResponse> doDataCommonUnidade(
        @QueryParam String term, 
        @QueryParam Integer defaultValue
    ) throws Exception {
        // Lógica de busca...
        return DoDataCommonUnidadeResponse.convert(uniList);
    }
}

// api/rt001commons/dto/DoDataCommonUnidadeResponse.java
public record DoDataCommonUnidadeResponse(Integer id, String descricao) {
    public static List<DoDataCommonUnidadeResponse> convert(List<UnidadeBean> uniList) {
        return uniList.stream().map(DoDataCommonUnidadeResponse::convert).toList();
    }
}

Frontend:

// components/field-helper/unidade-field.tsx
export default function UnidadeField({...}: Props) {
  const autoComplete = useAutocomplete({
    name,
    queryFn: async ({ term, defaultValue }) => {
      const res = await CommonsApi.doDataCommonUnidade(term, defaultValue as number);
      return res.data;
    },
  });

  return <AutocompleteField {...autoComplete} />;
}

Como Encontrar Exemplos no Backend

O backend é a fonte de verdade para exemplos de implementação. Para encontrar exemplos:

  1. Procure por módulos similares em screen###/
  2. Veja como os DTOs são estruturados em screen###/[modulo]/dto/ (todos usam Record)
  3. Verifique os Business Objects em bo/ para lógica de negócio
  4. Consulte os serviços em service/ para selects frequentes (não para regras de negócio)
  5. Para autocompletes, veja exemplos em api/rt001commons/

Arquitetura do Frontend (@registretudo-web)

Estrutura de Pastas

src/
├── app/
│   ├── (private)/        # Rotas privadas (requerem autenticação)
│   │   └── [modulo]/     # Módulos da aplicação
│   │       ├── page.tsx  # Página principal do módulo
│   │       └── components/  # Componentes específicos do módulo
│   ├── (Public)/        # Rotas públicas
│   └── layout.tsx       # Layouts
├── components/           # Componentes globais
│   └── field-helper/    # Componentes de autocomplete reutilizáveis (padrão novo)
├── services/            # Serviços de API
│   ├── commons/         # Serviços para api/rt001commons (autocompletes)
│   └── [modulo]/
│       ├── index.ts     # Funções de API
│       └── schemas/     # Tipos TypeScript (schemas)
├── hooks/               # React Hooks customizados
├── constants/           # Constantes e enums
└── utils/               # Funções utilitárias

Padrões de Nomenclatura

Serviços de API

Schemas (Tipos TypeScript com Zod)

⚠️ IMPORTANTE: Para construir DTOs no Frontend, NÃO utilizamos interfaces genéricas. Utilizamos Zod para validação e tipagem.

Exemplo de Schema com Zod:

// services/unidade/schemas/DoProcessAlterarUnidadeRequest.ts
import { z } from "zod";

export const doProcessAlterarUnidadeSchema = z.object({
  descricao: z.string(),
  diaVencimento: z.number().optional(),
  liberadoAte: z.string().optional(),
});

export type DoProcessAlterarUnidadeRequest = z.infer<
  typeof doProcessAlterarUnidadeSchema
>;

// services/unidade/schemas/DoDataTableOcorrenciaResponse.ts
import { z } from "zod";

export const doDataTableOcorrenciaSchema = z.object({
  id: z.number(),
  descricao: z.string(),
  data: z.date(),
  hora: z.string(),
  situacao: z.string(),
});

export type DoDataTableOcorrenciaResponse = z.infer<
  typeof doDataTableOcorrenciaSchema
>;

Componentes

Exemplo de Serviço

// src/services/unidade/index.ts
const URL_PATH = "/unidade";

async function doDataTableOcorrencia(id: number) {
  const res = await api.get<DotumResponse<DoDataTableOcorrenciaResponse>>(
    `${URL_PATH}/${id}/info/ocorrencia`
  );
  return res.data.data;
}

async function doProcessAlterarUnidade(
  id: number,
  body: DoProcessAlterarUnidadeRequest
) {
  const res = await api.post<DotumResponse>(
    `${URL_PATH}/${id}`,
    body
  );
  return res.data;
}

export const UnidadeApi = {
  doDataTableOcorrencia,
  doProcessAlterarUnidade,
};

Componentes da Biblioteca @dot/ui

A biblioteca @dot/ui fornece componentes baseados em shadcn/ui:

Componentes Principais

Exemplo de Uso

import { Form } from "@dot/ui/form";
import { TextField, ComboField } from "@dot/ui/field";
import { Window } from "@dot/ui/window";
import { useWindow } from "@dot/ui/window";

⚠️ ATENÇÃO: Componentes Mal Feitos e Processo de Migração

O frontend está em migração e contém componentes feitos às pressas. SEMPRE:

  1. Verifique a qualidade antes de usar como referência
  2. Prefira exemplos do backend quando possível
  3. Compare múltiplos exemplos para identificar padrões corretos
  4. ⚠️ NÃO REFATORE NADA - Como está em processo de migração com diversas pessoas trabalhando, refatorações podem causar conflitos
  5. Apenas aponte pontos de melhoria quando necessário, mas não implemente refatorações
  6. Foque apenas no que é necessário para a tarefa atual

Sinais de componentes mal feitos:


Fluxo de Dados

Backend → Frontend

  1. Backend define DTOs em screen###/[modulo]/dto/
    • Todos usam Record do Java
    • Todos têm método convert
  2. Frontend cria schemas TypeScript em src/services/[modulo]/schemas/
    • Nomenclatura idêntica aos DTOs do backend
    • Tipos correspondem aos tipos Java
  3. Frontend cria serviços em src/services/[modulo]/index.ts
    • Funções doData* para GET
    • Funções doProcess* para POST
  4. Componentes usam React Query para chamar serviços
    • useQuery para dados (GET)
    • useMutation para ações (POST)

⚠️ IMPORTANTE: Uso do useQuery

O queryFn do useQuery SEMPRE deve ser assíncrono e retornar res.data:

❌ ERRADO:

const { data } = useQuery({
  queryKey: ["ocorrencia", id],
  queryFn: () => UnidadeApi.doDataTableOcorrencia(id),
});

✅ CORRETO:

const { data } = useQuery({
  queryKey: ["ocorrencia", id],
  queryFn: async () => {
    const res = await UnidadeApi.doDataTableOcorrencia(id);
    return res.data;
  },
});
⚠️ ATENÇÃO: Sempre use async na queryFn e retorne res.data explicitamente.

Exemplo Completo

Backend (Padrão Novo):

// Unidade055Ocorrencia.java
@Endpoint
public List<DoDataTableOcorrenciaResponse> doDataTableOcorrencia(@PathParam Integer id) throws Exception {
    // ...
}

Frontend Schema:

// services/unidade/schemas/DoDataTableOcorrenciaResponse.ts
export interface DoDataTableOcorrenciaResponse {
  id: number;
  data: string;
  descricao: string;
  // ...
}

Frontend Service:

// services/unidade/index.ts
async function doDataTableOcorrencia(id: number) {
  const res = await api.get<DotumResponse<DoDataTableOcorrenciaResponse>>(
    `${URL_PATH}/${id}/info/ocorrencia`
  );
  return res.data.data;
}

Frontend Component:

// components/ocorrencia-table.tsx
const { data } = useQuery({
  queryKey: ["ocorrencia", id],
  queryFn: async () => {
    const res = await UnidadeApi.doDataTableOcorrencia(id);
    return res.data;
  },
});

Onde Procurar Exemplos

Para Backend (Java)

✅ SEMPRE use o backend como referência principal
  1. Procure módulos similares em screen###/
  2. Veja DTOs em screen###/[modulo]/dto/
  3. Verifique Business Objects em bo/
  4. Consulte serviços em service/

Para Frontend (React/Next.js)

⚠️ Cuidado: alguns componentes são ruins
  1. Primeiro: Verifique o backend correspondente
  2. Segundo: Compare múltiplos exemplos no frontend
  3. Terceiro: Use componentes de @dot/ui como base
  4. Evite: Componentes que parecem mal estruturados

Módulos de Referência (Bons Exemplos)

Backend

Frontend


Tecnologias e Ferramentas

Backend

Frontend

Bibliotecas Compartilhadas


Convenções Importantes

Nomenclatura de Arquivos

Nomenclatura de Funções/Métodos

Estrutura de Módulos

Tratamento de Erros


Boas Práticas

Backend

⚠️ REGRAS CRÍTICAS - NUNCA VIOLAR:
  1. Controllers NÃO devem fazer inserts/updates/deletes - Essas operações devem ser feitas SOMENTE em Business Objects (BOs)
  2. Ordem de chamadas OBRIGATÓRIA: UserContext sempre ANTES de TransactionDB
  3. Ordem de filtros OBRIGATÓRIA: empresa primeiro, depois unidade (quando tem), depois outros dados (ordem de grandeza: maior para menor)
  4. SEMPRE vincule tratamento de dados a variáveis - NUNCA faça tratamento diretamente em chamadas de função ou ao setar em objetos
  5. Condicionais com boolean: Use == true ou == false SOMENTE quando já tem um boolean tratado (variável boolean) ou em chamada de função. Para null pointer checks, pode usar != null diretamente. NUNCA use a classe Boolean (wrapper), sempre use o primitivo boolean
  6. Leia adequadamente a classe WhereDB - Verifique exemplos corretos no código antes de usar
  1. Sempre estenda AbstractAction para controllers
  2. Use @Endpoint para expor métodos REST
  3. Use @PathParam e @RequestBody no padrão novo (não use parâmetros diretos)
  4. Todos os DTOs devem usar Record do Java
  5. Todos os responses devem ser tipados como DTOs (não retornar Bean diretamente)
  6. Para autocompletes, use endpoints em api/rt001commons com @QueryParam
  7. Separe lógica de negócio em BO classes (não em Services)
  8. Services são apenas para selects frequentes, não para regras de negócio
  9. Valide dados de entrada
  10. Use transações adequadamente
  11. SEMPRE use Constants (FWConstants, ParametroConstants, DDs) ao invés de valores literais
  12. Prefira o builder pattern do TransactionDB para selects
  13. Use StringBuilder para queries complexas
  14. Mantenha indentação nas queries SQL e ORM
  15. SEMPRE use UserContext para segurança e auditoria
  16. Prefira Utils do DotumCore ao invés de implementar lógica manual

Frontend

⚠️ REGRA CRÍTICA - NUNCA VIOLAR:
  1. Use TypeScript para tipagem forte
  2. Separe serviços de API dos componentes
  3. Use React Query para cache e sincronização
  4. SEMPRE use async na queryFn do useQuery e retorne res.data quando apropriado
  5. Para autocompletes, use componentes de components/field-helper/ (padrão novo)
  6. Componentes devem ser pequenos e focados
  7. Reutilize componentes de @dot/ui e field-helper
  8. Valide formulários com schemas Zod
  9. Sempre verifique qualidade antes de copiar código
  10. ⚠️ NÃO REFATORE - Apenas aponte melhorias quando necessário

Geral

  1. Mantenha consistência com padrões existentes
  2. Documente código complexo
  3. Evite duplicação
  4. ⚠️ NÃO REFATORE - Apenas aponte melhorias, pois pode causar conflitos com outras pessoas
  5. Foque apenas no necessário para a tarefa atual
  6. Teste antes de commitar

Checklist para Desenvolvimento

Antes de começar uma nova feature:


Recursos Adicionais


Última atualização: Este documento deve ser atualizado conforme o projeto evolui e novos padrões são estabelecidos.