Agregar reporte de Conceptos Especiales con filtros y exportación PDF/CSV
This commit is contained in:
124
dashboard.php
124
dashboard.php
@@ -649,10 +649,14 @@ switch ($page) {
|
||||
'balance' => 'Balance_General',
|
||||
'expenses' => 'Gastos_por_Categoria',
|
||||
'water-debtors' => 'Deudores_de_Agua',
|
||||
'concept-debtors' => 'Deudores_de_Conceptos'
|
||||
'concept-debtors' => 'Deudores_de_Conceptos',
|
||||
'concepts' => 'Conceptos_Especiales'
|
||||
];
|
||||
$reportName = $reportNames[$reportType] ?? ucfirst($reportType);
|
||||
|
||||
// Para conceptos, definir nombre personalizado del archivo PDF
|
||||
$pdfFilename = $reportName . '_IBIZA_' . $year . '.pdf';
|
||||
|
||||
// Incluir TCPDF
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/vendor/tecnickcom/tcpdf/tcpdf.php';
|
||||
@@ -709,6 +713,42 @@ switch ($page) {
|
||||
$expensesByCategory = Report::getExpensesByCategory();
|
||||
include __DIR__ . '/views/reports/pdf_expenses.php';
|
||||
break;
|
||||
case 'concepts':
|
||||
// Requerimos los modelos necesarios
|
||||
require_once __DIR__ . '/models/CollectionConcept.php';
|
||||
require_once __DIR__ . '/models/Expense.php';
|
||||
|
||||
$filterConceptId = $_GET['filter_concept'] ?? null;
|
||||
$conceptsToExport = [];
|
||||
|
||||
if ($filterConceptId) {
|
||||
$concept = CollectionConcept::findById($filterConceptId);
|
||||
if ($concept) {
|
||||
$status = CollectionConcept::getCollectionStatus($filterConceptId);
|
||||
$payments = CollectionConcept::getPaymentsByConcept($filterConceptId);
|
||||
$conceptsToExport[] = [
|
||||
'concept' => $concept,
|
||||
'status' => $status,
|
||||
'payments' => $payments
|
||||
];
|
||||
$conceptName = preg_replace('/[^a-zA-Z0-9_]/', '_', $concept['name']);
|
||||
$pdfFilename = 'Concepto_' . $conceptName . '_IBIZA.pdf';
|
||||
}
|
||||
} else {
|
||||
$allConcepts = CollectionConcept::all(true);
|
||||
foreach ($allConcepts as $c) {
|
||||
$status = CollectionConcept::getCollectionStatus($c['id']);
|
||||
$payments = CollectionConcept::getPaymentsByConcept($c['id']);
|
||||
$conceptsToExport[] = [
|
||||
'concept' => $c,
|
||||
'status' => $status,
|
||||
'payments' => $payments
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
include __DIR__ . '/views/reports/pdf_concepts.php';
|
||||
break;
|
||||
case 'balance':
|
||||
default:
|
||||
// Requerimos el modelo Report
|
||||
@@ -719,7 +759,7 @@ switch ($page) {
|
||||
}
|
||||
$html = ob_get_clean();
|
||||
$pdf->writeHTML($html, true, false, true, false, '');
|
||||
$pdf->Output($reportName . '_IBIZA_' . $year . '.pdf', 'D');
|
||||
$pdf->Output($pdfFilename, 'D');
|
||||
exit;
|
||||
|
||||
case 'export_csv_balance':
|
||||
@@ -771,6 +811,86 @@ switch ($page) {
|
||||
fclose($output);
|
||||
exit;
|
||||
|
||||
case 'export_csv_concepts':
|
||||
Auth::requireAdmin();
|
||||
require_once __DIR__ . '/models/CollectionConcept.php';
|
||||
|
||||
$filterConceptId = $_GET['filter_concept'] ?? null;
|
||||
$conceptsToExport = [];
|
||||
|
||||
if ($filterConceptId) {
|
||||
$concept = CollectionConcept::findById($filterConceptId);
|
||||
if ($concept) {
|
||||
$status = CollectionConcept::getCollectionStatus($filterConceptId);
|
||||
$payments = CollectionConcept::getPaymentsByConcept($filterConceptId);
|
||||
$conceptsToExport[] = [
|
||||
'concept' => $concept,
|
||||
'status' => $status,
|
||||
'payments' => $payments
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$allConcepts = CollectionConcept::all(true);
|
||||
foreach ($allConcepts as $c) {
|
||||
$status = CollectionConcept::getCollectionStatus($c['id']);
|
||||
$payments = CollectionConcept::getPaymentsByConcept($c['id']);
|
||||
$conceptsToExport[] = [
|
||||
'concept' => $c,
|
||||
'status' => $status,
|
||||
'payments' => $payments
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-Type: text/csv');
|
||||
|
||||
if ($filterConceptId && !empty($conceptsToExport)) {
|
||||
$conceptName = $conceptsToExport[0]['concept']['name'];
|
||||
$conceptName = preg_replace('/[^a-zA-Z0-9_]/', '_', $conceptName);
|
||||
$filename = 'Concepto_' . $conceptName . '_IBIZA';
|
||||
} else {
|
||||
$filename = 'Conceptos_Especiales_IBIZA';
|
||||
}
|
||||
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '.csv"');
|
||||
fputs(fopen('php://output', 'w'), $bom = (chr(0xEF) . chr(0xBB) . chr(0xBF)));
|
||||
|
||||
$output = fopen('php://output', 'w');
|
||||
|
||||
foreach ($conceptsToExport as $conceptData) {
|
||||
$concept = $conceptData['concept'];
|
||||
$status = $conceptData['status'];
|
||||
$payments = $conceptData['payments'];
|
||||
|
||||
fputcsv($output, ['Condominio IBIZA-Cto Sierra Morena 152 - Concepto: ' . htmlspecialchars($concept['name'])]);
|
||||
fputcsv($output, ['Fecha: ' . date('d/m/Y', strtotime($concept['concept_date']))]);
|
||||
if (!empty($concept['description'])) {
|
||||
fputcsv($output, ['Descripción: ' . htmlspecialchars($concept['description'])]);
|
||||
}
|
||||
fputcsv($output, ['']); // Blank line
|
||||
fputcsv($output, ['Monto por Casa:', $concept['amount_per_house']]);
|
||||
fputcsv($output, ['Total Recaudado:', $status['total_collected']]);
|
||||
fputcsv($output, ['Total Esperado:', $status['total_expected']]);
|
||||
fputcsv($output, ['Balance:', $status['total_collected']]);
|
||||
fputcsv($output, ['']); // Blank line
|
||||
fputcsv($output, ['Casa', 'Propietario', 'Monto Pagado', 'Fecha de Pago']);
|
||||
|
||||
foreach ($payments as $payment) {
|
||||
fputcsv($output, [
|
||||
$payment['house_number'],
|
||||
$payment['owner_name'] ?? '-',
|
||||
$payment['amount'] > 0 ? $payment['amount'] : 0,
|
||||
$payment['payment_date'] ? date('d/m/Y', strtotime($payment['payment_date'])) : '-'
|
||||
]);
|
||||
}
|
||||
|
||||
fputcsv($output, ['', '', 'Total:', $status['total_collected']]);
|
||||
fputcsv($output, ['']); // Blank line para separar conceptos
|
||||
}
|
||||
|
||||
fclose($output);
|
||||
exit;
|
||||
|
||||
default:
|
||||
// Si no es una acción de exportación conocida, continuar al default
|
||||
break;
|
||||
|
||||
@@ -185,7 +185,7 @@ class CollectionPayment {
|
||||
|
||||
public static function initializePayments($conceptId, $userId) {
|
||||
require_once __DIR__ . '/House.php'; // Incluir House model
|
||||
$houses = House::allActive(); // Obtener solo casas activas
|
||||
$houses = House::getActive(); // Obtener solo casas activas
|
||||
|
||||
$db = Database::getInstance();
|
||||
$db->beginTransaction();
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
<a href="/dashboard.php?page=reportes&type=concept-debtors" class="btn btn-outline-warning <?= ($_GET['type'] ?? '') == 'concept-debtors' ? 'active' : '' ?>">
|
||||
<i class="bi bi-cash-coin"></i> Deudores de Conceptos
|
||||
</a>
|
||||
<a href="/dashboard.php?page=reportes&type=concepts" class="btn btn-outline-info <?= ($_GET['type'] ?? '') == 'concepts' ? 'active' : '' ?>">
|
||||
<i class="bi bi-collection"></i> Conceptos Especiales
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -307,6 +310,216 @@ function exportConceptDebtorsPDF() {
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php elseif ($reportType == 'concepts'): ?>
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex align-items-center">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="bi bi-funnel"></i> Filtros de Conceptos Especiales
|
||||
</h5>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary ms-3" data-bs-toggle="collapse" data-bs-target="#conceptsFilterCollapse">
|
||||
<i class="bi bi-chevron-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button onclick="exportConceptsPDF()" class="btn btn-outline-primary btn-sm">
|
||||
<i class="bi bi-file-earmark-pdf"></i> PDF
|
||||
</button>
|
||||
<?php if (Auth::isAdmin()): ?>
|
||||
<button onclick="exportConceptsCSV()" class="btn btn-outline-success btn-sm">
|
||||
<i class="bi bi-file-earmark-csv"></i> CSV
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse show" id="conceptsFilterCollapse">
|
||||
<div class="card-body">
|
||||
<form id="conceptsFilter">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-12">
|
||||
<label class="form-label">Concepto</label>
|
||||
<select name="filter_concept" class="form-select">
|
||||
<option value="">Todos los conceptos</option>
|
||||
<?php
|
||||
require_once __DIR__ . '/../../models/CollectionConcept.php';
|
||||
$allConcepts = CollectionConcept::all(true);
|
||||
foreach ($allConcepts as $c): ?>
|
||||
<option value="<?= $c['id'] ?>" <?= ($_GET['filter_concept'] ?? '') == $c['id'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($c['name']) ?> - <?= date('d/m/Y', strtotime($c['concept_date'])) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-search"></i> Aplicar Filtros
|
||||
</button>
|
||||
<a href="/dashboard.php?page=reportes&type=concepts" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-x-circle"></i> Limpiar Filtros
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$filterConceptId = $_GET['filter_concept'] ?? null;
|
||||
$conceptsToDisplay = [];
|
||||
|
||||
if ($filterConceptId) {
|
||||
$concept = CollectionConcept::findById($filterConceptId);
|
||||
if ($concept) {
|
||||
$status = CollectionConcept::getCollectionStatus($filterConceptId);
|
||||
$payments = CollectionConcept::getPaymentsByConcept($filterConceptId);
|
||||
$conceptsToDisplay[] = [
|
||||
'concept' => $concept,
|
||||
'status' => $status,
|
||||
'payments' => $payments
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$allConcepts = CollectionConcept::all(true);
|
||||
foreach ($allConcepts as $c) {
|
||||
$status = CollectionConcept::getCollectionStatus($c['id']);
|
||||
$payments = CollectionConcept::getPaymentsByConcept($c['id']);
|
||||
$conceptsToDisplay[] = [
|
||||
'concept' => $c,
|
||||
'status' => $status,
|
||||
'payments' => $payments
|
||||
];
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php foreach ($conceptsToDisplay as $conceptData): ?>
|
||||
<?php $concept = $conceptData['concept']; $status = $conceptData['status']; $payments = $conceptData['payments']; ?>
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="bi bi-collection"></i> <?= htmlspecialchars($concept['name']) ?>
|
||||
<small class="text-muted">(<?= date('d/m/Y', strtotime($concept['concept_date'])) ?>)</small>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card border-primary">
|
||||
<div class="card-body text-center">
|
||||
<h6 class="text-muted">Monto por Casa</h6>
|
||||
<h5 class="text-primary">$<?= number_format($concept['amount_per_house'], 2) ?></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card border-success">
|
||||
<div class="card-body text-center">
|
||||
<h6 class="text-muted">Recaudado</h6>
|
||||
<h5 class="text-success">$<?= number_format($status['total_collected'], 2) ?></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card border-info">
|
||||
<div class="card-body text-center">
|
||||
<h6 class="text-muted">Esperado</h6>
|
||||
<h5 class="text-info">$<?= number_format($status['total_expected'], 2) ?></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card <?= $status['balance'] >= 0 ? 'border-success' : 'border-danger' ?>">
|
||||
<div class="card-body text-center">
|
||||
<h6 class="text-muted">Balance</h6>
|
||||
<h5 class="<?= $status['balance'] >= 0 ? 'text-success' : 'text-danger' ?>">
|
||||
$<?= number_format($status['total_collected'], 2) ?>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="mb-3">Pagos por Casa</h6>
|
||||
<?php if (empty($payments)): ?>
|
||||
<div class="alert alert-info text-center">
|
||||
No hay pagos registrados para este concepto.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead class="table-primary">
|
||||
<tr>
|
||||
<th>Casa</th>
|
||||
<th>Propietario</th>
|
||||
<th>Monto Pagado</th>
|
||||
<th>Fecha de Pago</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($payments as $payment):
|
||||
$rowClass = $payment['amount'] > 0 ? 'table-success' : 'table-light';
|
||||
?>
|
||||
<tr class="<?= $rowClass ?>">
|
||||
<td><strong><?= $payment['house_number'] ?></strong></td>
|
||||
<td><?= htmlspecialchars($payment['owner_name'] ?? '-') ?></td>
|
||||
<td class="text-end">
|
||||
<?= $payment['amount'] > 0 ? '$' . number_format($payment['amount'], 2) : '-' ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= $payment['payment_date'] ? date('d/m/Y', strtotime($payment['payment_date'])) : '-' ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot class="table-dark">
|
||||
<tr>
|
||||
<th colspan="2" class="text-end">Total Recaudado:</th>
|
||||
<th class="text-end">$<?= number_format($status['total_collected'], 2) ?></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<script>
|
||||
document.getElementById('conceptsFilter').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(this);
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (formData.get('filter_concept')) {
|
||||
params.append('filter_concept', formData.get('filter_concept'));
|
||||
}
|
||||
|
||||
window.location.href = '/dashboard.php?page=reportes&type=concepts&' + params.toString();
|
||||
});
|
||||
|
||||
function exportConceptsPDF() {
|
||||
const conceptId = '<?= $filterConceptId ?? '' ?>';
|
||||
let url = '/dashboard.php?page=reportes_actions&action=export_pdf_report&type=concepts';
|
||||
if (conceptId) {
|
||||
url += '&filter_concept=' + conceptId;
|
||||
}
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
function exportConceptsCSV() {
|
||||
const conceptId = '<?= $filterConceptId ?? '' ?>';
|
||||
let url = '/dashboard.php?page=reportes_actions&action=export_csv_concepts';
|
||||
if (conceptId) {
|
||||
url += '&filter_concept=' + conceptId;
|
||||
}
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div class="row g-4 mb-4">
|
||||
|
||||
82
views/reports/pdf_concepts.php
Normal file
82
views/reports/pdf_concepts.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<style>
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 9pt;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #000;
|
||||
padding: 4px;
|
||||
}
|
||||
th {
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
||||
|
||||
<?php
|
||||
$conceptCount = count($conceptsToExport);
|
||||
foreach ($conceptsToExport as $index => $conceptData):
|
||||
$concept = $conceptData['concept'];
|
||||
$status = $conceptData['status'];
|
||||
$payments = $conceptData['payments'];
|
||||
?>
|
||||
<h2><?= htmlspecialchars($concept['name']) ?></h2>
|
||||
<p><strong>Fecha:</strong> <?= date('d/m/Y', strtotime($concept['concept_date'])) ?></p>
|
||||
<?php if (!empty($concept['description'])): ?>
|
||||
<p><strong>Descripción:</strong> <?= htmlspecialchars($concept['description']) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<table border="1" cellpadding="5">
|
||||
<tr>
|
||||
<td style="background-color: #e3f2fd;"><strong>Monto por Casa:</strong></td>
|
||||
<td style="background-color: #e3f2fd; text-align: right; font-weight: bold;">$<?= number_format($concept['amount_per_house'], 2) ?></td>
|
||||
<td style="background-color: #d4edda;"><strong>Recaudado:</strong></td>
|
||||
<td style="background-color: #d4edda; text-align: right; font-weight: bold;">$<?= number_format($status['total_collected'], 2) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="background-color: #e3f2fd;"><strong>Esperado:</strong></td>
|
||||
<td style="background-color: #e3f2fd; text-align: right; font-weight: bold;">$<?= number_format($status['total_expected'], 2) ?></td>
|
||||
<td style="background-color: #d4edda;"><strong>Balance:</strong></td>
|
||||
<td style="background-color: #d4edda; text-align: right; font-weight: bold;">$<?= number_format($status['total_collected'], 2) ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3>Pagos por Casa</h3>
|
||||
<table border="1" cellpadding="5">
|
||||
<thead style="background-color: #0d6efd; color: white;">
|
||||
<tr>
|
||||
<th>Casa</th>
|
||||
<th>Propietario</th>
|
||||
<th style="text-align: right;">Monto Pagado</th>
|
||||
<th>Fecha de Pago</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($payments as $payment):
|
||||
$bgColor = $payment['amount'] > 0 ? '#d4edda' : '#f8f9fa';
|
||||
?>
|
||||
<tr style="background-color: <?= $bgColor ?>;">
|
||||
<td><strong><?= $payment['house_number'] ?></strong></td>
|
||||
<td><?= htmlspecialchars($payment['owner_name'] ?? '-') ?></td>
|
||||
<td style="text-align: right;">
|
||||
<?= $payment['amount'] > 0 ? '$' . number_format($payment['amount'], 2) : '-' ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= $payment['payment_date'] ? date('d/m/Y', strtotime($payment['payment_date'])) : '-' ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot style="background-color: #343a40; color: white; font-weight: bold;">
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: right;">Total Recaudado:</td>
|
||||
<td style="text-align: right;">$<?= number_format($status['total_collected'], 2) ?></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<?php if ($index < $conceptCount - 1): ?>
|
||||
<div style="page-break-after: always;"></div>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
Reference in New Issue
Block a user