Relatórios técnicos de desentupimento de esgoto no painel WordPress — Automação e Dados

Como criar relatórios técnicos de desentupimento no WordPress com custom post types, ACF e dashboards administrativos para equipes de campo.

Como transformar planilhas soltas e PDFs anexados em e-mails num painel único onde técnicos preenchem laudos no celular e gestores acompanham indicadores em tempo real? A resposta está em tratar o relatório técnico como entidade de domínio no WordPress, não como conteúdo de blog.

Muitas equipes de desentupimento ainda dependem de formulários em papel digitalizados depois, o que atrasa faturamento, dificulta auditoria e impede análise de padrões — como trechos recorrentes de entupimento na mesma tubulação. Um painel WordPress bem arquiteto resolve isso com Custom Post Types, campos estruturados e permissões granulares.

Arquitetura do Custom Post Type

Registre o CPT relatorio_tecnico no plugin principal ou functions.php do tema, priorizando show_in_rest => true para blocos Gutenberg e integração headless. Defina capability_type => 'technical_report' e mapeie capabilities para read_technical_report, edit_technical_reports, publish_technical_reports e delete_technical_reports. Isso isola o conteúdo e permite roles dedicadas.

function registrar_relatorio_tecnico_cpt() {
    $labels = [
        'name' => 'Relatórios Técnicos',
        'singular_name' => 'Relatório Técnico',
        'add_new_item' => 'Novo Laudo',
        'edit_item' => 'Editar Laudo',
    ];
    $args = [
        'labels' => $labels,
        'public' => false,
        'show_ui' => true,
        'show_in_menu' => true,
        'menu_icon' => 'dashicons-clipboard',
        'show_in_rest' => true,
        'rest_base' => 'relatorios-tecnicos',
        'capability_type' => 'technical_report',
        'capabilities' => [
            'read_post' => 'read_technical_report',
            'edit_post' => 'edit_technical_report',
            'edit_posts' => 'edit_technical_reports',
            'edit_others_posts' => 'edit_others_technical_reports',
            'publish_posts' => 'publish_technical_reports',
            'delete_post' => 'delete_technical_report',
        ],
        'map_meta_cap' => true,
        'supports' => ['title', 'editor', 'author', 'thumbnail', 'custom-fields'],
        'has_archive' => false,
        'rewrite' => ['slug' => 'relatorio-tecnico'],
    ];
    register_post_type('relatorio_tecnico', $args);
}
add_action('init', 'registrar_relatorio_tecnico_cpt');

Com map_meta_cap => true, o WordPress verifica permissões por post, essencial quando técnicos só devem editar próprios laudos. O rest_base padroniza endpoints para o app móvel da equipe.

Campos Estruturados com ACF Pro

Crie grupos de campos organizados em abas: Dados da Ocorrência (data, endereço geocodificado, tipo de imóvel, solicitante), Diagnóstico (tipo de obstrução: raízes, gordura, objeto, colapso; método: hidrojateamento, roto-rooter, inspeção vídeo; metragem atendida), Equipamentos e Insumos (repeater: equipamento, quantidade, tempo de uso), Mídias (gallery para fotos antes/depois, file para laudo PDF assinado), Assinaturas (campo signature customizado ou repeater com nome, cargo, assinatura digital).

Use acf/validate_value para exigir fotos quando status for “Concluído” e acf/save_post para disparar geração de PDF. Campos de endereço ganham geocodificação via API do Google Maps ou OpenStreetMap no front-end do admin, populando lat/lng em meta fields ocultos para consultas espaciais posteriores.

Roles e Permissões Granulares

Crie duas roles no plugin de ativação:

function criar_roles_desentupimento() {
    add_role('tecnico_campo', 'Técnico de Campo', [
        'read' => true,
        'read_technical_report' => true,
        'edit_technical_report' => true,
        'edit_technical_reports' => true,
        'publish_technical_reports' => true,
        'delete_technical_report' => true,
        'upload_files' => true,
    ]);
    add_role('gestor_operacoes', 'Gestor de Operações', [
        'read' => true,
        'read_technical_report' => true,
        'edit_technical_reports' => true,
        'edit_others_technical_reports' => true,
        'publish_technical_reports' => true,
        'delete_technical_reports' => true,
        'delete_others_technical_reports' => true,
        'list_users' => true,
        'edit_users' => true,
    ]);
}
register_activation_hook(__FILE__, 'criar_roles_desentupimento');

O técnico cria e edita próprios relatórios; o gestor visualiza todos, altera status, reatribui responsáveis e acessa o dashboard. Use o plugin Members ou User Role Editor para ajustes visuais sem código.

Dashboard Administrativo com Métricas

Adicione página de menu via add_menu_page('Dashboard Operacional', 'Operações', 'edit_technical_reports', 'dashboard-operacional', 'render_dashboard_operacional', 'dashicons-chart-area', 25). No callback, enfileire Chart.js e um script próprio que consome endpoint REST customizado:

register_rest_route('desentupimento/v1', '/metricas/', [
    'methods' => 'GET',
    'callback' => 'get_metricas_dashboard',
    'permission_callback' => fn() => current_user_can('edit_technical_reports'),
]);

function get_metricas_dashboard($request) {
    global $wpdb;
    $dias = (int) $request->get_param('dias') ?: 30;
    $inicio = date('Y-m-d', strtotime("-{$dias} days"));
    
    $por_tipo = $wpdb->get_results($wpdb->prepare(
        "SELECT meta_value as tipo, COUNT(*) as total 
         FROM {$wpdb->postmeta} pm 
         JOIN {$wpdb->posts} p ON p.ID = pm.post_id 
         WHERE pm.meta_key = 'tipo_obstrucao' 
         AND p.post_type = 'relatorio_tecnico' 
         AND p.post_status = 'publish' 
         AND p.post_date >= %s 
         GROUP BY meta_value",
        $inicio
    ));
    
    $por_tecnico = $wpdb->get_results($wpdb->prepare(
        "SELECT u.display_name as tecnico, COUNT(*) as total 
         FROM {$wpdb->posts} p 
         JOIN {$wpdb->users} u ON u.ID = p.post_author 
         WHERE p.post_type = 'relatorio_tecnico' 
         AND p.post_status = 'publish' 
         AND p.post_date >= %s 
         GROUP BY p.post_author",
        $inicio
    ));
    
    $por_status = $wpdb->get_results($wpdb->prepare(
        "SELECT post_status as status, COUNT(*) as total 
         FROM {$wpdb->posts} 
         WHERE post_type = 'relatorio_tecnico' 
         AND post_date >= %s 
         GROUP BY post_status",
        $inicio
    ));
    
    return compact('por_tipo', 'por_tecnico', 'por_status');
}

No JavaScript, monte gráficos de barras (tipo de obstrução), pizza (carga por técnico) e linha (tendência semanal). Adicione seletor de período (7/30/90 dias) que refaz a requisição. Para mapas de calor, retorne lat/lng dos relatórios e use Leaflet.js com plugin Heatmap.

Geração Automática de PDF e Entrega

No hook acf/save_post (priority 20, após ACF salvar metas), verifique se status mudou para “Concluído” e se PDF ainda não existe:

action('acf/save_post', 'gerar_pdf_relatorio', 20);
function gerar_pdf_relatorio($post_id) {
    if (get_post_type($post_id) !== 'relatorio_tecnico') return;
    if (get_post_meta($post_id, '_pdf_gerado', true)) return;
    if (get_field('status', $post_id) !== 'concluido') return;
    
    $dados = [
        'titulo' => get_the_title($post_id),
        'data' => get_field('data_atendimento', $post_id),
        'endereco' => get_field('endereco_completo', $post_id),
        'tipo_obstrucao' => get_field('tipo_obstrucao', $post_id),
        'metodo' => get_field('metodo_utilizado', $post_id),
        'metragem' => get_field('metragem_atendida', $post_id),
        'observacoes' => get_field('observacoes_tecnicas', $post_id),
        'fotos' => get_field('fotos_antes_depois', $post_id),
        'tecnico' => get_the_author_meta('display_name', get_post_field('post_author', $post_id)),
        'assinaturas' => get_field('assinaturas', $post_id),
    ];
    
    $html = render_template_pdf($dados); // Blade, Twig ou include PHP
    
    require_once ABSPATH . 'wp-admin/includes/file.php';
    $upload = wp_upload_bits("relatorio-{$post_id}.pdf", null, $html); // mPDF output
    
    if (!$upload['error']) {
        $attachment_id = wp_insert_attachment([
            'post_title' => "Laudo {$post_id}",
            'post_mime_type' => 'application/pdf',
            'guid' => $upload['url'],
        ], $upload['file'], $post_id);
        wp_update_attachment_metadata($attachment_id, wp_generate_attachment_metadata($attachment_id, $upload['file']));
        update_post_meta($post_id, '_pdf_anexo_id', $attachment_id);
        update_post_meta($post_id, '_pdf_gerado', true);
        
        // Opcional: enviar por e-mail ao cliente
        $cliente_email = get_field('email_cliente', $post_id);
        if ($cliente_email) {
            wp_mail($cliente_email, "Laudo de Desentupimento - {$dados['titulo']}",
                "Seu laudo está em anexo.", '', [$upload['file']]);
        }
    }
}

Use mPDF ou DomPDF instalados via Composer no plugin (composer require mpdf/mpdf). O template HTML deve ser responsivo para A4, com cabeçalho da empresa, tabela de dados e rodapé com QR code vinculando ao relatório no painel (validação de autenticidade).

Colunas Administrativas e Filtros Avançados

Adicione colunas editáveis na lista de posts para agilizar gestão:

add_filter('manage_relatorio_tecnico_posts_columns', 'colunas_relatorio_tecnico');
function colunas_relatorio_tecnico($columns) {
    unset($columns['date']);
    return array_merge($columns, [
        'status_atendimento' => 'Status',
        'tecnico_responsavel' => 'Técnico',
        'data_atendimento' => 'Data Atendimento',
        'tipo_obstrucao' => 'Obstrução',
        'pdf_status' => 'Laudo PDF',
    ]);
}

add_action('manage_relatorio_tecnico_posts_custom_column', 'conteudo_colunas_relatorio', 10, 2);
function conteudo_colunas_relatorio($column, $post_id) {
    switch ($column) {
        case 'status_atendimento':
            $status = get_field('status_atendimento', $post_id);
            echo "<span class='status-badge status-{$status}'>{$status}</span>";
            break;
        case 'tecnico_responsavel':
            $author = get_userdata(get_post_field('post_author', $post_id));
            echo $author ? $author->display_name : '—';
            break;
        case 'data_atendimento':
            echo get_field('data_atendimento', $post_id) ?: get_the_date('d/m/Y', $post_id);
            break;
        case 'tipo_obstrucao':
            echo get_field('tipo_obstrucao', $post_id) ?: '—';
            break;
        case 'pdf_status':
            $pdf_id = get_post_meta($post_id, '_pdf_anexo_id', true);
            if ($pdf_id) {
                echo "<a href='" . wp_get_attachment_url($pdf_id) . "' target='_blank' class='button button-small'>Ver PDF</a>";
            } else {
                echo "<span class='dashicons dashicons-no-alt' style='color:#d63638'></span>";
            }
            break;
    }
}

add_filter('manage_edit-relatorio_tecnico_sortable_columns', 'colunas_ordenaveis_relatorio');
function colunas_ordenaveis_relatorio($columns) {
    $columns['data_atendimento'] = 'data_atendimento';
    $columns['tecnico_responsavel'] = 'author';
    return $columns;
}

Registre pre_get_posts para ordenar por meta data_atendimento e adicione restrict_manage_posts com dropdowns de filtro por status, técnico e tipo de obstrução.

Performance em Escala

Com milhares de relatórios, queries de meta não indexadas travam o admin. Crie tabela de índice customizada:

function criar_tabela_indices_relatorios() {
    global $wpdb;
    $table = $wpdb->prefix . 'relatorio_indices';
    $charset = $wpdb->get_charset_collate();
    $sql = "CREATE TABLE {$table} (
        id bigint(20) NOT NULL AUTO_INCREMENT,
        post_id bigint(20) NOT NULL,
        data_atendimento date DEFAULT NULL,
        tecnico_id bigint(20) DEFAULT NULL,
        tipo_obstrucao varchar(100) DEFAULT NULL,
        status varchar(50) DEFAULT NULL,
        bairro varchar(100) DEFAULT NULL,
        PRIMARY KEY (id),
        KEY idx_data (data_atendimento),
        KEY idx_tecnico (tecnico_id),
        KEY idx_tipo (tipo_obstrucao),
        KEY idx_status (status),
        KEY idx_bairro (bairro)
    ) {$charset};";
    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    dbDelta($sql);
}
register_activation_hook(__FILE__, 'criar_tabela_indices_relatorios');

Popule via save_post e transition_post_status. No posts_clauses, redirecione meta_query para JOIN nessa tabela quando filtros usarem esses campos. Ative object cache (Redis) e defina wp_cache_set('relatorio_dashboard_30d', $dados, 'dashboard', HOUR_IN_SECONDS) no endpoint de métricas.

Integração com Ferramentas de Campo

Técnicos em campo precisam de formulário offline-first. Duas abordagens: (1) PWA com Service Worker cacheando o wp-admin/post-new.php?post_type=relatorio_tecnico e sincronizando via Background Sync API; (2) App React Native/Flutter consumindo REST API com autenticação JWT (plugin JWT Authentication for WP REST API). A segunda é mais robusta para fotos grandes e geolocalização contínua.

No WordPress, crie endpoint POST /wp-json/desentupimento/v1/relatorios que aceita multipart/form-data, valida nonces, cria o CPT em rascunho e retorna ID para o app anexar mídias em chamadas subsequentes (POST /wp-json/wp/v2/media). Use wp_handle_upload no callback REST.

Para equipes que já usam área do cliente para manutenção de coluna de esgoto em SP, o mesmo CPT pode alimentar o histórico visível ao condomínio, filtrando por meta_query de condominio_id salvo no relatório.

Exportação e Auditoria

Gestores precisam exportar dados para planilhas contábeis ou auditoria. Implemente botão “Exportar CSV” na tela de lista via admin_footer-edit.php condicional ao post_type=relatorio_tecnico. O AJAX chama endpoint que faz WP_Query com mesmos filtros da tela, itera resultados e gera CSV com fputcsv em stream (evita estouro de memória). Colunas: ID, Data, Técnico, Endereço, Tipo Obstrução, Método, Metragem, Status, Link PDF.

Para trilha de auditoria, use hook transition_post_status gravando em tabela relatorio_auditoria (post_id, from_status, to_status, user_id, timestamp, ip). Exiba aba “Histórico” no editor do relatório via edit_form_after_title.

Um painel assim elimina planilhas paralelas, garante rastreabilidade legal dos laudos e dá à gestão visão real da operação — tudo dentro do WordPress que a equipe já conhece. Comece pelo CPT e ACF, evolua para dashboard e PDF, depois integre app móvel conforme a maturidade da equipe. Para casos que exigem geolocalização de atendimentos, veja como integração de geolocalização para caça vazamentos 24 horas em SP resolve o mapeamento em tempo real.

O próximo passo é validar o fluxo com um técnico piloto por duas semanas, ajustar campos e permissões, e só então escalar para toda a frota. desentupimento de esgoto no município de São Paulo

Checklist prático

0 de 9 concluídos

Perguntas frequentes

Qual a melhor forma de estruturar dados técnicos de desentupimento no WordPress?

Use Custom Post Types para isolar os relatórios do conteúdo padrão, combinados com Advanced Custom Fields para campos estruturados como tipo de obstrução, equipamento utilizado, metragem e resultado da inspeção por vídeo. Isso garante queries performáticas e interface limpa para operadores.

Como restringir o acesso aos relatórios apenas para técnicos e gestores?

Crie roles personalizadas via `add_role()` ou plugins como Members, atribuindo capabilities específicas como `read_technical_report`, `edit_technical_report` e `publish_technical_report`. No registro do CPT, defina `capability_type` e `capabilities` mapeando para essas permissões customizadas.

É possível gerar PDFs automáticos dos relatórios para entrega ao cliente?

Sim, integre bibliotecas como DomPDF ou mPDF via hook `save_post` ou action agendada. Capture os campos ACF, monte um template HTML estruturado e converta para PDF, salvando o arquivo no Media Library associado ao CPT ou enviando por e-mail via `wp_mail()`.

Como exibir um dashboard com métricas de atendimentos por região e tipo de ocorrência?

Crie uma página de admin customizada com `add_menu_page()` e use `WP_Query` com `meta_query` para agregar dados. Bibliotecas como Chart.js ou WordPress Charts podem renderizar gráficos diretamente no painel, consumindo endpoints REST ou AJAX nonces para dados em tempo real.

Como integrar fotos de inspeção e laudos técnicos aos relatórios?

Configure campos do tipo Gallery ou Repeater no ACF Pro para upload múltiplo de imagens e arquivos PDF. No front-end ou admin, use `wp_get_attachment_image()` e `wp_get_attachment_url()` para exibição organizada, com lightbox para fotos e links diretos para laudos.

Qual estratégia de performance para milhares de relatórios no painel?

Implemente índices customizados na tabela `postmeta` via `dbDelta()` para campos de filtro frequentes (data, técnico, status). Use `posts_clauses` para otimizar queries, ative object caching (Redis/Memcached) e pagine listas com `pre_get_posts` limitando a 20 itens por página.