{"id":7334,"date":"2026-04-09T18:46:41","date_gmt":"2026-04-09T21:46:41","guid":{"rendered":"https:\/\/saudecajati.com.br\/?page_id=7334"},"modified":"2026-04-10T13:13:08","modified_gmt":"2026-04-10T16:13:08","slug":"leitor-de-afd","status":"publish","type":"page","link":"https:\/\/saudecajati.com.br\/index.php\/leitor-de-afd\/","title":{"rendered":"Leitor de AFD"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"7334\" class=\"elementor elementor-7334\">\n\t\t\t\t<div class=\"elementor-element elementor-element-c5a85fc e-flex e-con-boxed e-con e-parent\" data-id=\"c5a85fc\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-fe9b81a elementor-align-center elementor-icon-list--layout-traditional elementor-list-item-link-full_width elementor-invisible elementor-widget elementor-widget-icon-list\" data-id=\"fe9b81a\" data-element_type=\"widget\" data-settings=\"{&quot;_animation&quot;:&quot;slideInDown&quot;}\" data-widget_type=\"icon-list.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<ul class=\"elementor-icon-list-items\">\n\t\t\t\t\t\t\t<li class=\"elementor-icon-list-item\">\n\t\t\t\t\t\t\t\t\t\t<span class=\"elementor-icon-list-text\">Leitor de AFD - Secretaria<\/span>\n\t\t\t\t\t\t\t\t\t<\/li>\n\t\t\t\t\t\t<\/ul>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-7bffa37 e-flex e-con-boxed e-con e-parent\" data-id=\"7bffa37\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f3d0bc4 elementor-widget elementor-widget-html\" data-id=\"f3d0bc4\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<html lang=\"pt-BR\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;600;700;800&display=swap\" rel=\"stylesheet\">\r\n    <style>\r\n        \/* CONFIGURA\u00c7\u00c3O GLOBAL - PADR\u00c3O CAJATI *\/\r\n        #leitor-afd-app {\r\n            font-family: 'Inter', sans-serif !important;\r\n            background-color: #f4f6f8 !important;\r\n            padding: 20px !important;\r\n            color: #2c3e50 !important;\r\n            max-width: 900px !important;\r\n            margin: 20px auto !important;\r\n            border-radius: 12px !important;\r\n            box-shadow: 0 4px 10px rgba(0,0,0,0.05) !important;\r\n            border: 1px solid #e2e8f0 !important;\r\n        }\r\n\r\n        .card-config {\r\n            background: #ffffff !important;\r\n            padding: 20px !important;\r\n            border-radius: 12px !important;\r\n            border: 1px solid #edf2f7 !important;\r\n            margin-bottom: 15px !important;\r\n        }\r\n\r\n        .label-instrucao {\r\n            display: block !important;\r\n            font-weight: 700 !important;\r\n            margin-bottom: 8px !important;\r\n            color: #00A09A !important;\r\n            font-size: 13px !important;\r\n            text-transform: uppercase !important;\r\n        }\r\n\r\n        .afd-input {\r\n            width: 100% !important;\r\n            padding: 12px !important;\r\n            border: 2px solid #edf2f7 !important;\r\n            border-radius: 8px !important;\r\n            font-size: 15px !important;\r\n            box-sizing: border-box !important;\r\n            font-family: 'Inter', sans-serif !important;\r\n            outline: none !important;\r\n        }\r\n\r\n        .afd-input:focus {\r\n            border-color: #00A09A !important;\r\n        }\r\n\r\n        .afd-input:disabled {\r\n            background-color: #f4f6f8 !important;\r\n            cursor: not-allowed;\r\n        }\r\n\r\n        \/* BOT\u00c3O *\/\r\n        .btn-cajati {\r\n            width: 100% !important;\r\n            background-color: #00A09A !important;\r\n            color: #ffffff !important;\r\n            border: none !important;\r\n            padding: 15px !important;\r\n            border-radius: 8px !important;\r\n            font-weight: 700 !important;\r\n            font-size: 16px !important;\r\n            cursor: pointer !important;\r\n            display: flex !important;\r\n            align-items: center !important;\r\n            justify-content: center !important;\r\n            transition: background-color 0.2s !important;\r\n            margin-top: 15px !important;\r\n        }\r\n\r\n        .btn-cajati:hover:not(:disabled) { background-color: #00827d !important; }\r\n        .btn-cajati:disabled { background-color: #cbd5e1 !important; cursor: not-allowed; }\r\n        \r\n        .afd-status {\r\n            text-align: center !important;\r\n            margin-top: 15px !important;\r\n            font-size: 13px !important;\r\n            font-weight: 600 !important;\r\n        }\r\n    <\/style>\r\n<\/head>\r\n<body>\r\n\r\n<div id=\"leitor-afd-app\">\r\n    \r\n    <div class=\"card-config\">\r\n        <label class=\"label-instrucao\">1. Arquivo AFD (.txt)<\/label>\r\n        <input type=\"file\" id=\"arquivo-ponto\" class=\"afd-input\" accept=\".txt, .afd\" onchange=\"lerArquivoAFD(event)\">\r\n    <\/div>\r\n\r\n    <div style=\"display: grid; grid-template-columns: 1.5fr 1fr; gap: 15px;\">\r\n        <div class=\"card-config\">\r\n            <label class=\"label-instrucao\">2. Selecionar Servidor<\/label>\r\n            <select id=\"seletor-servidor\" class=\"afd-input\" disabled onchange=\"validarSelecao()\">\r\n                <option value=\"\">Aguardando arquivo...<\/option>\r\n            <\/select>\r\n        <\/div>\r\n\r\n        <div class=\"card-config\">\r\n            <label class=\"label-instrucao\">3. M\u00eas de Refer\u00eancia<\/label>\r\n            <input type=\"month\" id=\"mes-ref\" class=\"afd-input\">\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <button onclick=\"imprimirFolha()\" class=\"btn-cajati\" id=\"btn-imprimir\" disabled>\r\n        Gerar e Imprimir PDF\r\n    <\/button>\r\n    \r\n    <div id=\"afd-status\" class=\"afd-status\" style=\"color: #64748b;\">Insira o arquivo gerado pelo rel\u00f3gio para come\u00e7ar.<\/div>\r\n<\/div>\r\n\r\n<script>\r\n    let bancoDadosAFD = {}; \r\n\r\n    function lerArquivoAFD(event) {\r\n        const file = event.target.files[0];\r\n        if (!file) return;\r\n\r\n        const reader = new FileReader();\r\n        reader.onload = function(e) {\r\n            const linhas = e.target.result.split(\/\\r?\\n\/);\r\n            bancoDadosAFD = {};\r\n            const mapNomes = {}; \r\n\r\n            \/\/ ==========================================\r\n            \/\/ PASSO 1: Mapear Nomes e Limpar (Registro Tipo 5)\r\n            \/\/ ==========================================\r\n            linhas.forEach(linha => {\r\n                const linhaTrim = linha.trim();\r\n                \r\n                \/\/ Verifica se a linha \u00e9 do Tipo 5 (Inclus\u00e3o de funcion\u00e1rio)\r\n                if (linhaTrim.length >= 22 && linhaTrim.substring(9, 10) === '5') {\r\n                    \r\n                    const restante = linhaTrim.substring(10);\r\n                    \r\n                    \/\/ Regex ignora Data\/Hora inicial se houver, pega a letra da opera\u00e7\u00e3o, o PIS e o resto\r\n                    const match = restante.match(\/^(?:\\d{12})?[A-Za-z](\\d{11,12})(.*)\/);\r\n                    \r\n                    if (match) {\r\n                        const pis = match[1];\r\n                        const rawName = match[2];\r\n                        \r\n                        \/\/ M\u00c1GICA DA LIMPEZA: O rel\u00f3gio separa o nome do crach\u00e1 com v\u00e1rios espa\u00e7os.\r\n                        \/\/ Cortamos o texto logo no primeiro bloco de 2 ou mais espa\u00e7os.\r\n                        let nomeLimpo = rawName.split(\/\\s{2,}\/)[0];\r\n                        \r\n                        \/\/ Limpeza de seguran\u00e7a extra (caso n\u00e3o tenha os espa\u00e7os, remove hashes no final)\r\n                        nomeLimpo = nomeLimpo.replace(\/[0-9a-fA-F]{8,}$\/i, '').trim();\r\n\r\n                        if (pis && nomeLimpo) {\r\n                            mapNomes[pis] = nomeLimpo;\r\n                            \/\/ Cadastra no banco para exibir na lista mesmo sem batida de ponto\r\n                            bancoDadosAFD[pis] = { nome: nomeLimpo, marcacoes: {} };\r\n                        }\r\n                    }\r\n                }\r\n            });\r\n\r\n            \/\/ ==========================================\r\n            \/\/ PASSO 2: Mapear Batidas de Ponto (Registro Tipo 3)\r\n            \/\/ ==========================================\r\n            linhas.forEach(linha => {\r\n                const linhaTrim = linha.trim();\r\n                \r\n                \/\/ Verifica se \u00e9 o Registro Tipo 3\r\n                if (linhaTrim.length >= 33 && linhaTrim.substring(9, 10) === '3') {\r\n                    \/\/ Extrai os campos: Dia(2), Mes(2), Ano(4), Hora(2), Min(2), PIS(11-12)\r\n                    const match3 = linhaTrim.match(\/^\\d{9}3(\\d{2})(\\d{2})(\\d{4})(\\d{2})(\\d{2})(\\d{11,12})\/);\r\n                    \r\n                    if (match3) {\r\n                        const dia = match3[1];\r\n                        const mes = match3[2];\r\n                        const ano = match3[3];\r\n                        const horaFormatada = match3[4] + ':' + match3[5];\r\n                        const pis = match3[6];\r\n\r\n                        \/\/ Se o ponto foi batido por um PIS que n\u00e3o tem nome registrado\r\n                        if (!bancoDadosAFD[pis]) {\r\n                            bancoDadosAFD[pis] = {\r\n                                nome: mapNomes[pis] || `Servidor Sem Nome (ID: ${pis})`,\r\n                                marcacoes: {}\r\n                            };\r\n                        }\r\n\r\n                        \/\/ Registra a hora garantindo que n\u00e3o haver\u00e1 duplicatas (Set)\r\n                        const chaveMes = `${ano}-${mes}`;\r\n                        if (!bancoDadosAFD[pis].marcacoes[chaveMes]) bancoDadosAFD[pis].marcacoes[chaveMes] = {};\r\n                        if (!bancoDadosAFD[pis].marcacoes[chaveMes][dia]) bancoDadosAFD[pis].marcacoes[chaveMes][dia] = new Set();\r\n                        \r\n                        bancoDadosAFD[pis].marcacoes[chaveMes][dia].add(horaFormatada);\r\n                    }\r\n                }\r\n            });\r\n\r\n            \/\/ ==========================================\r\n            \/\/ PASSO 3: Preencher o Menu Suspenso\r\n            \/\/ ==========================================\r\n            const select = document.getElementById('seletor-servidor');\r\n            select.innerHTML = '<option value=\"\">-- Selecione o Servidor --<\/option>';\r\n            \r\n            const listaPIS = Object.keys(bancoDadosAFD);\r\n            \r\n            \/\/ Ordena em ordem alfab\u00e9tica perfeita\r\n            listaPIS.sort((a, b) => bancoDadosAFD[a].nome.localeCompare(bancoDadosAFD[b].nome))\r\n                    .forEach(pis => {\r\n                if(bancoDadosAFD[pis].nome.length > 0) {\r\n                    const opt = document.createElement('option');\r\n                    opt.value = pis;\r\n                    opt.textContent = bancoDadosAFD[pis].nome;\r\n                    select.appendChild(opt);\r\n                }\r\n            });\r\n            \r\n            select.disabled = false;\r\n            document.getElementById('mes-ref').value = new Date().toISOString().slice(0, 7);\r\n            \r\n            const total = listaPIS.length;\r\n            if (total > 0) {\r\n                document.getElementById('afd-status').textContent = `Perfeito! Lista gerada com ${total} servidores.`;\r\n                document.getElementById('afd-status').style.color = \"#00A09A\";\r\n            } else {\r\n                document.getElementById('afd-status').textContent = \"Nenhum dado encontrado no arquivo.\";\r\n                document.getElementById('afd-status').style.color = \"red\";\r\n            }\r\n        };\r\n        \r\n        \/\/ Tratamento ISO-8859-1 (L\u00ea '\u00c7' e acentos do Control iD corretamente)\r\n        reader.readAsText(file, 'ISO-8859-1');\r\n    }\r\n\r\n    function validarSelecao() {\r\n        const select = document.getElementById('seletor-servidor');\r\n        document.getElementById('btn-imprimir').disabled = (select.value === \"\");\r\n    }\r\n\r\n    function imprimirFolha() {\r\n        const pis = document.getElementById('seletor-servidor').value;\r\n        const mesRef = document.getElementById('mes-ref').value;\r\n\r\n        if (!pis || !mesRef) {\r\n            alert(\"Selecione um servidor e o m\u00eas.\");\r\n            return;\r\n        }\r\n\r\n        const [ano, mes] = mesRef.split('-');\r\n        const servidor = bancoDadosAFD[pis];\r\n        const marcacoesDoMes = servidor.marcacoes[mesRef] || {};\r\n        \r\n        const diasNoMes = new Date(ano, mes, 0).getDate();\r\n        let linhasTabelaHTML = '';\r\n\r\n        \/\/ Dicion\u00e1rio de Feriados Nacionais (Formato M\u00eas-Dia)\r\n        const feriadosFixos = {\r\n            \"01-01\": \"FERIADO: ANO NOVO\",\r\n            \"04-21\": \"FERIADO: TIRADENTES\",\r\n            \"05-01\": \"FERIADO: DIA DO TRABALHO\",\r\n            \"09-07\": \"FERIADO: INDEPEND\u00caNCIA\",\r\n            \"10-12\": \"FERIADO: PADROEIRA DO BRASIL\",\r\n            \"11-02\": \"FERIADO: FINADOS\",\r\n            \"11-15\": \"FERIADO: PROC. DA REP\u00daBLICA\",\r\n            \"11-20\": \"FERIADO: CONSCI\u00caNCIA NEGRA\",\r\n            \"12-25\": \"FERIADO: NATAL\"\r\n        };\r\n\r\n        for (let i = 1; i <= diasNoMes; i++) {\r\n            const diaStr = String(i).padStart(2, '0');\r\n            const horas = Array.from(marcacoesDoMes[diaStr] || []).sort();\r\n            \r\n            \/\/ L\u00d3GICA DE FINAIS DE SEMANA E FERIADOS\r\n            const dataAtual = new Date(ano, parseInt(mes) - 1, i);\r\n            const diaSemana = dataAtual.getDay(); \/\/ 0 = Domingo, 6 = S\u00e1bado\r\n            const mesDiaStr = `${mes}-${diaStr}`;\r\n            const nomeFeriado = feriadosFixos[mesDiaStr];\r\n\r\n            let estiloLinha = \"\";\r\n            let textoObservacao = \"\";\r\n\r\n            if (nomeFeriado) {\r\n                estiloLinha = \"background-color: #f1f5f9; -webkit-print-color-adjust: exact; print-color-adjust: exact;\";\r\n                textoObservacao = `<strong>${nomeFeriado}<\/strong>`;\r\n            } else if (diaSemana === 0) {\r\n                estiloLinha = \"background-color: #f1f5f9; -webkit-print-color-adjust: exact; print-color-adjust: exact;\";\r\n                textoObservacao = \"<strong>DOMINGO<\/strong>\";\r\n            } else if (diaSemana === 6) {\r\n                estiloLinha = \"background-color: #f1f5f9; -webkit-print-color-adjust: exact; print-color-adjust: exact;\";\r\n                textoObservacao = \"<strong>S\u00c1BADO<\/strong>\";\r\n            }\r\n\r\n            linhasTabelaHTML += `\r\n                <tr style=\"${estiloLinha}\">\r\n                    <td><strong>${diaStr}<\/strong><\/td>\r\n                    <td>${horas[0] || ''}<\/td>\r\n                    <td>${horas[1] || ''}<\/td>\r\n                    <td>${horas[2] || ''}<\/td>\r\n                    <td>${horas[3] || ''}<\/td>\r\n                    <td style=\"color: #64748b;\">${textoObservacao}<\/td>\r\n                <\/tr>\r\n            `;\r\n        }\r\n\r\n        const htmlImpressao = `\r\n            <!DOCTYPE html>\r\n            <html>\r\n            <head>\r\n                <title>Folha de Ponto - ${servidor.nome}<\/title>\r\n                <style>\r\n                    body { font-family: Arial, sans-serif; margin: 0; padding: 20px; color: #000; background: #fff; }\r\n                    .header-print { display: flex; align-items: center; border-bottom: 2px solid #00A09A; padding-bottom: 10px; margin-bottom: 20px; }\r\n                    .header-logo { height: 70px; }\r\n                    .header-center { margin-left: 20px; font-size: 12px; font-weight: 600; line-height: 1.3; }\r\n                    .titulo-documento-print { background-color: #00A09A; color: white; padding: 10px; border-radius: 8px; font-size: 18px; text-align: center; font-weight: 800; margin-bottom: 15px; text-transform: uppercase; -webkit-print-color-adjust: exact; print-color-adjust: exact; }\r\n                    .print-info { display: flex; justify-content: space-between; margin-bottom: 15px; font-size: 13px; font-weight: 700; }\r\n                    .print-table { width: 100%; border-collapse: collapse; font-size: 11px; margin-bottom: 30px; }\r\n                    .print-table th { background-color: #00A09A; color: white; padding: 8px; border: 1px solid #008580; -webkit-print-color-adjust: exact; print-color-adjust: exact; }\r\n                    .print-table td { border: 1px solid #00A09A; padding: 6px; text-align: center; height: 20px; }\r\n                    .print-table tbody tr { border-bottom: 1px solid #00A09A; }\r\n                    .footer-print-text { text-align: center; font-size: 11px; margin-bottom: 40px; }\r\n                    .signature-box { border-top: 1px solid #000; width: 60%; margin: 0 auto; text-align: center; padding-top: 5px; font-weight: bold; }\r\n                    .site-link { text-align: center; color: #00A09A; font-weight: bold; margin-top: 20px; font-size: 12px; }\r\n                    @page { size: A4 portrait; margin: 1cm; }\r\n                <\/style>\r\n            <\/head>\r\n            <body>\r\n                <div class=\"header-print\">\r\n                    <img decoding=\"async\" src=\"https:\/\/saudecajati.com.br\/wp-content\/uploads\/2024\/10\/LOGO-SAUDEnovo-300x109.png\" class=\"header-logo\">\r\n                    <div class=\"header-center\">\r\n                        SECRETARIA MUNICIPAL DE SA\u00daDE<br>\r\n                        Rua Teodoro Ferreira Machado, s\/n \u2013 Centro \u2013 CEP 11.950-000<br>\r\n                        Cajati, SP\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div class=\"titulo-documento-print\">\r\n                    CONTROLE DE FREQU\u00caNCIA INDIVIDUAL\r\n                <\/div>\r\n                \r\n                <div class=\"print-info\">\r\n                    <span>SERVIDOR(A): ${servidor.nome.toUpperCase()}<\/span>\r\n                    <span>M\u00caS: ${mes}\/${ano}<\/span>\r\n                <\/div>\r\n\r\n                <table class=\"print-table\">\r\n                    <thead>\r\n                        <tr>\r\n                            <th width=\"8%\">DIA<\/th>\r\n                            <th width=\"12%\">ENTRADA 1<\/th>\r\n                            <th width=\"12%\">SA\u00cdDA 1<\/th>\r\n                            <th width=\"12%\">ENTRADA 2<\/th>\r\n                            <th width=\"12%\">SA\u00cdDA 2<\/th>\r\n                            <th>ASSINATURA \/ OBSERVA\u00c7\u00c3O<\/th>\r\n                        <\/tr>\r\n                    <\/thead>\r\n                    <tbody>\r\n                        ${linhasTabelaHTML}\r\n                    <\/tbody>\r\n                <\/table>\r\n                \r\n                <div class=\"footer-print-text\">\r\n                    Declaro que as informa\u00e7\u00f5es acima s\u00e3o verdadeiras e refletem minha jornada de trabalho.\r\n                <\/div>\r\n                <div class=\"signature-box\">\r\n                    Assinatura do Servidor\r\n                <\/div>\r\n                <div class=\"site-link\">\r\n                    www.saudecajati.com.br\r\n                <\/div>\r\n                \r\n                <script>\r\n                    window.onload = function() {\r\n                        setTimeout(function() {\r\n                            window.print();\r\n                            window.close();\r\n                        }, 500);\r\n                    };\r\n                <\\\/script>\r\n            <\/body>\r\n            <\/html>\r\n        `;\r\n\r\n        const printWindow = window.open('', '_blank');\r\n        if (printWindow) {\r\n            printWindow.document.open();\r\n            printWindow.document.write(htmlImpressao);\r\n            printWindow.document.close();\r\n        } else {\r\n            alert(\"Por favor, permita pop-ups no seu navegador para gerar o PDF.\");\r\n        }\r\n    }\r\n<\/script>\r\n\r\n<\/body>\r\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-2a663fa e-con-full e-flex e-con e-child\" data-id=\"2a663fa\" data-element_type=\"container\">\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Leitor de AFD &#8211; Secretaria 1. Arquivo AFD (.txt) 2. Selecionar Servidor Aguardando arquivo&#8230; 3. M\u00eas de Refer\u00eancia Gerar e Imprimir PDF Insira o arquivo gerado pelo rel\u00f3gio para come\u00e7ar.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"inline_featured_image":false,"footnotes":""},"class_list":["post-7334","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/saudecajati.com.br\/index.php\/wp-json\/wp\/v2\/pages\/7334","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/saudecajati.com.br\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/saudecajati.com.br\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/saudecajati.com.br\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/saudecajati.com.br\/index.php\/wp-json\/wp\/v2\/comments?post=7334"}],"version-history":[{"count":115,"href":"https:\/\/saudecajati.com.br\/index.php\/wp-json\/wp\/v2\/pages\/7334\/revisions"}],"predecessor-version":[{"id":7455,"href":"https:\/\/saudecajati.com.br\/index.php\/wp-json\/wp\/v2\/pages\/7334\/revisions\/7455"}],"wp:attachment":[{"href":"https:\/\/saudecajati.com.br\/index.php\/wp-json\/wp\/v2\/media?parent=7334"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}