
Exception: Malformed HTML content: <!DOCTYPE html>
<html>
<head>
<base target="_top">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sistema de Pagamento de Lenha</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background:
linear-gradient(135deg, rgba(26, 26, 26, 0.75) 0%, rgba(45, 36, 22, 0.75) 100%),
url('https://png.pngtree.com/background/20250119/original/pngtree-rustic-firewood-pile-on-wooden-background-stacked-logs-in-brown-wallpaper-picture-image_13515525.jpg') center/cover fixed no-repeat;
color: #e8e6e3;
padding-top: 140px;
min-height: 100vh;
}
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
background: linear-gradient(135deg, #2d5016 0%, #4a7c2a 100%);
color: white;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
z-index: 1000;
}
.header-content {
max-width: 1400px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
height: 50px;
}
.header h1 {
font-size: 24px;
font-weight: 600;
text-align: right;
}
.tabs {
background: rgba(61, 47, 31, 0.6);
max-width: 1400px;
margin: 20px auto;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
overflow: hidden;
backdrop-filter: blur(10px);
}
.tab-buttons {
position: fixed;
top: 90px;
left: 0;
right: 0;
display: flex;
background: rgba(109, 76, 65, 0.85);
border-bottom: 2px solid #6d4c41;
z-index: 999;
max-width: 1400px;
margin: 0 auto;
backdrop-filter: blur(5px);
}
.tab-button {
flex: 1;
padding: 15px 20px;
background: none;
border: none;
cursor: pointer;
font-size: 16px;
font-weight: 500;
color: #fff;
transition: all 0.3s;
position: relative;
}
.tab-button:hover {
background: rgba(141, 110, 99, 0.5);
}
.tab-button.active {
background: rgba(93, 64, 55, 0.7);
}
.tab-button.active::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 3px;
background: #4a7c2a;
}
.tab-content {
display: none;
padding: 30px;
animation: fadeIn 0.3s;
background: rgba(45, 36, 22, 0.5);
border-radius: 0 0 10px 10px;
backdrop-filter: blur(8px);
}
.tab-content.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
}
.section-header h2 {
color: #4a7c2a;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #4a7c2a;
font-weight: 500;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 12px;
border: 2px solid rgba(109, 76, 65, 0.6);
border-radius: 8px;
font-size: 14px;
transition: all 0.3s;
background: rgba(26, 20, 16, 0.6);
color: #e8e6e3;
backdrop-filter: blur(5px);
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: #4a7c2a;
box-shadow: 0 0 0 3px rgba(74, 124, 42, 0.2);
background: rgba(26, 20, 16, 0.8);
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.radio-group {
display: flex;
flex-direction: column;
gap: 10px;
padding: 12px;
background: rgba(26, 20, 16, 0.6);
border: 2px solid rgba(109, 76, 65, 0.6);
border-radius: 8px;
backdrop-filter: blur(5px);
}
.radio-option {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.radio-option input[type="radio"] {
width: auto;
cursor: pointer;
}
.radio-option label {
margin-bottom: 0;
cursor: pointer;
color: #e8e6e3;
font-weight: normal;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
margin-right: 10px;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-primary {
background: linear-gradient(135deg, #2d5016 0%, #4a7c2a 100%);
color: white;
}
.btn-primary:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(45, 80, 22, 0.3);
}
.btn-secondary {
background: rgba(109, 76, 65, 0.9);
color: white;
backdrop-filter: blur(5px);
}
.btn-secondary:hover {
background: rgba(93, 64, 55, 0.9);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(93, 64, 55, 0.3);
}
.btn-outline {
background: rgba(61, 47, 31, 0.6);
color: #4a7c2a;
border: 2px solid #4a7c2a;
backdrop-filter: blur(5px);
}
.btn-outline:hover {
background: #4a7c2a;
color: white;
}
.button-group {
margin-top: 30px;
display: flex;
gap: 10px;
}
.table-container {
overflow-x: auto;
margin-top: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
background: rgba(26, 20, 16, 0.7);
max-height: 600px;
overflow-y: auto;
position: relative;
backdrop-filter: blur(10px);
}
table {
width: 100%;
border-collapse: collapse;
background: transparent;
}
thead {
background: linear-gradient(135deg, #2d5016 0%, #4a7c2a 100%);
color: white;
position: sticky;
top: 0;
z-index: 10;
border-radius: 8px 8px 0 0; /* Arredondar superior esquerdo e direito */
}
th:first-child {
border-top-left-radius: 8px;
}
th:last-child {
border-top-right-radius: 8px;
}
th {
padding: 10px 8px;
text-align: center;
vertical-align: middle;
border-bottom: 1px solid rgba(109, 76, 65, 0.6);
color: #e8e6e3;
cursor: pointer;
user-select: none;
position: relative;
white-space: nowrap;
font-size: 11px;
}
th:hover {
background: rgba(255, 255, 255, 0.1);
}
th.sorted-asc::after {
content: ' ▲';
opacity: 1;
font-size: 10px;
}
th.sorted-desc::after {
content: ' ▼';
opacity: 1;
font-size: 10px;
}
th.group-border-left {
border-left: 3px solid rgba(255, 255, 255, 0.3);
}
td {
padding: 3px 5px; /* altura menor das linhas */
text-align: center;
border-bottom: 1px solid rgba(109, 76, 65, 0.3);
color: #e8e6e3;
font-size: 12px; /* texto um pouco menor */
line-height: 1; /* menor espaçamento interno */
}
td:first-child {
text-align: left;
}
/* Ocultar coluna Código */
th[data-coluna="codigo"],
td:nth-child(9) {
display: none;
}
tbody tr {
background: rgba(26, 20, 16, 0.4);
cursor: pointer;
transition: background 0.2s;
}
tbody tr:hover {
background: rgba(45, 36, 22, 0.6);
}
tbody tr.selected {
background: rgba(74, 124, 42, 0.3) !important;
border-left: 4px solid #4a7c2a;
}
.status-badge {
padding: 5px 12px;
border-radius: 20px;
font-weight: 500;
font-size: 12px;
display: inline-block;
}
.status-pago {
background: #2d5016;
color: #a5d6a7;
}
.status-aberto {
background: #5d2c2c;
color: #ffcdd2;
}
.status-emitir-nf {
background: #fff3cd;
color: #856404;
}
.status-a-pagar {
background: #d1ecf1;
color: #0c5460;
}
.btn-pagar, .btn-nota-fiscal {
background: transparent;
color: #e8e6e3;
padding: 6px;
border: none;
cursor: pointer;
font-size: 18px;
margin-right: 5px;
display: inline-flex;
align-items: center;
justify-content: center;
transition: transform 0.2s;
}
.btn-imprimir {
background: transparent;
color: #e8e6e3;
padding: 6px;
border: none;
cursor: pointer;
font-size: 18px;
margin-right: 5px;
display: inline-flex;
align-items: center;
justify-content: center;
transition: transform 0.2s;
}
.btn-pagar:hover, .btn-imprimir:hover, .btn-nota-fiscal:hover {
transform: scale(1.2);
}
.btn-pagar:disabled, .btn-imprimir:disabled, .btn-nota-fiscal:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-excluir {
background: transparent;
color: #e8e6e3;
padding: 6px;
border: none;
cursor: pointer;
font-size: 18px;
margin-right: 5px;
display: inline-flex;
align-items: center;
justify-content: center;
transition: transform 0.2s;
}
.btn-excluir:hover {
transform: scale(1.2);
}
.btn-excluir:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-imprimir-tabela {
background: linear-gradient(135deg, #2d5016 0%, #4a7c2a 100%);
color: white;
padding: 12px 30px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 8px;
margin-left: 10px;
}
.btn-imprimir-tabela:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(45, 80, 22, 0.3);
}
.resultados-por-pagina {
display: flex;
align-items: center;
gap: 8px;
margin-left: auto;
}
.resultados-por-pagina label {
color: #4a7c2a;
font-weight: 500;
font-size: 14px;
white-space: nowrap;
}
.resultados-por-pagina select {
width: 80px;
padding: 8px;
border: 2px solid rgba(109, 76, 65, 0.6);
border-radius: 8px;
font-size: 14px;
background: rgba(26, 20, 16, 0.6);
color: #e8e6e3;
cursor: pointer;
backdrop-filter: blur(5px);
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
z-index: 1000;
justify-content: center;
align-items: center;
}
.modal.active {
display: flex;
}
.modal-content {
background: rgba(45, 36, 22, 0.95);
padding: 30px;
border-radius: 10px;
max-width: 500px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 10px 40px rgba(0,0,0,0.5);
border: 2px solid rgba(109, 76, 65, 0.8);
backdrop-filter: blur(15px);
}
.modal-header {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid rgba(109, 76, 65, 0.6);
}
.modal-header h2 {
color: #4a7c2a;
}
.close-modal {
float: right;
font-size: 28px;
font-weight: bold;
color: #999;
cursor: pointer;
}
.close-modal:hover {
color: #e8e6e3;
}
.filter-section {
background: rgba(61, 47, 31, 0.6);
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
backdrop-filter: blur(8px);
}
.dashboard-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.card {
background: rgba(61, 47, 31, 0.65);
padding: 25px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
border-left: 4px solid #4a7c2a;
backdrop-filter: blur(8px);
}
.card h3 {
color: #a5a19e;
font-size: 14px;
font-weight: 500;
margin-bottom: 10px;
}
.card .value {
color: #4a7c2a;
font-size: 28px;
font-weight: bold;
}
.chart-container {
background: rgba(61, 47, 31, 0.65);
padding: 25px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
margin-bottom: 20px;
backdrop-filter: blur(8px);
}
.chart-container h3 {
color: #4a7c2a;
margin-bottom: 20px;
font-size: 18px;
}
canvas {
max-height: 300px;
}
.success-message {
background: rgba(45, 80, 22, 0.9);
color: #a5d6a7;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
display: none;
border: 1px solid #4a7c2a;
backdrop-filter: blur(5px);
}
.error-message {
background: rgba(93, 44, 44, 0.9);
color: #ffcdd2;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
display: none;
border: 1px solid #c62828;
backdrop-filter: blur(5px);
}
.linha-totais {
font-weight: bold;
background: #6d4c41 !important;
border-top: 3px solid #6d4c41 !important;
height: 40px; /* Altura aumentada */
}
.linha-totais td {
font-weight: bold;
color: white !important;
background: #6d4c41 !important;
border-top: 3px solid #6d4c41 !important;
padding: 10px 5px; /* Padding maior para aumentar altura */
vertical-align: middle;
}
.linha-totais:hover {
background: #6d4c41 !important;
}
.linha-totais:hover td {
background: #6d4c41 !important;
}
.loading {
text-align: center;
padding: 20px;
color: #a5a19e;
}
td.group-border-left {
border-left: 1px solid #6d4c41 !important;
}
@media print {
body * {
visibility: hidden;
}
.print-area, .print-area * {
visibility: visible;
}
.print-area {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
.print-area-financeiro,
.print-area-financeiro * {
visibility: visible;
}
.print-area-financeiro {
position: absolute;
left: 0;
top: 0;
width: 100%;
background: white;
padding: 20px;
}
.print-header-financeiro {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 3px solid #2d5016;
}
.print-logo-financeiro {
height: 60px;
}
.print-title-financeiro {
font-size: 24px;
font-weight: bold;
color: #2d5016;
text-align: center;
flex-grow: 1;
}
.print-info-financeiro {
margin-bottom: 20px;
font-size: 12px;
color: #333;
}
.print-info-financeiro strong {
color: #2d5016;
}
.print-table-financeiro {
width: 100%;
border-collapse: collapse;
font-size: 9px;
margin-top: 10px;
}
.print-table-financeiro th {
background: #2d5016 !important;
color: white !important;
padding: 8px 4px;
text-align: center;
border: 1px solid #2d5016;
font-weight: bold;
font-size: 9px;
}
.print-table-financeiro td {
padding: 4px 3px;
border: 1px solid #ccc;
text-align: center;
color: #000 !important;
background: white !important;
font-size: 8px;
line-height: 1.1;
}
.print-table-financeiro tbody tr:nth-child(even) {
background: #f5f5f5 !important;
}
.print-status-badge {
padding: 3px 8px;
border-radius: 12px;
font-weight: bold;
font-size: 8px;
display: inline-block;
}
.print-status-pago {
background: #c8e6c9 !important;
color: #2e7d32 !important;
border: 1px solid #2e7d32;
}
.print-status-aberto {
background: #ffcdd2 !important;
color: #c62828 !important;
border: 1px solid #c62828;
}
.print-footer-financeiro {
margin-top: 20px;
padding-top: 10px;
border-top: 2px solid #2d5016;
font-size: 10px;
color: #666;
text-align: center;
}
.print-totais {
margin-top: 15px;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
font-size: 11px;
}
.print-total-card {
background: #f5f5f5 !important;
padding: 10px;
border-radius: 5px;
border-left: 4px solid #2d5016;
}
.print-total-card strong {
display: block;
color: #2d5016;
margin-bottom: 5px;
}
.print-total-card .valor {
font-size: 14px;
font-weight: bold;
color: #333;
}
/* Ajustes para comprovantes de lançamento */
.voucher {
border: 2px solid #2d5016;
padding: 15px; /* Reduzido de 20px */
margin-bottom: 8px; /* Reduzido de 10px */
page-break-inside: avoid;
}
.voucher-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px; /* Reduzido de 20px */
border-bottom: 2px solid #2d5016;
padding-bottom: 10px; /* Reduzido de 15px */
}
.voucher-info {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6px; /* Reduzido de 10px */
margin-bottom: 10px; /* Reduzido de 15px */
font-size: 13px;
}
.voucher-field {
margin-bottom: 5px; /* Reduzido de 8px */
}
.voucher-signature {
margin-top: 30px; /* Reduzido de 50px */
display: flex;
justify-content: space-between;
}
.voucher-separator {
border-top: 2px dashed #999;
margin: 12px 0; /* Reduzido de 20px */
text-align: center;
position: relative;
}
@page {
size: landscape;
margin: 10mm;
}
}
/* ==================== ONBOARDING STYLES ==================== */
#onboardingOverlay {
display: none;
position: fixed;
inset: 0;
background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 50%, #16213e 100%);
z-index: 9999;
overflow-y: auto;
align-items: center;
justify-content: center;
}
#onboardingOverlay.active {
display: flex;
}
.onb-container {
max-width: 560px;
width: 95%;
margin: 40px auto;
padding: 40px 36px 36px;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 20px;
box-shadow: 0 30px 80px rgba(0,0,0,0.6);
backdrop-filter: blur(20px);
}
.onb-dev-logo {
display: block;
height: 52px;
margin: 0 auto 28px;
}
.onb-title {
text-align: center;
font-size: 22px;
font-weight: 700;
color: #fff;
margin-bottom: 6px;
letter-spacing: -0.3px;
}
.onb-subtitle {
text-align: center;
font-size: 13px;
color: rgba(255,255,255,0.5);
margin-bottom: 28px;
}
.onb-step {
display: none;
}
.onb-step.active {
display: block;
animation: fadeIn 0.35s ease;
}
.onb-label {
display: block;
font-size: 12px;
font-weight: 600;
color: rgba(255,255,255,0.6);
text-transform: uppercase;
letter-spacing: 0.8px;
margin-bottom: 7px;
margin-top: 18px;
}
.onb-input {
width: 100%;
padding: 12px 14px;
background: rgba(255,255,255,0.07);
border: 1px solid rgba(255,255,255,0.15);
border-radius: 10px;
font-size: 15px;
color: #fff;
transition: border-color 0.2s, box-shadow 0.2s;
box-sizing: border-box;
}
.onb-input:focus {
outline: none;
border-color: rgba(255,255,255,0.4);
box-shadow: 0 0 0 3px rgba(255,255,255,0.06);
}
.onb-color-row {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 14px;
margin-top: 6px;
}
.onb-color-block label {
display: block;
font-size: 11px;
color: rgba(255,255,255,0.5);
margin-bottom: 6px;
}
.onb-color-preview {
display: flex;
align-items: center;
gap: 10px;
background: rgba(255,255,255,0.07);
border: 1px solid rgba(255,255,255,0.15);
border-radius: 10px;
padding: 8px 10px;
}
.onb-color-preview input[type="color"] {
width: 32px;
height: 32px;
border: none;
background: none;
cursor: pointer;
border-radius: 6px;
padding: 0;
}
.onb-color-preview input[type="text"] {
background: none;
border: none;
color: #fff;
font-size: 13px;
width: 80px;
}
.onb-color-preview input[type="text"]:focus {
outline: none;
}
.onb-logo-preview {
margin-top: 14px;
text-align: center;
}
.onb-logo-preview img {
max-height: 70px;
max-width: 100%;
border-radius: 8px;
display: none;
}
.onb-logo-initials {
display: inline-flex;
align-items: center;
justify-content: center;
width: 72px;
height: 72px;
border-radius: 50%;
font-size: 26px;
font-weight: 700;
color: #fff;
background: linear-gradient(135deg, #2d5016, #4a7c2a);
display: none;
}
.onb-nav {
display: flex;
gap: 12px;
margin-top: 28px;
}
.onb-btn {
flex: 1;
padding: 13px;
border: none;
border-radius: 10px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.onb-btn-primary {
background: linear-gradient(135deg, #4a7c2a, #2d5016);
color: #fff;
}
.onb-btn-primary:hover { filter: brightness(1.1); transform: translateY(-1px); }
.onb-btn-secondary {
background: rgba(255,255,255,0.08);
color: rgba(255,255,255,0.7);
border: 1px solid rgba(255,255,255,0.12);
}
.onb-btn-secondary:hover { background: rgba(255,255,255,0.12); }
.onb-progress {
display: flex;
justify-content: center;
gap: 8px;
margin-bottom: 28px;
}
.onb-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: rgba(255,255,255,0.2);
transition: background 0.3s;
}
.onb-dot.active {
background: #4a7c2a;
transform: scale(1.3);
}
.onb-info {
font-size: 12px;
color: rgba(255,255,255,0.4);
margin-top: 10px;
line-height: 1.5;
}
/* Loading screen */
#loadingScreen {
display: none;
position: fixed;
inset: 0;
background: linear-gradient(135deg, #0f0f1a, #1a1a2e);
z-index: 9998;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 18px;
}
#loadingScreen.active { display: flex; }
.loading-spinner {
width: 44px;
height: 44px;
border: 3px solid rgba(255,255,255,0.1);
border-top-color: #4a7c2a;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { color: rgba(255,255,255,0.6); font-size: 15px; }
</style>
</head>
<body>
<!-- ==================== TELA DE CARREGAMENTO ==================== -->
<div id="loadingScreen" class="active">
<img src="https://eduardocm.<?> style="height:60px;opacity:0.9;" alt="Logo" onerror="this.style.display='none'">
<div class="loading-spinner"></div>
<div class="loading-text">Verificando configuração...</div>
</div>
<!-- ==================== ONBOARDING ==================== -->
<div id="onboardingOverlay">
<div class="onb-container">
<img src="https://eduardocm.<?> class="onb-dev-logo" alt="Logo Desenvolvedor" onerror="this.style.display='none'">
<div class="onb-title">Bem-vindo ao Sistema!</div>
<div class="onb-subtitle">Configure sua empresa em poucos passos para começar a usar.</div>
<div class="onb-progress">
<div class="onb-dot active" id="dot0"></div>
<div class="onb-dot" id="dot1"></div>
<div class="onb-dot" id="dot2"></div>
<div class="onb-dot" id="dot3"></div>
<div class="onb-dot" id="dot4"></div>
</div>
<!-- PASSO 1: Nome da empresa -->
<div class="onb-step active" id="step0">
<span class="onb-label">Passo 1 de 4 — Sua empresa</span>
<label class="onb-label" style="margin-top:14px;">Nome da empresa *</label>
<input type="text" class="onb-input" id="onbNomeEmpresa" placeholder="Ex: Agro Brasil Ltda" autocomplete="off">
<label class="onb-label">Título do sistema</label>
<input type="text" class="onb-input" id="onbTituloSistema" placeholder="Ex: Sistema de Pagamento de Lenha">
<div class="onb-nav">
<button class="onb-btn onb-btn-primary" onclick="onbProximo(1)">Próximo →</button>
</div>
</div>
<!-- PASSO 2: Logo -->
<div class="onb-step" id="step1">
<span class="onb-label">Passo 2 de 4 — Logomarca</span>
<label class="onb-label" style="margin-top:14px;">URL da logomarca <span style="color:rgba(255,255,255,0.35);font-weight:400;">(opcional)</span></label>
<input type="url" class="onb-input" id="onbLogoUrl" placeholder="https://suaempresa.com/logo.png" oninput="onbPreviewLogo()">
<div class="onb-logo-preview">
<img id="onbLogoImg" alt="Preview da logo">
<div class="onb-logo-initials" id="onbLogoInitials"></div>
</div>
<p class="onb-info">Se não informar uma URL, será gerado automaticamente um ícone com as iniciais do nome da empresa.</p>
<div class="onb-nav">
<button class="onb-btn onb-btn-secondary" onclick="onbVoltar(0)">← Voltar</button>
<button class="onb-btn onb-btn-primary" onclick="onbProximo(2)">Próximo →</button>
</div>
</div>
<!-- PASSO 3: Cores -->
<div class="onb-step" id="step2">
<span class="onb-label">Passo 3 de 4 — Cores institucionais</span>
<div class="onb-color-row" style="margin-top:14px;">
<div class="onb-color-block">
<label>Primária</label>
<div class="onb-color-preview">
<input type="color" id="onbCor1" value=getCssVar('--cor-primaria') oninput="syncColorText('onbCor1','onbCor1txt')">
<input type="text" id="onbCor1txt" value=getCssVar('--cor-primaria') maxlength="7" oninput="syncColorPicker('onbCor1txt','onbCor1')">
</div>
</div>
<div class="onb-color-block">
<label>Secundária</label>
<div class="onb-color-preview">
<input type="color" id="onbCor2" value="#4a7c2a" oninput="syncColorText('onbCor2','onbCor2txt')">
<input type="text" id="onbCor2txt" value="#4a7c2a" maxlength="7" oninput="syncColorPicker('onbCor2txt','onbCor2')">
</div>
</div>
<div class="onb-color-block">
<label>Terciária</label>
<div class="onb-color-preview">
<input type="color" id="onbCor3" value="#6d4c41" oninput="syncColorText('onbCor3','onbCor3txt')">
<input type="text" id="onbCor3txt" value="#6d4c41" maxlength="7" oninput="syncColorPicker('onbCor3txt','onbCor3')">
</div>
</div>
</div>
<div class="onb-nav" style="margin-top:28px;">
<button class="onb-btn onb-btn-secondary" onclick="onbVoltar(1)">← Voltar</button>
<button class="onb-btn onb-btn-primary" onclick="onbProximo(3)">Próximo →</button>
</div>
</div>
<!-- PASSO 4: Empresas pagadoras -->
<div class="onb-step" id="step3">
<span class="onb-label">Passo 4 de 5 — Empresas pagadoras</span>
<p class="onb-info" style="margin-top:14px;">Informe as empresas que realizarão pagamentos. Você pode adicionar mais depois diretamente na aba <strong>Contadores</strong> da planilha.</p>
<div id="onbEmpresasList" style="margin-top:14px;display:flex;flex-direction:column;gap:10px;"></div>
<button type="button" onclick="onbAdicionarEmpresa()" style="margin-top:12px;background:rgba(255,255,255,0.07);border:1px dashed rgba(255,255,255,0.25);border-radius:10px;padding:10px;color:rgba(255,255,255,0.6);font-size:13px;width:100%;cursor:pointer;">+ Adicionar empresa</button>
<div class="onb-nav">
<button class="onb-btn onb-btn-secondary" onclick="onbVoltar(2)">← Voltar</button>
<button class="onb-btn onb-btn-primary" onclick="onbProximo(4)">Próximo →</button>
</div>
</div>
<!-- PASSO 5: Acesso inicial -->
<div class="onb-step" id="step4">
<span class="onb-label">Passo 5 de 5 — Dados de acesso inicial</span>
<label class="onb-label" style="margin-top:14px;">Usuário *</label>
<input type="text" class="onb-input" id="onbLogin" placeholder="Ex: admin" autocomplete="off">
<label class="onb-label">Senha *</label>
<input type="password" class="onb-input" id="onbSenha" placeholder="Crie uma senha segura">
<p class="onb-info">Esses dados serão usados para o primeiro acesso ao sistema.</p>
<div class="onb-nav">
<button class="onb-btn onb-btn-secondary" onclick="onbVoltar(3)">← Voltar</button>
<button class="onb-btn onb-btn-primary" id="onbBtnFinalizar" onclick="onbFinalizar()">✓ Finalizar configuração</button>
</div>
</div>
</div>
</div>
<div class="header">
<div class="header-content">
<div id="headerLogoContainer">
<!-- Logo dinâmica: preenchida via applyConfig() -->
</div>
<h1 id="headerTitulo">Sistema de Pagamento de Lenha</h1>
</div>
</div>
<div class="tab-buttons">
<button class="tab-button active" onclick="openTab('cadastro')">Cadastro Fornecedor</button>
<button class="tab-button" onclick="openTab('lancamento')">Lançamento</button>
<button class="tab-button" onclick="openTab('financeiro')">Financeiro</button>
<button class="tab-button" onclick="openTab('dashboard')">Dashboard</button>
</div>
<div class="tabs">
<div id="cadastro" class="tab-content active">
<h2 style="color: #4a7c2a; margin-bottom: 25px;">Cadastro de Fornecedor</h2>
<div class="success-message" id="successCadastro"></div>
<div class="error-message" id="errorCadastro"></div>
<form id="formCadastro">
<div class="form-row">
<div class="form-group">
<label>Nome: *</label>
<input type="text" id="nomeFornecedor" required>
</div>
<div class="form-group">
<label>CPF: *</label>
<input type="text" id="cpfFornecedor" required maxlength="14" placeholder="000.000.000-00">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Veículo: *</label>
<input type="text" id="veiculoFornecedor" required>
</div>
<div class="form-group">
<label>Placa: *</label>
<input type="text" id="placaFornecedor" required maxlength="8" placeholder="ABC-1234">
</div>
</div>
<div class="button-group">
<button type="submit" class="btn btn-primary">Salvar Fornecedor</button>
<button type="button" class="btn btn-outline" onclick="limparFormCadastro()">Limpar</button>
</div>
</form>
</div>
<div id="lancamento" class="tab-content">
<div class="section-header">
<h2>Lançamento de Entrega</h2>
</div>
<div class="success-message" id="successLancamento"></div>
<div class="error-message" id="errorLancamento"></div>
<form id="formLancamento">
<div class="form-row">
<div class="form-group">
<label>Fornecedor: *</label>
<select id="fornecedorLanc" onchange="carregarVeiculosEUltimaCompra()" required>
<option value="">Selecione...</option>
</select>
</div>
<div class="form-group">
<label>Veículo: *</label>
<select id="veiculoLanc" onchange="preencherUltimaCompra()" required>
<option value="">Selecione o fornecedor primeiro</option>
</select>
</div>
<div class="form-group">
<label>Tipo de Lançamento: *</label>
<div class="radio-group">
<div class="radio-option">
<input type="radio" id="radioQtde" name="tipoLancamento" value="Qtde (m3)" checked onchange="alternarTipoLancamento()">
<label for="radioQtde">Qtde (m³)</label>
</div>
<div class="radio-option">
<input type="radio" id="radioPeso" name="tipoLancamento" value="Peso (Kg)" onchange="alternarTipoLancamento()">
<label for="radioPeso">Peso (Kg)</label>
</div>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Empresa Pagadora: *</label>
<select id="empresa" onchange="atualizarCodigo()" required>
<option value="">Carregando...</option>
</select>
</div>
<div class="form-group">
<label>Data: *</label>
<input type="date" id="dataLancamento" required>
</div>
<div class="form-group">
<label>Hora: *</label>
<input type="time" id="horaLancamento" value="08:00" required>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Produto: *</label>
<select id="produtoLanc" onchange="verificarNovoProduto()" required>
<option value="">Carregando...</option>
</select>
</div>
<div class="form-group" id="grupoCampoQuantidade">
<label>Quantidade (m³): *</label>
<input type="number" id="quantidadeLanc" step="0.01" min="0">
</div>
<div class="form-group" id="grupoCampoPeso" style="display:none;">
<label>Peso (Kg): *</label>
<input type="text" id="pesoLanc" oninput="aplicarMascaraPeso(event);">
</div>
<div class="form-group" id="grupoCampoValorUnit" style="display:none;">
<label>Valor Unit. (Kg): *</label>
<input type="text" id="valorUnitKgLanc" oninput="aplicarMascaraMoeda(event); calcularValorTotalPorPeso();">
</div>
<div class="form-group">
<label>Valor Total (R$): *</label>
<input type="text" id="valorLanc" required oninput="aplicarMascaraMoeda(event)">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Local de Entrega: *</label>
<select id="localLanc" onchange="verificarNovoLocal()" required>
<option value="">Carregando...</option>
</select>
</div>
<div class="form-group" id="grupoCampoPedido" style="display:none;">
<label>Pedido: *</label>
<input type="text" id="pedidoLanc" placeholder="000.000" maxlength="6" oninput="this.value = this.value.replace(/\D/g, '')" onblur="aplicarMascaraPedido(event)">
</div>
<div class="form-group">
<label>Observação:</label>
<textarea id="observacaoLanc" rows="3"></textarea>
</div>
</div>
<div class="button-group">
<button type="submit" class="btn btn-primary" id="btnSalvar">Salvar</button>
<button type="button" class="btn btn-outline" onclick="limparFormLancamento()">Limpar</button>
</div>
</form>
</div>
<div id="financeiro" class="tab-content">
<h2 style="color: #4a7c2a; margin-bottom: 25px;">Controle Financeiro</h2>
<div class="filter-section">
<h3 style="color: #4a7c2a; margin-bottom: 15px;">Filtros de Pesquisa</h3>
<div class="form-row">
<div class="form-group">
<label>Fornecedor:</label>
<select id="filtroFornecedor">
<option value="">Todos</option>
</select>
</div>
<div class="form-group">
<label>Código:</label>
<input type="text" id="filtroCodigo" placeholder="Ex: P-1001">
</div>
<div class="form-group">
<label>Nº Pedido:</label>
<input type="text" id="filtroPedido" placeholder="000.000" onblur="aplicarMascaraPedido(event)">
</div>
<div class="form-group">
<label>Local de Entrega:</label>
<select id="filtroLocal">
<option value="">Todos</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Status de NF:</label>
<select id="filtroStatusNF">
<option value="">Todos</option>
<option value="EM ABERTO">EM ABERTO</option>
<option value="EMITIR NF">EMITIR NF</option>
<option value="A PAGAR">A PAGAR</option>
<option value="FINALIZADO">FINALIZADO</option>
</select>
</div>
<div class="form-group">
<label>Data Início:</label>
<input type="date" id="filtroDataInicio">
</div>
<div class="form-group">
<label>Data Fim:</label>
<input type="date" id="filtroDataFim">
</div>
<div class="form-group">
<label>Nº NF:</label>
<input type="text" id="filtroNumeroNF" placeholder="Ex: 12345">
</div>
</div>
<div style="display:flex; gap:10px; margin-top:10px; align-items: center;">
<button class="btn btn-primary" onclick="aplicarFiltrosFinanceiro()">Pesquisar</button>
<button class="btn btn-secondary" onclick="limparFiltrosFinanceiro()">Limpar Filtros</button>
<button class="btn-imprimir-tabela" onclick="imprimirTabelaFinanceiro()">
<span>🖨️</span> Imprimir
</button>
<div class="resultados-por-pagina">
<label>Resultados por Página:</label>
<select id="resultadosPorPagina" onchange="aplicarFiltrosFinanceiro()">
<option value="10" selected>10</option>
<option value="15">15</option>
<option value="30">30</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="999999">Todos</option>
</select>
</div>
</div>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th style="border-left: none;">Ações</th>
<th data-coluna="status" onclick="ordenarTabela('status')">Status</th>
<th data-coluna="pagamento" onclick="ordenarTabela('pagamento')" class="group-border-left">Pagamento</th>
<th data-coluna="nf" onclick="ordenarTabela('nf')">NF</th>
<th data-coluna="pedido" onclick="ordenarTabela('pedido')">Pedido</th>
<th data-coluna="data" onclick="ordenarTabela('data')" class="group-border-left">Data</th>
<th data-coluna="hora" onclick="ordenarTabela('hora')">Hora</th>
<th data-coluna="local" onclick="ordenarTabela('local')">Local</th>
<th data-coluna="codigo" onclick="ordenarTabela('codigo')">Código</th>
<th data-coluna="fornecedor" onclick="ordenarTabela('fornecedor')" class="group-border-left">Fornecedor</th>
<th data-coluna="produto" onclick="ordenarTabela('produto')">Produto</th>
<th data-coluna="veiculo" onclick="ordenarTabela('veiculo')">Veículo</th>
<th data-coluna="placa" onclick="ordenarTabela('placa')">Placa</th>
<th data-coluna="quantidade" onclick="ordenarTabela('quantidade')" class="group-border-left">Qtd (m³)</th>
<th data-coluna="peso" onclick="ordenarTabela('peso')">Qtd (Kg)</th>
<th data-coluna="valor" onclick="ordenarTabela('valor')" class="group-border-left">Valor Total</th>
<th>Obs</th>
</tr>
</thead>
<tbody id="tabelaFinanceiro">
<tr>
<td colspan="17" class="loading">Carregando dados...</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="dashboard" class="tab-content">
<h2 style="color: #4a7c2a; margin-bottom: 25px;">Dashboard</h2>
<div class="filter-section">
<h3 style="color: #4a7c2a; margin-bottom: 15px;">Filtros de Pesquisa</h3>
<div class="form-row">
<div class="form-group">
<label>Fornecedor:</label>
<select id="dashFornecedor">
<option value="">Todos</option>
</select>
</div>
<div class="form-group">
<label>Código:</label>
<input type="text" id="dashCodigo" placeholder="Ex: P-1001">
</div>
<div class="form-group">
<label>Nº Pedido:</label>
<input type="text" id="dashPedido" placeholder="000.000" onblur="aplicarMascaraPedido(event)">
</div>
<div class="form-group">
<label>Local de Entrega:</label>
<select id="dashLocal">
<option value="">Todos</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Status de NF:</label>
<select id="dashStatusNF">
<option value="">Todos</option>
<option value="EM ABERTO">EM ABERTO</option>
<option value="EMITIR NF">EMITIR NF</option>
<option value="A PAGAR">A PAGAR</option>
<option value="FINALIZADO">FINALIZADO</option>
</select>
</div>
<div class="form-group">
<label>Data Início:</label>
<input type="date" id="dashDataInicio">
</div>
<div class="form-group">
<label>Data Fim:</label>
<input type="date" id="dashDataFim">
</div>
<div class="form-group">
<label>Nº NF:</label>
<input type="text" id="dashNumeroNF" placeholder="Ex: 12345" maxlength="8" oninput="this.value = this.value.replace(/\D/g, '')">
</div>
</div>
<div style="display:flex; gap:10px; margin-top:10px;">
<button class="btn btn-primary" onclick="atualizarDashboard()">Atualizar Dashboard</button>
<button class="btn btn-secondary" onclick="limparFiltrosDashboard()">Limpar Filtros</button>
<button class="btn-imprimir-tabela" onclick="imprimirDashboard()">
<span>🖨️</span> Imprimir
</button>
</div>
</div>
<div class="dashboard-cards" style="grid-template-columns: repeat(4, 1fr);">
<div class="card">
<h3>Qtde Recebida (Kg)</h3>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px;">
<div style="text-align: center;">
<div style="font-size: 12px; color: #a5a19e; margin-bottom: 5px;">Lenha</div>
<div class="value" style="font-size: 22px;" id="qtdeLenha">0</div>
</div>
<div style="text-align: center;">
<div style="font-size: 12px; color: #a5a19e; margin-bottom: 5px;">Casca de Arroz</div>
<div class="value" style="font-size: 22px;" id="qtdeCascaArroz">0</div>
</div>
</div>
</div>
<div class="card">
<h3>Valor Total (R$)</h3>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px;">
<div style="text-align: center;">
<div style="font-size: 12px; color: #a5a19e; margin-bottom: 5px;">Lenha</div>
<div class="value" style="font-size: 22px;" id="valorLenha">0,00</div>
</div>
<div style="text-align: center;">
<div style="font-size: 12px; color: #a5a19e; margin-bottom: 5px;">Casca de Arroz</div>
<div class="value" style="font-size: 22px;" id="valorCascaArroz">0,00</div>
</div>
</div>
</div>
<div class="card">
<h3>Total Pago (R$)</h3>
<div class="value" id="totalPago">0,00</div>
</div>
<div class="card">
<h3>Total a Pagar (R$)</h3>
<div class="value" id="totalAPagar">0,00</div>
</div>
</div>
<div class="form-row">
<div class="chart-container">
<h3>Quantidade por Local de Entrega (Kg)</h3>
<canvas id="chartLocal"></canvas>
</div>
<div class="chart-container">
<h3>Quantidade por Produto (Kg)</h3>
<canvas id="chartProduto"></canvas>
</div>
</div>
<div class="form-row">
<div class="chart-container">
<h3>Quantidade de Compras por Empresa - Últimos 6 Meses (Kg)</h3>
<canvas id="chartEmpresaQtd"></canvas>
</div>
<div class="chart-container">
<h3>Valor Total por Empresa - Últimos 6 Meses</h3>
<canvas id="chartEmpresaValor"></canvas>
</div>
</div>
<div class="form-row">
<div class="chart-container">
<h3>Quantidade por Fornecedor (Kg)</h3>
<canvas id="chartFornecedorQtd"></canvas>
</div>
<div class="chart-container">
<h3>Valor Total por Fornecedor</h3>
<canvas id="chartFornecedorValor"></canvas>
</div>
</div>
<div class="form-row">
<div class="chart-container">
<h3>Variação de Preço Médio da Lenha (Últimos 12 Meses)</h3>
<canvas id="chartPrecoLenha"></canvas>
</div>
<div class="chart-container">
<h3>Variação de Preço Médio da Casca de Arroz (Últimos 12 Meses)</h3>
<canvas id="chartPrecoCasca"></canvas>
</div>
</div>
</div>
</div>
<div id="modalPagamento" class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="close-modal" onclick="fecharModalPagamento()">×</span>
<h2>Confirmar Pagamento</h2>
</div>
<form id="formPagamento">
<input type="hidden" id="codigoPagamento">
<div class="form-group">
<label>Data do Pagamento:</label>
<input type="date" id="dataPagamento" required>
</div>
<div class="form-group">
<label>Tipo de Pagamento:</label>
<select id="tipoPagamento" onchange="atualizarNumeroPagamento()" required>
<option value="">Selecione...</option>
<option value="Cheque">Cheque</option>
<option value="Transferência">Transferência</option>
</select>
</div>
<div class="form-group">
<label>Nº de Cheque / Transferência:</label>
<input type="text" id="numeroPagamento" placeholder="Digite o número do cheque ou TED" maxlength="6" oninput="this.value = this.value.replace(/\D/g, '')" required>
</div>
<div class="form-group">
<label>Observações:</label>
<textarea id="observacaoPagamento" rows="3"></textarea>
</div>
<div class="button-group">
<button type="submit" class="btn btn-primary">Confirmar</button>
<button type="button" class="btn btn-outline" onclick="fecharModalPagamento()">Cancelar</button>
</div>
</form>
</div>
</div>
<div id="modalNotaFiscal" class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="close-modal" onclick="fecharModalNotaFiscal()">×</span>
<h2>Registrar Nota Fiscal</h2>
</div>
<form id="formNotaFiscal">
<input type="hidden" id="codigoNotaFiscal">
<div class="form-group">
<label>Data de Emissão: *</label>
<input type="date" id="dataEmissaoNF" required>
</div>
<div class="form-group">
<label>Número da NF: *</label>
<input type="text" id="numeroNF" maxlength="8" oninput="this.value = this.value.replace(/\D/g, '')" required placeholder="Ex: 12345">
</div>
<div class="form-group">
<label>Observações:</label>
<textarea id="observacaoNF" rows="3"></textarea>
</div>
<div class="button-group">
<button type="submit" class="btn btn-primary">Confirmar</button>
<button type="button" class="btn btn-outline" onclick="fecharModalNotaFiscal()">Cancelar</button>
</div>
</form>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script>
<script>
let fornecedoresCache = [];
let lancamentosCache = [];
let dadosImpressao = null;
let ultimasComprasCache = {};
let financeiroCarregado = false;
let empresaPorFornecedor = {};
const CONVERSAO_M3_PARA_KG = 285.7142857142857;
function formatarValorReal(valor) {
return 'R$ ' + parseFloat(valor).toLocaleString('pt-BR', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
}
function formatarNumero(numero) {
return parseFloat(numero).toLocaleString('pt-BR', {
minimumFractionDigits: 0,
maximumFractionDigits: 0
});
}
function formatarPedido(numero) {
if (!numero) return '';
// Remove tudo que não é número
const num = numero.toString().replace(/\D/g, '');
// Adiciona zeros à esquerda se necessário
const padded = num.padStart(6, '0');
// Formata com separador de milhar
return padded.slice(0, 3) + '.' + padded.slice(3);
}
Chart.register(ChartDataLabels);
if (typeof ChartDataLabels !== 'undefined') {
Chart.register(ChartDataLabels);
}
function formatarPedidoTabela(pedido) {
if (!pedido) return '-';
// Remove tudo que não é número ou letra
const limpo = pedido.toString().replace(/[^A-Za-z0-9]/g, '');
// Se começar com letra (ex: P-1001, D-1001), manter formato original
if (/^[A-Za-z]/.test(limpo)) {
return pedido;
}
// Se for apenas números, formatar com separador de milhar
const numero = parseInt(limpo, 10);
if (!isNaN(numero)) {
return formatarNumero(numero);
}
return pedido;
}
function openTab(tabName) {
const contents = document.querySelectorAll('.tab-content');
const buttons = document.querySelectorAll('.tab-button');
contents.forEach(content => content.classList.remove('active'));
buttons.forEach(button => button.classList.remove('active'));
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
if (tabName === 'lancamento') {
carregarDadosLancamento();
} else if (tabName === 'financeiro') {
carregarDadosFinanceiro();
} else if (tabName === 'dashboard') {
carregarDadosDashboard();
}
}
window._appConfig = null; // Configuração global da empresa
window.onload = function() {
// Mostrar loading e verificar se sistema já foi configurado
document.getElementById('loadingScreen').classList.add('active');
google.script.run
.withSuccessHandler(function(configurado) {
if (configurado) {
// Sistema já configurado — carregar configurações e iniciar normalmente
google.script.run
.withSuccessHandler(function(config) {
document.getElementById('loadingScreen').classList.remove('active');
if (config) {
window._appConfig = config;
applyConfig(config);
}
iniciarSistema();
})
.withFailureHandler(function() {
document.getElementById('loadingScreen').classList.remove('active');
iniciarSistema();
})
.obterConfiguracaoEmpresa();
} else {
// Sistema não configurado — mostrar onboarding
document.getElementById('loadingScreen').classList.remove('active');
document.getElementById('onboardingOverlay').classList.add('active');
onbAtualizarPreviewInicial();
}
})
.withFailureHandler(function() {
// Em caso de erro na verificação, iniciar normalmente
document.getElementById('loadingScreen').classList.remove('active');
iniciarSistema();
})
.verificarSistemaConfigurado();
};
/** Inicializa o sistema principal (comportamento original) */
function iniciarSistema() {
const hoje = new Date().toISOString().split('T')[0];
document.getElementById('dataLancamento').value = hoje;
document.getElementById('dataPagamento').value = hoje;
document.getElementById('dataEmissaoNF').value = hoje;
carregarFornecedores();
setTimeout(function() {
document.querySelector('.tab-button:nth-child(2)').click();
}, 100);
}
/** Aplica as configurações da empresa na interface */
function applyConfig(config) {
// Título do sistema
var titulo = config.tituloSistema || config.nomeEmpresa || 'Sistema de Pagamento de Lenha';
document.getElementById('headerTitulo').textContent = titulo;
document.title = titulo;
// Logo no header
var logoContainer = document.getElementById('headerLogoContainer');
if (config.logoUrl && config.logoUrl.trim() !== '') {
logoContainer.innerHTML = '<img src="' + config.logoUrl + '" alt="Logo" class="logo" onerror="this.style.display=\'none\'">';
window._logoUrl = config.logoUrl;
} else {
// Gerar logo com iniciais
var iniciais = gerarIniciais(config.nomeEmpresa || '');
var bgColor = config.corPrimaria || getCssVar('--cor-primaria');
logoContainer.innerHTML = '<div style="width:50px;height:50px;border-radius:50%;background:' + bgColor + ';display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px;color:#fff;">' + iniciais + '</div>';
window._logoUrl = '';
}
// Aplicar cores diretamente nos elementos via JS (compatível com GAS/Caja)
var p = config.corPrimaria || '#2d5016';
var s = config.corSecundaria || '#4a7c2a';
var t = config.corTerciaria || '#6d4c41';
var pD = shadeColor(p, -30);
var sL = shadeColor(s, 20);
var tD = shadeColor(t, -20);
// Injetar tag <style> dinâmica com as cores escolhidas
var styleEl = document.getElementById('dynamicColors');
if (!styleEl) {
styleEl = document.createElement('style');
styleEl.id = 'dynamicColors';
document.head.appendChild(styleEl);
}
styleEl.textContent = [
'.header { background: linear-gradient(135deg, ' + p + ' 0%, ' + s + ' 100%) !important; }',
'.tab-buttons { background: rgba(109,76,65,0.85); border-bottom: 2px solid ' + t + ' !important; }',
'.tab-button.active::after { background: ' + s + ' !important; }',
'.tab-button:hover { background: rgba(141,110,99,0.5); }',
'.tab-button.active { background: rgba(93,64,55,0.7); }',
'.section-header h2, .modal-header h2, .form-group label, .resultados-por-pagina label { color: ' + s + ' !important; }',
'.btn-primary { background: linear-gradient(135deg, ' + p + ' 0%, ' + s + ' 100%) !important; }',
'.btn-outline { color: ' + s + ' !important; border-color: ' + s + ' !important; }',
'.btn-outline:hover { background: ' + s + ' !important; }',
'.btn-imprimir-tabela { background: linear-gradient(135deg, ' + p + ' 0%, ' + s + ' 100%) !important; }',
'.card { border-left-color: ' + s + ' !important; }',
'.card .value { color: ' + s + ' !important; }',
'thead { background: linear-gradient(135deg, ' + p + ' 0%, ' + s + ' 100%) !important; }',
'tbody tr.selected { background: ' + s + '4d !important; border-left-color: ' + s + ' !important; }',
'.status-pago { background: ' + p + ' !important; }',
'.filter-section h3 { color: ' + s + ' !important; }',
'.tab-content h2[style*="color"] { color: ' + s + ' !important; }',
'#dashboard h2, #financeiro h2, #cadastro h2, #lancamento h2 { color: ' + s + ' !important; }'
].join('
');
}
/** Clareia (+) ou escurece (-) uma cor hex */
function shadeColor(hex, percent) {
var num = parseInt(hex.replace('#',''), 16);
var r = Math.min(255, Math.max(0, (num >> 16) + Math.round(2.55 * percent)));
var g = Math.min(255, Math.max(0, ((num >> 8) & 0x00FF) + Math.round(2.55 * percent)));
var b = Math.min(255, Math.max(0, (num & 0x0000FF) + Math.round(2.55 * percent)));
return '#' + ((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1);
}
function gerarIniciais(nome) {
if (!nome) return '??';
var partes = nome.trim().split(/\s+/).filter(function(p) { return p.length > 0; });
if (partes.length === 0) return '??';
if (partes.length === 1) return partes[0].substring(0, 2).toUpperCase();
return (partes[0][0] + partes[partes.length-1][0]).toUpperCase();
}
/* ==================== ONBOARDING FUNCTIONS ==================== */
var onbStep = 0;
function onbAtualizarDots(step) {
for (var i = 0; i < 5; i++) {
var dot = document.getElementById('dot' + i);
if (dot) dot.classList.toggle('active', i === step);
}
}
function onbProximo(targetStep) {
// Validações por passo
if (targetStep === 1) {
var nome = document.getElementById('onbNomeEmpresa').value.trim();
if (!nome) { alert('Por favor, informe o nome da empresa.'); return; }
}
// Validação passo empresas: deve ter ao menos 1 empresa
if (targetStep === 4) {
var empresaInputs = document.querySelectorAll('.onb-empresa-input');
var temEmpresa = false;
empresaInputs.forEach(function(inp) { if (inp.value.trim()) temEmpresa = true; });
if (!temEmpresa) { alert('Adicione ao menos uma empresa pagadora.'); return; }
}
document.getElementById('step' + onbStep).classList.remove('active');
onbStep = targetStep;
document.getElementById('step' + onbStep).classList.add('active');
onbAtualizarDots(onbStep);
if (onbStep === 1) { onbAtualizarPreviewLogo(); }
}
function onbVoltar(targetStep) {
document.getElementById('step' + onbStep).classList.remove('active');
onbStep = targetStep;
document.getElementById('step' + onbStep).classList.add('active');
onbAtualizarDots(onbStep);
}
function onbAtualizarPreviewInicial() {
// Inicializa preview da logo com iniciais
onbPreviewLogo();
}
function onbPreviewLogo() {
var url = (document.getElementById('onbLogoUrl') || {}).value || '';
var nomeEmpresa = (document.getElementById('onbNomeEmpresa') || {}).value || '';
var imgEl = document.getElementById('onbLogoImg');
var initEl = document.getElementById('onbLogoInitials');
if (!imgEl || !initEl) return;
if (url.trim() !== '') {
imgEl.src = url;
imgEl.style.display = 'block';
initEl.style.display = 'none';
} else {
imgEl.style.display = 'none';
var iniciais = gerarIniciais(nomeEmpresa);
initEl.textContent = iniciais;
initEl.style.display = 'inline-flex';
}
}
function onbAtualizarPreviewLogo() {
onbPreviewLogo();
}
function syncColorText(pickerId, textId) {
var picker = document.getElementById(pickerId);
var text = document.getElementById(textId);
if (picker && text) text.value = picker.value;
}
function syncColorPicker(textId, pickerId) {
var text = document.getElementById(textId);
var picker = document.getElementById(pickerId);
if (text && picker && /^#[0-9A-Fa-f]{6}$/.test(text.value)) {
picker.value = text.value;
}
}
function onbFinalizar() {
var login = document.getElementById('onbLogin').value.trim();
var senha = document.getElementById('onbSenha').value.trim();
if (!login || !senha) { alert('Por favor, informe o usuário e a senha de acesso.'); return; }
var btn = document.getElementById('onbBtnFinalizar');
btn.disabled = true;
btn.textContent = 'Salvando...';
// Coletar empresas pagadoras
var empresasInputs = document.querySelectorAll('.onb-empresa-input');
var empresasList = [];
empresasInputs.forEach(function(inp) {
var v = inp.value.trim();
if (v) empresasList.push(v);
});
var dados = {
nomeEmpresa: document.getElementById('onbNomeEmpresa').value.trim(),
tituloSistema: document.getElementById('onbTituloSistema').value.trim(),
logoUrl: document.getElementById('onbLogoUrl').value.trim(),
corPrimaria: document.getElementById('onbCor1').value,
corSecundaria: document.getElementById('onbCor2').value,
corTerciaria: document.getElementById('onbCor3').value,
loginUsuario: login,
loginSenha: senha,
empresas: empresasList
};
google.script.run
.withSuccessHandler(function(result) {
if (result && result.sucesso) {
window._appConfig = dados;
applyConfig(dados);
document.getElementById('onboardingOverlay').classList.remove('active');
iniciarSistema();
} else {
alert('Erro ao salvar: ' + (result ? result.mensagem : 'Erro desconhecido'));
btn.disabled = false;
btn.textContent = '✓ Finalizar configuração';
}
})
.withFailureHandler(function(err) {
alert('Erro ao salvar configuração: ' + err);
btn.disabled = false;
btn.textContent = '✓ Finalizar configuração';
})
.salvarConfiguracaoInicial(dados);
}
function onbAdicionarEmpresa() {
var list = document.getElementById('onbEmpresasList');
var idx = list.children.length;
var row = document.createElement('div');
row.style.cssText = 'display:flex;gap:8px;align-items:center;';
row.innerHTML = '<input type="text" class="onb-input onb-empresa-input" placeholder="Nome da empresa" style="flex:1;" />' +
'<button type="button" onclick="this.parentElement.remove()" style="background:rgba(255,80,80,0.2);border:1px solid rgba(255,80,80,0.3);border-radius:8px;padding:10px 14px;color:#ff8080;cursor:pointer;font-size:16px;">✕</button>';
list.appendChild(row);
// Focus new input
row.querySelector('input').focus();
}
/** Obtém valor de uma variável CSS do :root */
function getCssVar(varName) {
return getComputedStyle(document.documentElement).getPropertyValue(varName).trim() || varName;
}
/* ==================== FIM ONBOARDING ==================== */
document.getElementById('formCadastro').addEventListener('submit', function(e) {
e.preventDefault();
const btnSalvar = e.target.querySelector('button[type="submit"]');
btnSalvar.disabled = true;
const dados = {
nome: document.getElementById('nomeFornecedor').value,
cpf: document.getElementById('cpfFornecedor').value,
veiculo: document.getElementById('veiculoFornecedor').value,
placa: document.getElementById('placaFornecedor').value
};
google.script.run.withSuccessHandler(function(msg) {
mostrarMensagem('successCadastro', msg);
limparFormCadastro();
carregarFornecedores();
btnSalvar.disabled = false;
}).withFailureHandler(function(error) {
mostrarMensagem('errorCadastro', 'Erro ao salvar: ' + error);
btnSalvar.disabled = false;
}).salvarFornecedor(dados);
});
function limparFormCadastro() {
document.getElementById('formCadastro').reset();
}
function carregarFornecedores() {
google.script.run.withSuccessHandler(function(fornecedores) {
fornecedoresCache = fornecedores || [];
const selectLanc = document.getElementById('fornecedorLanc');
const selectFinanceiro = document.getElementById('filtroFornecedor');
const selectDash = document.getElementById('dashFornecedor');
selectLanc.innerHTML = '<option value="">Selecione...</option>';
selectFinanceiro.innerHTML = '<option value="">Todos</option>';
selectDash.innerHTML = '<option value="">Todos</option>';
const fornecedoresUnicos = [...new Set(fornecedores.map(f => f.nome))];
fornecedoresUnicos.forEach(nome => {
selectLanc.innerHTML += `<option value="${nome}">${nome}</option>`;
selectFinanceiro.innerHTML += `<option value="${nome}">${nome}</option>`;
selectDash.innerHTML += `<option value="${nome}">${nome}</option>`;
});
}).listarFornecedores();
}
function carregarDadosLancamento() {
// Carregar empresas da planilha Contadores
google.script.run.withSuccessHandler(function(empresas) {
var selectEmpresa = document.getElementById('empresa');
selectEmpresa.innerHTML = '<option value="">Selecione...</option>';
(empresas || []).forEach(function(emp) {
selectEmpresa.innerHTML += '<option value="' + emp + '">' + emp + '</option>';
});
}).listarEmpresas();
google.script.run.withSuccessHandler(function(produtos) {
const select = document.getElementById('produtoLanc');
select.innerHTML = '<option value="">Selecione...</option>';
produtos.forEach(p => {
select.innerHTML += `<option value="${p}">${p}</option>`;
});
select.innerHTML += '<option value="+">+ Adicionar novo produto</option>';
}).listarProdutos();
google.script.run.withSuccessHandler(function(locais) {
const select = document.getElementById('localLanc');
select.innerHTML = '<option value="">Selecione...</option>';
locais.forEach(l => {
select.innerHTML += `<option value="${l}">${l}</option>`;
});
select.innerHTML += '<option value="+">+ Adicionar novo local</option>';
}).listarLocais();
if (fornecedoresCache.length === 0) {
carregarFornecedores();
}
}
function verificarNovoProduto() {
const select = document.getElementById('produtoLanc');
if (select.value === '+') {
const produto = prompt('Digite o nome do novo produto:');
if (produto && produto.trim()) {
google.script.run.withSuccessHandler(function(msg) {
alert(msg);
carregarDadosLancamento();
}).adicionarProduto(produto.trim());
} else {
select.value = '';
}
}
}
function verificarNovoLocal() {
const select = document.getElementById('localLanc');
if (select.value === '+') {
const local = prompt('Digite o nome do novo local:');
if (local && local.trim()) {
google.script.run.withSuccessHandler(function(msg) {
alert(msg);
carregarDadosLancamento();
}).adicionarLocal(local.trim());
} else {
select.value = '';
}
}
}
function atualizarCodigo() {
const empresa = document.getElementById('empresa').value;
if (empresa) {
google.script.run.withSuccessHandler(function(codigo) {
// Não exibir mais o código na parte superior
}).gerarProximoCodigo(empresa);
}
}
function alternarTipoLancamento() {
const tipoPeso = document.getElementById('radioPeso').checked;
// Mostrar/ocultar campos
document.getElementById('grupoCampoQuantidade').style.display = tipoPeso ? 'none' : 'block';
document.getElementById('grupoCampoPeso').style.display = tipoPeso ? 'block' : 'none';
document.getElementById('grupoCampoValorUnit').style.display = tipoPeso ? 'block' : 'none';
document.getElementById('grupoCampoPedido').style.display = tipoPeso ? 'block' : 'none';
// Ajustar required
document.getElementById('quantidadeLanc').required = !tipoPeso;
document.getElementById('pesoLanc').required = tipoPeso;
document.getElementById('valorUnitKgLanc').required = tipoPeso;
document.getElementById('pedidoLanc').required = tipoPeso;
// Ajustar readonly do Valor Total
const campoValorTotal = document.getElementById('valorLanc');
if (tipoPeso) {
campoValorTotal.readOnly = true;
campoValorTotal.style.backgroundColor = 'rgba(26, 20, 16, 0.4)';
campoValorTotal.style.cursor = 'not-allowed';
} else {
campoValorTotal.readOnly = false;
campoValorTotal.style.backgroundColor = 'rgba(26, 20, 16, 0.6)';
campoValorTotal.style.cursor = 'text';
}
}
function calcularValorTotalPorPeso() {
// Obter peso (remover pontos de milhar e trocar vírgula por ponto)
const pesoStr = document.getElementById('pesoLanc').value;
const peso = parseFloat(pesoStr.replace(/\./g, '').replace(',', '.')) || 0;
// Obter valor unitário (remover pontos de milhar e trocar vírgula por ponto)
const valorUnitStr = document.getElementById('valorUnitKgLanc').value;
const valorUnit = parseFloat(valorUnitStr.replace(/\./g, '').replace(',', '.')) || 0;
const valorTotal = peso * valorUnit;
// Formatar com máscara monetária
const valorFormatado = formatarMoeda(valorTotal);
document.getElementById('valorLanc').value = valorFormatado;
}
function formatarMoeda(valor) {
if (isNaN(valor)) valor = 0;
return valor.toLocaleString('pt-BR', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
}
function aplicarMascaraMoeda(event) {
let valor = event.target.value;
// Remove tudo que não é número
valor = valor.replace(/\D/g, '');
// Converte para número
valor = (parseFloat(valor) / 100).toFixed(2);
// Formata
valor = valor.replace('.', ',');
valor = valor.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1.');
event.target.value = valor;
}
function aplicarMascaraPeso(event) {
let valor = event.target.value;
valor = valor.replace(/\D/g, ''); // mantém apenas números
if (valor !== '') {
valor = parseInt(valor, 10).toString(); // remove casas decimais
valor = valor.replace(/\B(?=(\d{3})+(?!\d))/g, '.'); // adiciona pontos de milhar
}
event.target.value = valor;
calcularValorTotalPorPeso(); // recalcula automático
}
function aplicarMascaraPedido(event) {
let valor = event.target.value;
// Remove tudo que não é número
valor = valor.replace(/\D/g, '');
// Limita a 6 dígitos
if (valor.length > 6) {
valor = valor.substring(0, 6);
}
// Adiciona zeros à esquerda
valor = valor.padStart(6, '0');
// Adiciona separador de milhar
valor = valor.slice(0, 3) + '.' + valor.slice(3);
event.target.value = valor;
}
function carregarVeiculosEUltimaCompra() {
const fornecedor = document.getElementById('fornecedorLanc').value;
const selectVeiculo = document.getElementById('veiculoLanc');
const selectEmpresa = document.getElementById('empresa');
if (!fornecedor) {
selectVeiculo.innerHTML = '<option value="">Selecione o fornecedor primeiro</option>';
return;
}
if (empresaPorFornecedor[fornecedor]) {
selectEmpresa.value = empresaPorFornecedor[fornecedor];
atualizarCodigo();
}
const veiculosFornecedor = fornecedoresCache.filter(f => f.nome === fornecedor);
if (veiculosFornecedor.length === 0) {
selectVeiculo.innerHTML = '<option value="">Nenhum veículo cadastrado</option>';
return;
}
if (veiculosFornecedor.length === 1) {
const veic = veiculosFornecedor[0].veiculo;
const placa = veiculosFornecedor[0].placa;
selectVeiculo.innerHTML = `<option value="${veic}|${placa}">${veic} - ${placa}</option>`;
carregarUltimaCompraCache(fornecedor, veic);
} else {
selectVeiculo.innerHTML = '<option value="">Selecione...</option>';
veiculosFornecedor.forEach(v => {
selectVeiculo.innerHTML += `<option value="${v.veiculo}|${v.placa}">${v.veiculo} - ${v.placa}</option>`;
});
}
}
function preencherUltimaCompra() {
const fornecedor = document.getElementById('fornecedorLanc').value;
const veiculo = document.getElementById('veiculoLanc').value.split('|')[0];
if (!fornecedor || !veiculo) return;
carregarUltimaCompraCache(fornecedor, veiculo);
}
function carregarUltimaCompraCache(fornecedor, veiculo) {
const chaveCache = `${fornecedor}|${veiculo}`;
if (ultimasComprasCache[chaveCache]) {
preencherFormularioComUltimaCompra(ultimasComprasCache[chaveCache]);
return;
}
google.script.run
.withSuccessHandler(function(ultimaCompra) {
if (ultimaCompra) {
ultimasComprasCache[chaveCache] = ultimaCompra;
if (ultimaCompra.empresa) {
empresaPorFornecedor[fornecedor] = ultimaCompra.empresa;
document.getElementById('empresa').value = ultimaCompra.empresa;
atualizarCodigo();
}
preencherFormularioComUltimaCompra(ultimaCompra);
} else {
preencherFormularioComUltimaCompra(null);
}
})
.withFailureHandler(function(error) {
console.error('Erro ao carregar última compra:', error);
preencherFormularioComUltimaCompra(null);
})
.obterUltimaCompraComEmpresa(fornecedor, veiculo);
}
function preencherFormularioComUltimaCompra(ultimaCompra) {
setTimeout(function() {
if (ultimaCompra) {
// Definir tipo de lançamento
if (ultimaCompra.tipoLancamento === 'Peso (Kg)') {
document.getElementById('radioPeso').checked = true;
alternarTipoLancamento();
// Formatar peso com 1 casa decimal
if (ultimaCompra.peso) {
const pesoFormatado = parseFloat(ultimaCompra.peso).toFixed(1).replace('.', ',');
document.getElementById('pesoLanc').value = pesoFormatado;
}
// Formatar valor unitário
if (ultimaCompra.valorUnitKg) {
const valorUnitFormatado = formatarMoeda(ultimaCompra.valorUnitKg);
document.getElementById('valorUnitKgLanc').value = valorUnitFormatado;
}
// Formatar pedido
if (ultimaCompra.pedido) {
document.getElementById('pedidoLanc').value = formatarPedido(ultimaCompra.pedido);
}
} else {
document.getElementById('radioQtde').checked = true;
alternarTipoLancamento();
document.getElementById('quantidadeLanc').value = ultimaCompra.quantidade || '';
}
document.getElementById('produtoLanc').value = ultimaCompra.produto || '';
// Formatar valor total
if (ultimaCompra.valorTotal) {
document.getElementById('valorLanc').value = formatarMoeda(ultimaCompra.valorTotal);
}
document.getElementById('localLanc').value = ultimaCompra.localEntrega || '';
document.getElementById('observacaoLanc').value = ultimaCompra.observacao || '';
} else {
document.getElementById('radioQtde').checked = true;
alternarTipoLancamento();
document.getElementById('produtoLanc').value = '';
document.getElementById('quantidadeLanc').value = '';
document.getElementById('valorLanc').value = '';
document.getElementById('localLanc').value = '';
document.getElementById('observacaoLanc').value = 'Primeira Compra';
}
}, 50);
}
document.getElementById('formLancamento').addEventListener('submit', function(e) {
e.preventDefault();
const btnSalvar = document.getElementById('btnSalvar');
btnSalvar.disabled = true;
const veiculoPlaca = document.getElementById('veiculoLanc').value.split('|');
const fornecedor = document.getElementById('fornecedorLanc').value;
const fornecedorObj = fornecedoresCache.find(f => f.nome === fornecedor);
const empresa = document.getElementById('empresa').value;
const tipoPeso = document.getElementById('radioPeso').checked;
const dados = {
empresa: empresa,
data: document.getElementById('dataLancamento').value,
hora: document.getElementById('horaLancamento').value,
fornecedor: fornecedor,
cpf: fornecedorObj ? fornecedorObj.cpf : '',
veiculo: veiculoPlaca[0],
placa: veiculoPlaca[1],
produto: document.getElementById('produtoLanc').value,
valorTotal: document.getElementById('valorLanc').value.replace(/\./g, '').replace(',', '.'),
localEntrega: document.getElementById('localLanc').value,
observacao: document.getElementById('observacaoLanc').value,
tipoLancamento: tipoPeso ? 'Peso (Kg)' : 'Qtde (m3)'
};
if (tipoPeso) {
dados.peso = document.getElementById('pesoLanc').value.replace(/\./g, '').replace(',', '.');
dados.valorUnitKg = document.getElementById('valorUnitKgLanc').value.replace(/\./g, '').replace(',', '.');
dados.pedido = document.getElementById('pedidoLanc').value.replace(/\./g, '');
dados.quantidade = '';
} else {
dados.quantidade = document.getElementById('quantidadeLanc').value;
dados.peso = '';
dados.valorUnitKg = '';
dados.pedido = '';
}
google.script.run.withSuccessHandler(function(resultado) {
mostrarMensagem('successLancamento', resultado.mensagem);
dadosImpressao = resultado.dadosCompletos;
empresaPorFornecedor[fornecedor] = empresa;
const chaveCache = `${fornecedor}|${veiculoPlaca[0]}`;
delete ultimasComprasCache[chaveCache];
financeiroCarregado = false;
if (confirm('Deseja imprimir as 3 vias do comprovante?')) {
imprimirComprovante(resultado.dadosCompletos);
}
limparFormLancamento();
btnSalvar.disabled = false;
}).withFailureHandler(function(error) {
mostrarMensagem('errorLancamento', 'Erro ao salvar: ' + error);
btnSalvar.disabled = false;
}).salvarLancamento(dados);
});
function limparFormLancamento() {
document.getElementById('formLancamento').reset();
document.getElementById('horaLancamento').value = '08:00';
const hoje = new Date().toISOString().split('T')[0];
document.getElementById('dataLancamento').value = hoje;
document.getElementById('veiculoLanc').innerHTML = '<option value="">Selecione o fornecedor primeiro</option>';
document.getElementById('radioQtde').checked = true;
alternarTipoLancamento();
}
function imprimirComprovante(dados) {
const html = `<html><head><title>Comprovante de Entrega</title><style>@page{size:A4;margin:10mm}body{font-family:Arial,sans-serif;margin:0;padding:20px;zoom:0.8}.voucher{border:2px solid #2d5016;padding:20px;margin-bottom:10px;page-break-inside:avoid}.voucher-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;border-bottom:2px solid #2d5016;padding-bottom:15px}.logo{height:40px}.voucher-title{font-size:16px;font-weight:bold;color:#2d5016;text-align:center;flex-grow:1}.voucher-via{background:#2d5016;color:white;padding:5px 15px;border-radius:5px;font-size:12px}.voucher-info{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:15px;font-size:13px}.voucher-field{margin-bottom:8px}.voucher-field strong{color:#2d5016}.voucher-signature{margin-top:50px;display:flex;justify-content:space-between}.signature-line{width:200px;text-align:center;font-size:12px}.signature-line-top{border-top:1px solid #333;padding-top:5px}.signature-date{margin-top:15px;text-align:center}.voucher-separator{border-top:2px dashed #999;margin:20px 0;text-align:center;position:relative}.voucher-separator::before{content:'✂ CORTAR AQUI';background:white;padding:0 10px;position:relative;top:-10px;color:#999;font-size:11px}@media print{.voucher{page-break-inside:avoid}}</style></head><body>${gerarVoucherHTML(dados,'FINANCEIRO')}<div class="voucher-separator"></div>${gerarVoucherHTML(dados,'CONTABILIDADE')}<div class="voucher-separator"></div>${gerarVoucherHTML(dados,'ALMOXARIFADO')}</body></html>`;
const novaJanela = window.open('', '_blank');
novaJanela.document.write(html);
novaJanela.document.close();
setTimeout(function() { novaJanela.print(); }, 500);
}
function gerarVoucherHTML(dados, via) {
let infoQuantidade = '';
let tituloComprovante = 'COMPROVANTE DE ENTREGA DE LENHA';
// Alterar título conforme o produto
if (dados.produto && dados.produto.toLowerCase().includes('casca de arroz')) {
tituloComprovante = 'COMPROVANTE DE ENTREGA DE CASCA DE ARROZ';
} else if (dados.produto && dados.produto.toLowerCase() !== 'lenha') {
tituloComprovante = 'COMPROVANTE DE ENTREGA DE ' + dados.produto.toUpperCase();
}
if (dados.tipoLancamento === 'Peso (Kg)') {
const pesoFormatado = parseFloat(dados.peso).toFixed(1).replace('.', ',');
const valorUnitFormatado = parseFloat(dados.valorUnitKg).toFixed(2).replace('.', ',');
const pedidoFormatado = formatarPedido(dados.pedido);
infoQuantidade = `
<div class="voucher-field"><strong>Pedido:</strong> ${pedidoFormatado}</div>
<div class="voucher-field"><strong>Peso:</strong> ${pesoFormatado} Kg</div>
<div class="voucher-field"><strong>Valor Unit. (Kg):</strong> R$ ${valorUnitFormatado}</div>
<div class="voucher-field"><strong>Local de Entrega:</strong> ${dados.localEntrega}</div>
<div class="voucher-field"><strong>Observação:</strong> ${dados.observacao || '-'}</div>
`;
} else {
infoQuantidade = `
<div class="voucher-field"><strong>Código:</strong> ${dados.codigo}</div>
<div class="voucher-field"><strong>Quantidade:</strong> ${dados.quantidade} m³</div>
<div class="voucher-field"><strong>Local de Entrega:</strong> ${dados.localEntrega}</div>
${dados.observacao ? `<div class="voucher-field"><strong>Observação:</strong> ${dados.observacao}</div>` : ''}
`;
}
const valorTotalFormatado = parseFloat(dados.valorTotal).toFixed(2).replace('.', ',');
return `<div class="voucher"><div class="voucher-header"><img src="${window._logoUrl||''}" alt="Logo" class="logo"><div class="voucher-title">${tituloComprovante}</div><div class="voucher-via">${via}</div></div><div class="voucher-info"><div class="voucher-field"><strong>Empresa:</strong> ${dados.empresa}</div><div class="voucher-field"><strong>Data:</strong> ${formatarData(dados.data)}</div><div class="voucher-field"><strong>Hora:</strong> ${dados.hora}</div><div class="voucher-field"><strong>Fornecedor:</strong> ${dados.fornecedor}</div><div class="voucher-field"><strong>CPF:</strong> ${dados.cpf}</div><div class="voucher-field"><strong>Veículo:</strong> ${dados.veiculo}</div><div class="voucher-field"><strong>Placa:</strong> ${dados.placa}</div><div class="voucher-field"><strong>Produto:</strong> ${dados.produto}</div>${infoQuantidade}<div class="voucher-field"><strong>Valor Total (R$):</strong> R$ ${valorTotalFormatado}</div></div><div class="voucher-signature"><div class="signature-line"><div class="signature-date">Data: ______/______/_________</div></div><div class="signature-line"><div class="signature-line-top">Assinatura</div></div></div></div>`;
}
function imprimirLancamento(codigo) {
const btn = event.target.closest('button');
const originalDisabled = btn.disabled;
btn.disabled = true;
const lancamento = lancamentosCache.find(l => l.codigo === codigo);
if (!lancamento) {
alert('Lançamento não encontrado! Código: ' + codigo);
console.error('Cache:', lancamentosCache);
btn.disabled = originalDisabled;
return;
}
// Criar objeto completo para impressão
const dadosImpressao = {
codigo: lancamento.codigo,
empresa: lancamento.empresa,
data: lancamento.data,
hora: lancamento.hora,
fornecedor: lancamento.fornecedor,
cpf: lancamento.cpf,
veiculo: lancamento.veiculo,
placa: lancamento.placa,
produto: lancamento.produto,
quantidade: lancamento.quantidade,
valorTotal: lancamento.valorTotal,
localEntrega: lancamento.localEntrega,
observacao: lancamento.observacao,
tipoLancamento: lancamento.tipoLancamento,
peso: lancamento.peso,
valorUnitKg: lancamento.valorUnitKg,
pedido: lancamento.pedido
};
imprimirComprovante(dadosImpressao);
setTimeout(() => { btn.disabled = originalDisabled; }, 1000);
}
function carregarDadosFinanceiro() {
if (financeiroCarregado) {
return;
}
carregarFornecedores();
google.script.run.withSuccessHandler(function(locais) {
const selectLocal = document.getElementById('filtroLocal');
selectLocal.innerHTML = '<option value="">Todos</option>';
locais.forEach(l => {
selectLocal.innerHTML += `<option value="${l}">${l}</option>`;
});
}).listarLocais();
setTimeout(function() {
aplicarFiltrosFinanceiro();
financeiroCarregado = true;
}, 500);
}
function aplicarFiltrosFinanceiro() {
// Desabilitar botões durante processamento
const btnPesquisar = document.querySelector('.filter-section button.btn-primary');
const btnLimpar = document.querySelector('.filter-section button.btn-secondary');
const btnImprimir = document.querySelector('.btn-imprimir-tabela');
if (btnPesquisar) btnPesquisar.disabled = true;
if (btnLimpar) btnLimpar.disabled = true;
if (btnImprimir) btnImprimir.disabled = true;
// Mostrar "Carregando Dados"
const tbody = document.getElementById('tabelaFinanceiro');
tbody.innerHTML = '<tr><td colspan="16" class="loading">Carregando dados...</td></tr>';
const filtros = {
fornecedor: document.getElementById('filtroFornecedor').value || '',
codigo: document.getElementById('filtroCodigo').value || '',
pedido: document.getElementById('filtroPedido').value || '',
numeroNF: document.getElementById('filtroNumeroNF').value || '',
local: document.getElementById('filtroLocal').value || '',
statusNF: document.getElementById('filtroStatusNF').value || '',
dataInicio: document.getElementById('filtroDataInicio').value || '',
dataFim: document.getElementById('filtroDataFim').value || '',
limite: parseInt(document.getElementById('resultadosPorPagina').value) || 15
};
google.script.run.withSuccessHandler(function (respostaJson) {
try {
const resposta = JSON.parse(respostaJson);
if (resposta.sucesso) {
lancamentosCache = resposta.lancamentos || [];
renderizarTabelaFinanceiro(resposta.lancamentos);
} else {
mostrarMensagem('errorLancamento', resposta.mensagem || 'Erro ao buscar lançamentos');
}
} catch (e) {
console.error('Erro ao processar resposta:', e);
}
// Reabilitar botões
if (btnPesquisar) btnPesquisar.disabled = false;
if (btnLimpar) btnLimpar.disabled = false;
if (btnImprimir) btnImprimir.disabled = false;
}).withFailureHandler(function (erro) {
console.error('Erro na execução do Apps Script:', erro);
// Reabilitar botões em caso de erro
if (btnPesquisar) btnPesquisar.disabled = false;
if (btnLimpar) btnLimpar.disabled = false;
if (btnImprimir) btnImprimir.disabled = false;
}).buscarLancamentosFinanceiro(filtros);
}
function limparFiltrosFinanceiro() {
document.getElementById('filtroFornecedor').value = '';
document.getElementById('filtroCodigo').value = '';
document.getElementById('filtroPedido').value = '';
document.getElementById('filtroNumeroNF').value = ''; // 🆕 limpar Nº NF
document.getElementById('filtroLocal').value = '';
document.getElementById('filtroStatusNF').value = '';
document.getElementById('filtroDataInicio').value = '';
document.getElementById('filtroDataFim').value = '';
document.getElementById('resultadosPorPagina').value = '10';
aplicarFiltrosFinanceiro();
}
function calcularStatusNF(lanc) {
const temNF = lanc.numeroNF && lanc.numeroNF !== '';
const temPagamento = lanc.statusPagamento === 'PAGO';
if (!temNF && !temPagamento) return 'EM ABERTO';
if (!temNF && temPagamento) return 'EMITIR NF';
if (temNF && !temPagamento) return 'A PAGAR';
if (temNF && temPagamento) return 'FINALIZADO';
return 'EM ABERTO';
}
function renderizarTabelaFinanceiro(lancamentos) {
const tbody = document.getElementById('tabelaFinanceiro');
tbody.innerHTML = '';
if (!lancamentos || lancamentos.length === 0) {
tbody.innerHTML = '<tr><td colspan="17" style="text-align:center;">Nenhum lançamento encontrado.</td></tr>';
return;
}
lancamentos.forEach(lanc => {
const tr = document.createElement('tr');
// Calcular o status NF corretamente
const statusNF = calcularStatusNF(lanc);
let statusClass = '';
if (statusNF === 'FINALIZADO') statusClass = 'status-pago';
else if (statusNF === 'A PAGAR') statusClass = 'status-a-pagar';
else if (statusNF === 'EMITIR NF') statusClass = 'status-emitir-nf';
else statusClass = 'status-aberto';
// Ícones dos botões
const iconePagamento = lanc.statusPagamento === 'PAGO' ? '✅' : '💰';
const titlePagamento = lanc.statusPagamento === 'PAGO' ? 'Ver pagamento' : 'Registrar pagamento';
const temNF = lanc.numeroNF && lanc.numeroNF !== '';
const iconeNF = temNF ? '☑️' : '📜';
const titleNF = temNF ? 'Ver NF' : 'Registrar NF';
// Formatar quantidade e peso
const displayQtdM3 = lanc.tipoLancamento === 'Qtde (m3)' && lanc.quantidade > 0
? Math.round(parseFloat(lanc.quantidade))
: '-';
const displayPesoKg = lanc.tipoLancamento === 'Peso (Kg)' && lanc.peso > 0
? formatarNumero(lanc.peso)
: '-';
// Formatar NF com separador de milhar
const nfFormatado = lanc.numeroNF ? formatarNumero(lanc.numeroNF) : '-';
// Formatar Pedido - 6 dígitos com separador de milhar
let pedidoFormatado = '-';
if (lanc.pedido) {
const pedidoLimpo = lanc.pedido.toString().replace(/\D/g, '');
if (/^[A-Za-z]/.test(lanc.pedido.toString())) {
// Se começar com letra, manter formato original
pedidoFormatado = lanc.pedido;
} else if (pedidoLimpo) {
// Se for apenas números, formatar com 6 dígitos e separador de milhar
const pedidoNum = parseInt(pedidoLimpo, 10);
if (!isNaN(pedidoNum)) {
const pedidoPadded = pedidoNum.toString().padStart(6, '0');
pedidoFormatado = pedidoPadded.slice(0, 3) + '.' + pedidoPadded.slice(3);
}
}
}
tr.innerHTML = `
<td style="white-space: nowrap;">
<button class="btn-excluir" title="Excluir" onclick="confirmarExclusao('${lanc.codigo}')">🗑️</button>
<button class="btn-imprimir" title="Imprimir" onclick="imprimirLancamento('${lanc.codigo}')">🖨️</button>
<button class="btn-nota-fiscal" title="${titleNF}" onclick="abrirModalNotaFiscal('${lanc.codigo}')">${iconeNF}</button>
<button class="btn-pagar" title="${titlePagamento}" onclick="abrirModalPagamento('${lanc.codigo}')">${iconePagamento}</button>
</td>
<td><span class="status-badge ${statusClass}">${statusNF}</span></td>
<td class="group-border-left">${lanc.numeroPagamento || '-'}</td>
<td>${nfFormatado}</td>
<td>${pedidoFormatado}</td>
<td class="group-border-left">${formatarData(lanc.data)}</td>
<td>${lanc.hora || '-'}</td>
<td>${lanc.localEntrega || '-'}</td>
<td>${lanc.codigo || '-'}</td>
<td class="group-border-left">${lanc.fornecedor || '-'}</td>
<td>${lanc.produto || '-'}</td>
<td>${lanc.veiculo || '-'}</td>
<td>${lanc.placa || '-'}</td>
<td class="group-border-left">${displayQtdM3}</td>
<td>${displayPesoKg}</td>
<td class="group-border-left">${formatarValorReal(lanc.valorTotal || 0)}</td>
<td style="font-size: 10px; max-width: 100px; word-wrap: break-word;">${lanc.observacao || '-'}</td>
`;
// Impedir conflito de clique entre linha e botões
tr.addEventListener('click', function (e) {
if (e.target.closest('button')) {
e.stopPropagation();
return;
}
document.querySelectorAll('#tabelaFinanceiro tr').forEach(row => {
row.classList.remove('selected');
});
this.classList.add('selected');
});
tbody.appendChild(tr);
});
// Calcular totais
let totalLancamentos = lancamentos.length;
let totalQtdM3 = 0;
let totalPesoKg = 0;
let totalValor = 0;
lancamentos.forEach(lanc => {
if (lanc.tipoLancamento === 'Qtde (m3)' && lanc.quantidade > 0) {
totalQtdM3 += parseFloat(lanc.quantidade);
}
if (lanc.tipoLancamento === 'Peso (Kg)' && lanc.peso > 0) {
totalPesoKg += parseFloat(lanc.peso);
}
totalValor += parseFloat(lanc.valorTotal) || 0;
});
const trTotal = document.createElement('tr');
trTotal.className = 'linha-totais';
trTotal.innerHTML = `
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" class="group-border-left" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" class="group-border-left" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" class="group-border-left" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="text-align: right; font-weight: bold; color: white;">TOTAIS: ${totalLancamentos}</td>
<td class="group-border-left" style="font-weight: bold; color: white; text-align: center;">${totalQtdM3 > 0 ? Math.round(totalQtdM3) : '-'}</td>
<td style="font-weight: bold; color: white; text-align: center;">${totalPesoKg > 0 ? formatarNumero(totalPesoKg) : '-'}</td>
<td class="group-border-left" style="font-weight: bold; color: white; text-align: center;">${formatarValorReal(totalValor)}</td>
<td style="color: white;"></td>
`;
tbody.appendChild(trTotal);
}
function atualizarNumeroPagamento() {
const tipoPagamento = document.getElementById('tipoPagamento').value;
const numeroPagamento = document.getElementById('numeroPagamento');
if (tipoPagamento === 'Transferência') {
numeroPagamento.value = 'TED';
numeroPagamento.readOnly = true;
} else {
if (numeroPagamento.value === 'TED') {
numeroPagamento.value = '';
}
numeroPagamento.readOnly = false;
numeroPagamento.placeholder = 'Digite o número do cheque';
}
}
function abrirModalPagamento(codigo) {
const lancamento = lancamentosCache.find(l => l.codigo === codigo);
if (!lancamento) {
alert('Lançamento não encontrado no cache!');
return;
}
document.getElementById('codigoPagamento').value = codigo;
// Se já existe pagamento, preencher os campos
if (lancamento.statusPagamento === 'PAGO') {
document.getElementById('dataPagamento').value = lancamento.dataPagamento ?
new Date(lancamento.dataPagamento).toISOString().split('T')[0] : '';
document.getElementById('tipoPagamento').value = lancamento.tipoPagamento || '';
document.getElementById('numeroPagamento').value = lancamento.numeroPagamento || '';
document.getElementById('observacaoPagamento').value = lancamento.obsPagamento || '';
// Modo visualização/edição
const btnSubmit = document.querySelector('#formPagamento button[type="submit"]');
btnSubmit.textContent = 'Atualizar Pagamento';
} else {
// Limpar campos para novo pagamento
document.getElementById('numeroPagamento').value = '';
document.getElementById('observacaoPagamento').value = '';
document.getElementById('tipoPagamento').value = '';
const hoje = new Date().toISOString().split('T')[0];
document.getElementById('dataPagamento').value = hoje;
const btnSubmit = document.querySelector('#formPagamento button[type="submit"]');
btnSubmit.textContent = 'Confirmar Pagamento';
}
document.getElementById('numeroPagamento').readOnly = false;
document.getElementById('modalPagamento').classList.add('active');
}
function fecharModalPagamento() {
document.getElementById('modalPagamento').classList.remove('active');
document.getElementById('formPagamento').reset();
const hoje = new Date().toISOString().split('T')[0];
document.getElementById('dataPagamento').value = hoje;
document.getElementById('dataPagamento').disabled = false;
document.getElementById('tipoPagamento').disabled = false;
document.getElementById('numeroPagamento').disabled = false;
document.getElementById('numeroPagamento').readOnly = false;
document.getElementById('observacaoPagamento').disabled = false;
const btnSubmit = document.querySelector('#formPagamento button[type="submit"]');
btnSubmit.textContent = 'Confirmar';
btnSubmit.style.display = 'inline-block';
}
document.getElementById('formPagamento').addEventListener('submit', function(e) {
e.preventDefault();
const btnConfirmar = e.target.querySelector('button[type="submit"]');
btnConfirmar.disabled = true;
const codigo = document.getElementById('codigoPagamento').value;
const dataPagamento = document.getElementById('dataPagamento').value;
const tipoPagamento = document.getElementById('tipoPagamento').value;
const numeroPagamento = document.getElementById('numeroPagamento').value;
const observacaoPagamento = document.getElementById('observacaoPagamento').value;
google.script.run.withSuccessHandler(function(msg) {
alert(msg);
fecharModalPagamento();
financeiroCarregado = false;
aplicarFiltrosFinanceiro();
btnConfirmar.disabled = false;
}).withFailureHandler(function(error) {
alert('Erro ao registrar pagamento: ' + error);
btnConfirmar.disabled = false;
}).registrarPagamento(codigo, dataPagamento, tipoPagamento, numeroPagamento, observacaoPagamento);
});
function abrirModalNotaFiscal(codigo) {
const lancamento = lancamentosCache.find(l => l.codigo === codigo);
if (!lancamento) {
alert('Lançamento não encontrado no cache!');
return;
}
document.getElementById('codigoNotaFiscal').value = codigo;
// Se já existe NF, preencher os campos
if (lancamento.numeroNF && lancamento.numeroNF !== '') {
document.getElementById('dataEmissaoNF').value = lancamento.dataEmissaoNF ?
new Date(lancamento.dataEmissaoNF).toISOString().split('T')[0] : '';
document.getElementById('numeroNF').value = lancamento.numeroNF || '';
document.getElementById('observacaoNF').value = lancamento.obsNF || '';
// Modo visualização/edição
const btnSubmit = document.querySelector('#formNotaFiscal button[type="submit"]');
btnSubmit.textContent = 'Atualizar NF';
} else {
// Limpar campos para nova NF
const hoje = new Date().toISOString().split('T')[0];
document.getElementById('dataEmissaoNF').value = hoje;
document.getElementById('numeroNF').value = '';
document.getElementById('observacaoNF').value = '';
const btnSubmit = document.querySelector('#formNotaFiscal button[type="submit"]');
btnSubmit.textContent = 'Confirmar NF';
}
document.getElementById('modalNotaFiscal').classList.add('active');
}
function fecharModalNotaFiscal() {
document.getElementById('modalNotaFiscal').classList.remove('active');
document.getElementById('formNotaFiscal').reset();
const hoje = new Date().toISOString().split('T')[0];
document.getElementById('dataEmissaoNF').value = hoje;
document.getElementById('dataEmissaoNF').disabled = false;
document.getElementById('numeroNF').disabled = false;
document.getElementById('observacaoNF').disabled = false;
const btnSubmit = document.querySelector('#formNotaFiscal button[type="submit"]');
btnSubmit.textContent = 'Confirmar';
btnSubmit.style.display = 'inline-block';
}
document.getElementById('formNotaFiscal').addEventListener('submit', function(e) {
e.preventDefault();
const btnConfirmar = e.target.querySelector('button[type="submit"]');
btnConfirmar.disabled = true;
const codigo = document.getElementById('codigoNotaFiscal').value;
const dataEmissao = document.getElementById('dataEmissaoNF').value;
const numeroNF = document.getElementById('numeroNF').value;
const observacaoNF = document.getElementById('observacaoNF').value;
google.script.run.withSuccessHandler(function(msg) {
alert(msg);
fecharModalNotaFiscal();
financeiroCarregado = false;
aplicarFiltrosFinanceiro();
btnConfirmar.disabled = false;
}).withFailureHandler(function(error) {
alert('Erro ao registrar nota fiscal: ' + error);
btnConfirmar.disabled = false;
}).registrarNotaFiscal(codigo, dataEmissao, numeroNF, observacaoNF);
});
let charts = {};
function visualizarNotaFiscal(codigo) {
const lancamento = lancamentosCache.find(l => l.codigo === codigo);
if (!lancamento) {
alert('Lançamento não encontrado!');
return;
}
document.getElementById('codigoNotaFiscal').value = codigo;
document.getElementById('dataEmissaoNF').value = lancamento.dataEmissaoNF ?
new Date(lancamento.dataEmissaoNF).toISOString().split('T')[0] : '';
document.getElementById('numeroNF').value = lancamento.numeroNF || '';
document.getElementById('observacaoNF').value = lancamento.obsNF || '';
document.getElementById('dataEmissaoNF').disabled = false;
document.getElementById('numeroNF').disabled = false;
document.getElementById('observacaoNF').disabled = false;
const btnSubmit = document.querySelector('#formNotaFiscal button[type="submit"]');
btnSubmit.textContent = 'Atualizar';
btnSubmit.style.display = 'inline-block';
document.getElementById('modalNotaFiscal').classList.add('active');
}
function visualizarPagamento(codigo) {
const lancamento = lancamentosCache.find(l => l.codigo === codigo);
if (!lancamento) {
alert('Lançamento não encontrado!');
return;
}
document.getElementById('codigoPagamento').value = codigo;
document.getElementById('dataPagamento').value = lancamento.dataPagamento ?
new Date(lancamento.dataPagamento).toISOString().split('T')[0] : '';
document.getElementById('tipoPagamento').value = lancamento.tipoPagamento || '';
document.getElementById('numeroPagamento').value = lancamento.numeroPagamento || '';
document.getElementById('observacaoPagamento').value = lancamento.obsPagamento || '';
document.getElementById('dataPagamento').disabled = false;
document.getElementById('tipoPagamento').disabled = false;
document.getElementById('numeroPagamento').disabled = false;
document.getElementById('observacaoPagamento').disabled = false;
const btnSubmit = document.querySelector('#formPagamento button[type="submit"]');
btnSubmit.textContent = 'Atualizar';
btnSubmit.style.display = 'inline-block';
document.getElementById('modalPagamento').classList.add('active');
}
function carregarDadosDashboard() {
google.script.run.withSuccessHandler(function(locais) {
const selectLocal = document.getElementById('dashLocal');
selectLocal.innerHTML = '<option value="">Todos</option>';
locais.forEach(l => {
selectLocal.innerHTML += `<option value="${l}">${l}</option>`;
});
}).listarLocais();
atualizarDashboard();
}
function atualizarDashboard() {
const btnAtualizar = document.querySelector('.filter-section button.btn-primary');
const btnLimpar = document.querySelector('.filter-section button.btn-secondary');
const btnImprimir = document.querySelector('.filter-section .btn-imprimir-tabela');
// Desabilitar botões
if (btnAtualizar) btnAtualizar.disabled = true;
if (btnLimpar) btnLimpar.disabled = true;
if (btnImprimir) btnImprimir.disabled = true;
const filtros = {
fornecedor: document.getElementById('dashFornecedor').value || '',
codigo: document.getElementById('dashCodigo').value || '',
pedido: document.getElementById('dashPedido').value || '',
numeroNF: document.getElementById('dashNumeroNF').value || '',
local: document.getElementById('dashLocal').value || '',
statusNF: document.getElementById('dashStatusNF').value || '',
dataInicio: document.getElementById('dashDataInicio').value || '',
dataFim: document.getElementById('dashDataFim').value || ''
};
google.script.run.withSuccessHandler(function (dados) {
renderizarDashboard(dados);
// Reabilitar botões
if (btnAtualizar) btnAtualizar.disabled = false;
if (btnLimpar) btnLimpar.disabled = false;
if (btnImprimir) btnImprimir.disabled = false;
}).withFailureHandler(function(error) {
console.error('Erro ao carregar dashboard:', error);
alert('Erro ao carregar dados do dashboard');
// Reabilitar botões mesmo em caso de erro
if (btnAtualizar) btnAtualizar.disabled = false;
if (btnLimpar) btnLimpar.disabled = false;
if (btnImprimir) btnImprimir.disabled = false;
}).obterDadosDashboard(filtros);
}
function limparFiltrosDashboard() {
const btnAtualizar = document.querySelector('.filter-section button.btn-primary');
const btnLimpar = document.querySelector('.filter-section button.btn-secondary');
const btnImprimir = document.querySelector('.filter-section .btn-imprimir-tabela');
// Desabilitar botões
if (btnAtualizar) btnAtualizar.disabled = true;
if (btnLimpar) btnLimpar.disabled = true;
if (btnImprimir) btnImprimir.disabled = true;
document.getElementById('dashFornecedor').value = '';
document.getElementById('dashCodigo').value = '';
document.getElementById('dashPedido').value = '';
document.getElementById('dashNumeroNF').value = '';
document.getElementById('dashLocal').value = '';
document.getElementById('dashStatusNF').value = '';
document.getElementById('dashDataInicio').value = '';
document.getElementById('dashDataFim').value = '';
atualizarDashboard();
}
function renderizarDashboard(dados) {
document.getElementById('qtdeLenha').textContent = Math.round(dados.qtdeLenha).toLocaleString('pt-BR');
document.getElementById('qtdeCascaArroz').textContent = Math.round(dados.qtdeCascaArroz).toLocaleString('pt-BR');
document.getElementById('valorLenha').textContent = dados.valorLenha.toLocaleString('pt-BR', {minimumFractionDigits: 2, maximumFractionDigits: 2});
document.getElementById('valorCascaArroz').textContent = dados.valorCascaArroz.toLocaleString('pt-BR', {minimumFractionDigits: 2, maximumFractionDigits: 2});
document.getElementById('totalPago').textContent = dados.totalPago.toLocaleString('pt-BR', {minimumFractionDigits: 2});
document.getElementById('totalAPagar').textContent = dados.totalAPagar.toLocaleString('pt-BR', {minimumFractionDigits: 2});
Object.values(charts).forEach(chart => chart.destroy());
charts = {};
// Gráfico: Quantidade por Local
const ctxLocal = document.getElementById('chartLocal').getContext('2d');
charts.chartLocal = new Chart(ctxLocal, {
type: 'pie',
data: {
labels: Object.keys(dados.quantidadePorLocal),
datasets: [{
data: Object.values(dados.quantidadePorLocal),
backgroundColor: ['#4a7c2a', '#6d4c41', '#2d5016', '#8d6e63', '#558b2f']
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: { labels: { color: '#e8e6e3' } },
datalabels: {
color: '#fff',
font: { weight: 'bold', size: 14 },
formatter: (value) => value.toFixed(0)
}
}
}
});
// Gráfico: Quantidade por Produto
const ctxProduto = document.getElementById('chartProduto').getContext('2d');
charts.chartProduto = new Chart(ctxProduto, {
type: 'bar',
data: {
labels: Object.keys(dados.qtdePorProduto),
datasets: [{
label: 'Quantidade (Kg)',
data: Object.values(dados.qtdePorProduto),
backgroundColor: ['#4a7c2a', '#6d4c41', getCssVar('--cor-primaria')],
borderColor: [getCssVar('--cor-primaria'), '#5d4037', '#1a1410'],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: { display: false },
datalabels: {
color: '#fff',
font: { weight: 'bold', size: 12 },
anchor: 'end',
align: 'top',
formatter: (value) => value.toFixed(0)
}
},
scales: {
x: { ticks: { color: '#e8e6e3' } },
y: { ticks: { color: '#e8e6e3' } }
}
}
});
// Gráfico: Quantidade por Empresa por Mês (últimos 6 meses) - COM CORES CORRIGIDAS
var meses = dados.mesesParaGrafico || [];
var empresas = Object.keys(dados.compraPorEmpresa);
var produtos = ['Lenha', 'Casca de Arroz'];
var datasets = [];
var cores = {
// Cores geradas dinamicamente por índice de empresa usando variáveis CSS
};
// Preenchimento dinâmico: usa variáveis CSS para as cores dos gráficos
var _paleta = [
[getCssVar('--cor-secundaria'), getCssVar('--cor-terciaria')],
[getCssVar('--cor-primaria'), shadeColor(getCssVar('--cor-terciaria'), 20)],
['#90a4ae', '#b0bec5'],
['#a5d6a7', '#c8e6c9']
];
empresas.forEach(function(emp, idx) {
var pal = _paleta[idx % _paleta.length];
coresEmpresa[emp] = { 'Lenha': pal[0], 'Casca de Arroz': pal[1] };
// Fallback para outros produtos
Object.keys(produtos).forEach(function(prod) {
if (!coresEmpresa[emp][prod]) coresEmpresa[emp][prod] = pal[idx % pal.length];
});
});
empresas.forEach(function(empresa) {
produtos.forEach(function(produto) {
var dadosMes = [];
meses.forEach(function(mes) {
var valor = 0;
if (dados.compraPorEmpresaMes[mes] && dados.compraPorEmpresaMes[mes][empresa] && dados.compraPorEmpresaMes[mes][empresa][produto]) {
valor = dados.compraPorEmpresaMes[mes][empresa][produto];
}
dadosMes.push(valor);
});
datasets.push({
label: empresa + ' - ' + produto,
data: dadosMes,
backgroundColor: cores[empresa][produto],
borderColor: cores[empresa][produto],
borderWidth: 1
});
});
});
const ctxEmpresaQtd = document.getElementById('chartEmpresaQtd').getContext('2d');
charts.chartEmpresaQtd = new Chart(ctxEmpresaQtd, {
type: 'bar',
data: {
labels: meses,
datasets: datasets
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
display: true,
labels: { color: '#e8e6e3', font: { size: 10 } }
},
datalabels: {
color: '#fff',
font: { weight: 'bold', size: 9 },
anchor: 'center',
align: 'center',
formatter: (value) => value > 0 ? value.toFixed(0) : ''
}
},
scales: {
x: {
stacked: false,
ticks: { color: '#e8e6e3', font: { size: 10 } }
},
y: {
stacked: false,
ticks: { color: '#e8e6e3' }
}
}
}
});
// Gráfico: Valor por Empresa por Mês (últimos 6 meses) - COM CORES CORRIGIDAS
var datasetsValor = [];
empresas.forEach(function(empresa) {
produtos.forEach(function(produto) {
var dadosMes = [];
meses.forEach(function(mes) {
var valor = 0;
if (dados.valorPorEmpresaMes[mes] && dados.valorPorEmpresaMes[mes][empresa] && dados.valorPorEmpresaMes[mes][empresa][produto]) {
valor = dados.valorPorEmpresaMes[mes][empresa][produto];
}
dadosMes.push(valor);
});
datasetsValor.push({
label: empresa + ' - ' + produto,
data: dadosMes,
backgroundColor: cores[empresa][produto],
borderColor: cores[empresa][produto],
borderWidth: 1
});
});
});
const ctxEmpresaValor = document.getElementById('chartEmpresaValor').getContext('2d');
charts.chartEmpresaValor = new Chart(ctxEmpresaValor, {
type: 'bar',
data: {
labels: meses,
datasets: datasetsValor
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
display: true,
labels: { color: '#e8e6e3', font: { size: 10 } }
},
datalabels: {
color: '#fff',
font: { weight: 'bold', size: 9 },
anchor: 'center',
align: 'center',
formatter: (value) => value > 0 ? 'R$ ' + value.toFixed(0) : ''
}
},
scales: {
x: {
stacked: false,
ticks: { color: '#e8e6e3', font: { size: 10 } }
},
y: {
stacked: false,
ticks: { color: '#e8e6e3' }
}
}
}
});
// Mantém os gráficos de Fornecedor
const ctxFornecedorQtd = document.getElementById('chartFornecedorQtd').getContext('2d');
charts.chartFornecedorQtd = new Chart(ctxFornecedorQtd, {
type: 'bar',
data: {
labels: Object.keys(dados.quantidadePorFornecedor),
datasets: [{
label: 'Quantidade (Kg)',
data: Object.values(dados.quantidadePorFornecedor),
backgroundColor: '#4a7c2a',
borderColor: getCssVar('--cor-primaria'),
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
indexAxis: 'y',
plugins: {
legend: { display: false },
datalabels: {
color: '#fff',
font: { weight: 'bold', size: 11 },
anchor: 'end',
align: 'right',
formatter: (value) => value.toFixed(0)
}
},
scales: {
x: { ticks: { color: '#e8e6e3' } },
y: { ticks: { color: '#e8e6e3', font: { size: 10 } } }
}
}
});
const ctxFornecedorValor = document.getElementById('chartFornecedorValor').getContext('2d');
charts.chartFornecedorValor = new Chart(ctxFornecedorValor, {
type: 'bar',
data: {
labels: Object.keys(dados.valorPorFornecedor),
datasets: [{
label: 'Valor Total (R$)',
data: Object.values(dados.valorPorFornecedor),
backgroundColor: '#6d4c41',
borderColor: '#5d4037',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
indexAxis: 'y',
plugins: {
legend: { display: false },
datalabels: {
color: '#fff',
font: { weight: 'bold', size: 11 },
anchor: 'end',
align: 'right',
formatter: (value) => 'R$ ' + value.toFixed(0)
}
},
scales: {
x: { ticks: { color: '#e8e6e3' } },
y: { ticks: { color: '#e8e6e3', font: { size: 10 } } }
}
}
});
// Gráfico: Preço Médio LENHA (últimos 12 meses)
const mesesOrdenadosLenha = Object.keys(dados.precoMedioPorMesLenha).sort((a, b) => {
const [mesA, anoA] = a.split('/').map(Number);
const [mesB, anoB] = b.split('/').map(Number);
return (anoA * 12 + mesA) - (anoB * 12 + mesB);
}).slice(-12);
const ctxPrecoLenha = document.getElementById('chartPrecoLenha').getContext('2d');
charts.chartPrecoLenha = new Chart(ctxPrecoLenha, {
type: 'line',
data: {
labels: mesesOrdenadosLenha,
datasets: [{
label: 'Preço Médio Lenha (R$/Kg)',
data: mesesOrdenadosLenha.map(mes => dados.precoMedioPorMesLenha[mes]),
borderColor: '#4a7c2a',
backgroundColor: 'rgba(74, 124, 42, 0.1)',
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: { labels: { color: '#e8e6e3' } },
datalabels: {
color: '#4a7c2a',
font: { weight: 'bold', size: 11 },
backgroundColor: 'rgba(255, 255, 255, 0.8)',
borderRadius: 4,
padding: 4,
formatter: (value) => value ? 'R$ ' + value.toFixed(2) : ''
}
},
scales: {
x: { ticks: { color: '#e8e6e3' } },
y: { ticks: { color: '#e8e6e3' } }
}
}
});
// Gráfico: Preço Médio CASCA DE ARROZ (últimos 12 meses)
const mesesOrdenadosCasca = Object.keys(dados.precoMedioPorMesCasca).sort((a, b) => {
const [mesA, anoA] = a.split('/').map(Number);
const [mesB, anoB] = b.split('/').map(Number);
return (anoA * 12 + mesA) - (anoB * 12 + mesB);
}).slice(-12);
const ctxPrecoCasca = document.getElementById('chartPrecoCasca').getContext('2d');
charts.chartPrecoCasca = new Chart(ctxPrecoCasca, {
type: 'line',
data: {
labels: mesesOrdenadosCasca,
datasets: [{
label: 'Preço Médio Casca de Arroz (R$/Kg)',
data: mesesOrdenadosCasca.map(mes => dados.precoMedioPorMesCasca[mes]),
borderColor: '#8d6e63',
backgroundColor: 'rgba(141, 110, 99, 0.1)',
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: { labels: { color: '#e8e6e3' } },
datalabels: {
color: '#8d6e63',
font: { weight: 'bold', size: 11 },
backgroundColor: 'rgba(255, 255, 255, 0.8)',
borderRadius: 4,
padding: 4,
formatter: (value) => value ? 'R$ ' + value.toFixed(2) : ''
}
},
scales: {
x: { ticks: { color: '#e8e6e3' } },
y: { ticks: { color: '#e8e6e3' } }
}
}
});
}
function mostrarMensagem(elementId, mensagem) {
const el = document.getElementById(elementId);
el.textContent = mensagem;
el.style.display = 'block';
setTimeout(() => { el.style.display = 'none'; }, 5000);
}
function formatarData(data) {
if (!data) return '';
const d = new Date(data);
const dia = String(d.getUTCDate()).padStart(2, '0');
const mes = String(d.getUTCMonth() + 1).padStart(2, '0');
const ano = d.getUTCFullYear();
return `${dia}/${mes}/${ano}`;
}
document.getElementById('cpfFornecedor').addEventListener('input', function(e) {
let value = e.target.value.replace(/\D/g, '');
if (value.length <= 11) {
value = value.replace(/(\d{3})(\d)/, '$1.$2');
value = value.replace(/(\d{3})(\d)/, '$1.$2');
value = value.replace(/(\d{3})(\d{1,2})$/, '$1-$2');
e.target.value = value;
}
});
document.getElementById('placaFornecedor').addEventListener('input', function(e) {
let value = e.target.value.replace(/[^A-Za-z0-9]/g, '').toUpperCase();
if (value.length > 3) {
value = value.substring(0, 3) + '-' + value.substring(3, 7);
}
e.target.value = value;
});
let ordemAtual = { coluna: null, crescente: true };
function ordenarTabela(coluna) {
const tbody = document.getElementById('tabelaFinanceiro');
const linhas = Array.from(tbody.getElementsByTagName('tr')).filter(tr => !tr.classList.contains('linha-totais'));
if (ordemAtual.coluna === coluna) {
ordemAtual.crescente = !ordemAtual.crescente;
} else {
ordemAtual.coluna = coluna;
ordemAtual.crescente = true;
}
const indices = {
'status': 1, 'pagamento': 2, 'nf': 3, 'pedido': 4, 'data': 5, 'hora': 6,
'local': 7, 'codigo': 8, 'fornecedor': 9, 'veiculo': 10, 'placa': 11,
'quantidade': 12, 'peso': 13, 'valor': 14
};
const indice = indices[coluna];
linhas.sort((a, b) => {
let valorA = a.cells[indice].textContent.trim();
let valorB = b.cells[indice].textContent.trim();
if (coluna === 'quantidade' || coluna === 'peso' || coluna === 'valor') {
valorA = parseFloat(valorA.replace(/[^\d,.-]/g, '').replace(',', '.')) || 0;
valorB = parseFloat(valorB.replace(/[^\d,.-]/g, '').replace(',', '.')) || 0;
}
if (coluna === 'data') {
valorA = valorA.split('/').reverse().join('');
valorB = valorB.split('/').reverse().join('');
}
if (valorA < valorB) return ordemAtual.crescente ? -1 : 1;
if (valorA > valorB) return ordemAtual.crescente ? 1 : -1;
return 0;
});
const linhaTotal = tbody.querySelector('.linha-totais');
tbody.innerHTML = '';
linhas.forEach(linha => tbody.appendChild(linha));
if (linhaTotal) tbody.appendChild(linhaTotal);
document.querySelectorAll('th').forEach(th => {
th.classList.remove('sorted-asc', 'sorted-desc');
});
const thClicado = document.querySelector(`th[data-coluna="${coluna}"]`);
if (thClicado) {
thClicado.classList.add(ordemAtual.crescente ? 'sorted-asc' : 'sorted-desc');
}
}
function confirmarExclusao(codigo) {
const btn = event.target.closest('button');
const originalDisabled = btn.disabled;
btn.disabled = true;
if (confirm('Tem certeza que deseja excluir o lançamento ' + codigo + '? Esta ação não pode ser desfeita.')) {
google.script.run
.withSuccessHandler(function(msg) {
alert(msg);
financeiroCarregado = false;
aplicarFiltrosFinanceiro();
})
.withFailureHandler(function(error) {
alert('Erro ao excluir: ' + error.message);
btn.disabled = originalDisabled;
})
.excluirLancamento(codigo);
} else {
btn.disabled = originalDisabled;
}
}
function imprimirTabelaFinanceiro() {
const btn = event.target;
btn.disabled = true;
if (!lancamentosCache || lancamentosCache.length === 0) {
alert('Não há dados para imprimir. Por favor, realize uma pesquisa primeiro.');
btn.disabled = false;
return;
}
const filtrosAtivos = obterFiltrosAtivos();
const htmlImpressao = gerarHTMLImpressaoFinanceiro(lancamentosCache, filtrosAtivos);
const janelaImpressao = window.open('', '_blank');
janelaImpressao.document.write(htmlImpressao);
janelaImpressao.document.close();
setTimeout(function() {
janelaImpressao.focus();
janelaImpressao.print();
btn.disabled = false;
}, 500);
}
function imprimirDashboard() {
const btn = event.target.closest('button');
if (!btn) return;
btn.disabled = true;
// Verificar se os gráficos foram renderizados
if (!charts.chartLocal || !charts.chartProduto) {
alert('Aguarde o carregamento completo do dashboard antes de imprimir.');
btn.disabled = false;
return;
}
try {
const filtrosAtivos = obterFiltrosAtivosDashboard();
const htmlImpressao = gerarHTMLImpressaoDashboard(filtrosAtivos);
const janelaImpressao = window.open('', '_blank');
if (!janelaImpressao) {
alert('Pop-up bloqueado. Por favor, permita pop-ups para imprimir.');
btn.disabled = false;
return;
}
janelaImpressao.document.write(htmlImpressao);
janelaImpressao.document.close();
setTimeout(function() {
janelaImpressao.focus();
janelaImpressao.print();
btn.disabled = false;
}, 1000);
} catch (error) {
console.error('Erro ao imprimir:', error);
alert('Erro ao gerar impressão. Veja o console para detalhes.');
btn.disabled = false;
}
}
function obterFiltrosAtivos() {
const filtros = [];
const fornecedor = document.getElementById('filtroFornecedor').value;
const codigo = document.getElementById('filtroCodigo').value;
const pedido = document.getElementById('filtroPedido').value;
const local = document.getElementById('filtroLocal').value;
const statusNF = document.getElementById('filtroStatusNF').value;
const dataInicio = document.getElementById('filtroDataInicio').value;
const dataFim = document.getElementById('filtroDataFim').value;
if (fornecedor) filtros.push(`Fornecedor: ${fornecedor}`);
if (codigo) filtros.push(`Código: ${codigo}`);
if (pedido) filtros.push(`Pedido: ${pedido}`);
if (local) filtros.push(`Local: ${local}`);
if (statusNF) filtros.push(`Status NF: ${statusNF}`);
if (dataInicio && dataFim) filtros.push(`Período: ${formatarData(dataInicio)} a ${formatarData(dataFim)}`);
return filtros.length > 0 ? filtros.join(' | ') : 'Todos os registros';
}
function obterFiltrosAtivosDashboard() {
const filtros = [];
const fornecedor = document.getElementById('dashFornecedor').value;
const codigo = document.getElementById('dashCodigo').value;
const pedido = document.getElementById('dashPedido').value;
const local = document.getElementById('dashLocal').value;
const statusNF = document.getElementById('dashStatusNF').value;
const dataInicio = document.getElementById('dashDataInicio').value;
const dataFim = document.getElementById('dashDataFim').value;
if (fornecedor) filtros.push(`Fornecedor: ${fornecedor}`);
if (codigo) filtros.push(`Código: ${codigo}`);
if (pedido) filtros.push(`Pedido: ${pedido}`);
if (local) filtros.push(`Local: ${local}`);
if (statusNF) filtros.push(`Status NF: ${statusNF}`);
if (dataInicio && dataFim) filtros.push(`Período: ${formatarData(dataInicio)} a ${formatarData(dataFim)}`);
return filtros.length > 0 ? filtros.join(' | ') : 'Todos os registros';
}
function gerarHTMLImpressaoFinanceiro(lancamentos, filtrosAtivos) {
const dataHoraImpressao = new Date().toLocaleString('pt-BR');
let totalQuantidade = 0;
let totalPeso = 0;
let totalValor = 0;
let totalPago = 0;
let totalAberto = 0;
lancamentos.forEach(lanc => {
totalQuantidade += parseFloat(lanc.quantidade) || 0;
totalPeso += parseFloat(lanc.peso) || 0;
totalValor += parseFloat(lanc.valorTotal) || 0;
if (lanc.statusPagamento === 'PAGO') {
totalPago += parseFloat(lanc.valorTotal) || 0;
} else {
totalAberto += parseFloat(lanc.valorTotal) || 0;
}
});
let linhasTabela = '';
lancamentos.forEach(lanc => {
const status = calcularStatusNF(lanc);
let statusClass = 'print-status-aberto';
if (status === 'FINALIZADO') statusClass = 'print-status-pago';
const nfFormatado = lanc.numeroNF ? formatarNumero(lanc.numeroNF) : '-';
// Formatar Pedido - 6 dígitos com separador de milhar
let pedidoFormatado = '-';
if (lanc.pedido) {
const pedidoLimpo = lanc.pedido.toString().replace(/\D/g, '');
if (/^[A-Za-z]/.test(lanc.pedido.toString())) {
pedidoFormatado = lanc.pedido;
} else if (pedidoLimpo) {
const pedidoNum = parseInt(pedidoLimpo, 10);
if (!isNaN(pedidoNum)) {
const pedidoPadded = pedidoNum.toString().padStart(6, '0');
pedidoFormatado = pedidoPadded.slice(0, 3) + '.' + pedidoPadded.slice(3);
}
}
}
const displayQtdM3 = lanc.tipoLancamento === 'Qtde (m3)' && lanc.quantidade > 0 ? parseFloat(lanc.quantidade).toFixed(2) : '-';
const displayPesoKg = lanc.tipoLancamento === 'Peso (Kg)' && lanc.peso > 0 ? formatarNumero(lanc.peso) : '-';
linhasTabela += `
<tr>
<td><span class="print-status-badge ${statusClass}">${status}</span></td>
<td>${lanc.numeroPagamento || '-'}</td>
<td>${nfFormatado}</td>
<td>${pedidoFormatado}</td>
<td>${formatarData(lanc.data)}</td>
<td>${lanc.hora || '-'}</td>
<td>${lanc.codigo || '-'}</td>
<td>${lanc.fornecedor || '-'}</td>
<td>${lanc.produto || '-'}</td>
<td>${lanc.veiculo || '-'}</td>
<td>${lanc.placa || '-'}</td>
<td>${displayQtdM3}</td>
<td>${displayPesoKg}</td>
<td>${formatarValorReal(lanc.valorTotal || 0)}</td>
<td>${lanc.localEntrega || '-'}</td>
<td style="font-size: 7px; max-width: 80px; word-wrap: break-word;">${lanc.observacao || '-'}</td>
</tr>
`;
});
linhasTabela += `
<tr style="background: #6d4c41; font-weight: bold; border-top: 2px solid #2d5016; color: white;">
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="text-align: right; font-weight: bold; color: white;">TOTAIS: ${lancamentos.length}</td>
<td style="font-weight: bold; color: white; text-align: center;">${totalQuantidade > 0 ? formatarNumero(totalQuantidade) : '-'}</td>
<td style="font-weight: bold; color: white; text-align: center;">${totalPeso > 0 ? formatarNumero(totalPeso) : '-'}</td>
<td style="font-weight: bold; color: white; text-align: center;">${formatarValorReal(totalValor)}</td>
<td colspan="1" style="color: white;"></td>
<td colspan="1" style="color: white;"></td>
</tr>
`;
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Relatório Financeiro - Lenha</title>
<style>
@page { size: landscape; margin: 10mm; }
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: Arial, sans-serif; background: white; color: #000; padding: 15px; }
.print-header-financeiro { display: flex; align-items: center; justify-content: space-between; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 3px solid #2d5016; }
.print-logo-financeiro { height: 50px; }
.print-title-financeiro { font-size: 20px; font-weight: bold; color: #2d5016; text-align: center; flex-grow: 1; }
.print-info-financeiro { margin-bottom: 10px; font-size: 10px; color: #333; background: #f5f5f5; padding: 8px; border-radius: 5px; }
.print-info-financeiro strong { color: #2d5016; }
.print-table-financeiro { width: 100%; border-collapse: collapse; font-size: 7px; margin-top: 8px; }
.print-table-financeiro th { background: #2d5016; color: white; padding: 6px 3px; text-align: center; border: 1px solid #2d5016; font-weight: bold; font-size: 7px; white-space: nowrap; }
.print-table-financeiro td { padding: 4px 3px; border: 1px solid #ccc; text-align: center; color: #000; background: white; font-size: 7px; white-space: nowrap; }
.print-table-financeiro tbody tr:nth-child(even) { background: #f9f9f9; }
.print-status-badge { padding: 2px 6px; border-radius: 10px; font-weight: bold; font-size: 6px; display: inline-block; }
.print-status-pago { background: #c8e6c9; color: #2e7d32; border: 1px solid #2e7d32; }
.print-status-aberto { background: #ffcdd2; color: #c62828; border: 1px solid #c62828; }
.print-footer-financeiro { margin-top: 15px; padding-top: 8px; border-top: 2px solid #2d5016; font-size: 9px; color: #666; text-align: center; }
.print-totais { margin-top: 12px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; font-size: 10px; }
.print-total-card { background: #f5f5f5; padding: 8px; border-radius: 5px; border-left: 4px solid #2d5016; }
.print-total-card strong { display: block; color: #2d5016; margin-bottom: 4px; font-size: 9px; }
.print-total-card .valor { font-size: 12px; font-weight: bold; color: #333; }
</style>
</head>
<body>
<div class="print-area-financeiro">
<div class="print-header-financeiro">
<img src="${window._logoUrl||''}" alt="Logo" class="print-logo-financeiro">
<div class="print-title-financeiro">RELATÓRIO FINANCEIRO - PAGAMENTO DE LENHA</div>
</div>
<div class="print-info-financeiro">
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<div><strong>Data/Hora da Impressão:</strong> ${dataHoraImpressao}</div>
<div><strong>Total de Registros:</strong> ${lancamentos.length}</div>
</div>
<div><strong>Filtros Aplicados:</strong> ${filtrosAtivos}</div>
</div>
<table class="print-table-financeiro">
<thead>
<tr>
<th>Status</th>
<th>Pagamento</th>
<th>NF</th>
<th>Pedido</th>
<th>Data</th>
<th>Hora</th>
<th>Código</th>
<th>Fornecedor</th>
<th>Produto</th>
<th>Veículo</th>
<th>Placa</th>
<th>Qtd (m³)</th>
<th>Qtd (Kg)</th>
<th>Valor Total</th>
<th>Local</th>
<th>Obs</th>
</tr>
</thead>
<tbody>${linhasTabela}</tbody>
</table>
<div class="print-totais">
<div class="print-total-card">
<strong>Total de Registros</strong>
<div class="valor">${lancamentos.length}</div>
</div>
<div class="print-total-card">
<strong>Quantidade Total (m³)</strong>
<div class="valor">${totalQuantidade > 0 ? formatarNumero(totalQuantidade) : '-'}</div>
</div>
<div class="print-total-card">
<strong>Peso Total (Kg)</strong>
<div class="valor">${totalPeso > 0 ? formatarNumero(totalPeso) : '-'}</div>
</div>
<div class="print-total-card">
<strong>Valor Total (R$)</strong>
<div class="valor">${formatarValorReal(totalValor)}</div>
</div>
</div>
<div class="print-footer-financeiro">
Sistema de Pagamento de Lenha - Relatório gerado automaticamente em ${dataHoraImpressao}
</div>
</div>
</body>
</html>
`;
}
function gerarHTMLImpressaoDashboard(filtrosAtivos) {
const dataHoraImpressao = new Date().toLocaleString('pt-BR');
// Capturar valores dos cards - CORRIGIDO
const qtdeLenha = document.getElementById('qtdeLenha') ? document.getElementById('qtdeLenha').textContent : '0';
const qtdeCascaArroz = document.getElementById('qtdeCascaArroz') ? document.getElementById('qtdeCascaArroz').textContent : '0';
const valorLenha = document.getElementById('valorLenha') ? document.getElementById('valorLenha').textContent : '0,00';
const valorCascaArroz = document.getElementById('valorCascaArroz') ? document.getElementById('valorCascaArroz').textContent : '0,00';
const totalPago = document.getElementById('totalPago') ? document.getElementById('totalPago').textContent : '0,00';
const totalAPagar = document.getElementById('totalAPagar') ? document.getElementById('totalAPagar').textContent : '0,00';
// Capturar gráficos como imagens
const chartLocal = document.getElementById('chartLocal') ? document.getElementById('chartLocal').toDataURL('image/png') : '';
const chartProduto = document.getElementById('chartProduto') ? document.getElementById('chartProduto').toDataURL('image/png') : '';
const chartEmpresaQtd = document.getElementById('chartEmpresaQtd') ? document.getElementById('chartEmpresaQtd').toDataURL('image/png') : '';
const chartEmpresaValor = document.getElementById('chartEmpresaValor') ? document.getElementById('chartEmpresaValor').toDataURL('image/png') : '';
const chartFornecedorQtd = document.getElementById('chartFornecedorQtd') ? document.getElementById('chartFornecedorQtd').toDataURL('image/png') : '';
const chartFornecedorValor = document.getElementById('chartFornecedorValor') ? document.getElementById('chartFornecedorValor').toDataURL('image/png') : '';
const chartPrecoLenha = document.getElementById('chartPrecoLenha') ? document.getElementById('chartPrecoLenha').toDataURL('image/png') : '';
const chartPrecoCasca = document.getElementById('chartPrecoCasca') ? document.getElementById('chartPrecoCasca').toDataURL('image/png') : '';
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Dashboard - Relatório</title>
<style>
@page {
size: portrait;
margin: 8mm;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: Arial, sans-serif;
background: white;
color: #000;
padding: 8px;
font-size: 10px;
}
.print-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
padding-bottom: 6px;
border-bottom: 2px solid #2d5016;
}
.print-logo { height: 36px; }
.print-title {
font-size: 16px;
font-weight: bold;
color: #2d5016;
text-align: center;
flex-grow: 1;
}
.print-info {
margin-bottom: 8px;
font-size: 8px;
color: #333;
background: #f5f5f5;
padding: 6px;
border-radius: 3px;
}
.print-info strong { color: #2d5016; }
.print-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
margin: 8px 0;
}
.print-card {
background: #f5f5f5;
padding: 8px;
border-radius: 4px;
border-left: 3px solid #2d5016;
}
.print-card h3 {
color: #666;
font-size: 8px;
margin-bottom: 4px;
}
.print-card strong {
color: #2d5016;
font-size: 9px;
display: block;
margin-bottom: 6px;
}
.print-card .valor {
font-size: 14px;
font-weight: bold;
color: #2d5016;
}
.print-charts {
margin-top: 8px;
}
.print-chart-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-bottom: 8px;
page-break-inside: avoid;
}
.print-chart-full {
margin-bottom: 8px;
page-break-inside: avoid;
}
.print-chart-container {
background: #f9f9f9;
padding: 6px;
border-radius: 3px;
text-align: center;
border: 1px solid #e0e0e0;
}
.print-chart-container h3 {
color: #2d5016;
font-size: 9px;
margin-bottom: 4px;
font-weight: bold;
}
.print-chart-container img {
max-width: 100%;
height: auto;
max-height: 280px;
}
.print-chart-container.full img {
max-height: 300px;
}
.print-footer {
margin-top: 8px;
padding-top: 6px;
border-top: 2px solid #2d5016;
font-size: 8px;
color: #666;
text-align: center;
}
</style>
</head>
<body>
<div class="print-header">
<img src="${window._logoUrl||''}" alt="Logo" class="print-logo">
<div class="print-title">DASHBOARD - PAGAMENTO DE LENHA</div>
</div>
<div class="print-info">
<div style="display: flex; justify-content: space-between;">
<div><strong>Impressão:</strong> ${dataHoraImpressao}</div>
<div><strong>Filtros:</strong> ${filtrosAtivos}</div>
</div>
</div>
<div class="print-cards">
<div class="print-card">
<strong>Qtde Recebida</strong>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; font-size: 9px;">
<div style="text-align: center;">
<div style="color: #666; margin-bottom: 3px;">Lenha</div>
<div class="valor" style="font-size: 12px;">${qtdeLenha}</div>
</div>
<div style="text-align: center;">
<div style="color: #666; margin-bottom: 3px;">Casca Arroz</div>
<div class="valor" style="font-size: 12px;">${qtdeCascaArroz}</div>
</div>
</div>
</div>
<div class="print-card">
<strong>Valor Total</strong>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; font-size: 9px;">
<div style="text-align: center;">
<div style="color: #666; margin-bottom: 3px;">Lenha</div>
<div class="valor" style="font-size: 12px;">${valorLenha}</div>
</div>
<div style="text-align: center;">
<div style="color: #666; margin-bottom: 3px;">Casca Arroz</div>
<div class="valor" style="font-size: 12px;">${valorCascaArroz}</div>
</div>
</div>
</div>
<div class="print-card">
<strong>Total Pago (R$)</strong>
<div class="valor">${totalPago}</div>
</div>
<div class="print-card">
<strong>Total a Pagar (R$)</strong>
<div class="valor">${totalAPagar}</div>
</div>
</div>
<div class="print-charts">
<div class="print-chart-row">
<div class="print-chart-container">
<h3>Quantidade por Local (Kg)</h3>
<img src="${chartLocal}" alt="Gráfico Local">
</div>
<div class="print-chart-container">
<h3>Quantidade por Produto (Kg)</h3>
<img src="${chartProduto}" alt="Gráfico Produto">
</div>
</div>
<div class="print-chart-row">
<div class="print-chart-container">
<h3>Qtd por Empresa - Últimos 6 Meses (Kg)</h3>
<img src="${chartEmpresaQtd}" alt="Gráfico Empresa Qtd">
</div>
<div class="print-chart-container">
<h3>Valor por Empresa - Últimos 6 Meses</h3>
<img src="${chartEmpresaValor}" alt="Gráfico Empresa Valor">
</div>
</div>
<div class="print-chart-row">
<div class="print-chart-container">
<h3>Qtd por Fornecedor (Kg)</h3>
<img src="${chartFornecedorQtd}" alt="Gráfico Fornecedor Qtd">
</div>
<div class="print-chart-container">
<h3>Valor por Fornecedor</h3>
<img src="${chartFornecedorValor}" alt="Gráfico Fornecedor Valor">
</div>
</div>
<div class="print-chart-row">
<div class="print-chart-container">
<h3>Variação de Preço Médio da Lenha (R$/Kg)</h3>
<img src="${chartPrecoLenha}" alt="Gráfico Preço Lenha">
</div>
<div class="print-chart-container">
<h3>Variação de Preço Médio da Casca de Arroz (R$/Kg)</h3>
<img src="${chartPrecoCasca}" alt="Gráfico Preço Casca">
</div>
</div>
</div>
<div class="print-footer">
Sistema de Pagamento de Lenha - Relatório gerado em ${dataHoraImpressao}
</div>
</body>
</html>
`;
}
</script>
</body>
</html>. (line 4, file "Código")