+billing page

This commit is contained in:
Flatlogic Bot 2025-11-26 09:02:25 +00:00
parent e68686ffc2
commit 00cb8e93bb
6 changed files with 235 additions and 0 deletions

30
assets/js/billing.js Normal file
View File

@ -0,0 +1,30 @@
function initBillingPage(projectId) {
document.querySelectorAll('.billing-amount').forEach(input => {
input.addEventListener('blur', function() {
const month = this.dataset.month;
const amount = this.value;
const formData = new FormData();
formData.append('projectId', projectId);
formData.append('month', month);
formData.append('amount', amount);
fetch('save_billing.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Maybe show a small success indicator
} else {
alert('Error saving billing amount: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('An unexpected error occurred.');
});
});
});
}

114
billing.php Normal file
View File

@ -0,0 +1,114 @@
<?php
require_once __DIR__ . '/db/config.php';
$projectId = $_GET['id'] ?? null;
if (!$projectId) {
header("Location: projects.php");
exit();
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM projects WHERE id = :id");
$stmt->execute([':id' => $projectId]);
$project = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$project) {
header("Location: projects.php");
exit();
}
// Initialize billing records if they don't exist
$startDate = new DateTime($project['startDate']);
$endDate = new DateTime($project['endDate']);
$currentMonth = clone $startDate;
while ($currentMonth <= $endDate) {
$monthStr = $currentMonth->format('Y-m-01');
$stmt = $pdo->prepare("SELECT COUNT(*) FROM billingMonthly WHERE projectId = :projectId AND month = :month");
$stmt->execute([':projectId' => $projectId, ':month' => $monthStr]);
$count = $stmt->fetchColumn();
if ($count == 0) {
$insertStmt = $pdo->prepare("INSERT INTO billingMonthly (projectId, month, amount) VALUES (:projectId, :month, 0)");
$insertStmt->execute([':projectId' => $projectId, ':month' => $monthStr]);
}
$currentMonth->modify('+1 month');
}
// Fetch billing data
$stmt = $pdo->prepare("SELECT * FROM billingMonthly WHERE projectId = :projectId ORDER BY month");
$stmt->execute([':projectId' => $projectId]);
$billingData = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$db_error = "Database error: " . $e->getMessage();
}
?>
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Billing - <?php echo htmlspecialchars($project['name']); ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<div class="main-wrapper">
<main class="content-wrapper">
<div class="page-header">
<h1 class="h2">Billing - <?php echo htmlspecialchars($project['name']); ?></h1>
<div class="header-actions">
<a href="project_details.php?id=<?php echo $projectId; ?>" class="btn btn-secondary">Return</a>
<a href="export_billing.php?id=<?php echo $projectId; ?>" class="btn btn-secondary">Export to Excel</a>
</div>
</div>
<?php if (isset($db_error)): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($db_error); ?></div>
<?php endif; ?>
<div class="card">
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th style="width: 150px;">Billing</th>
<?php
$currentMonth = clone $startDate;
while ($currentMonth <= $endDate) {
echo '<th>' . $currentMonth->format('M Y') . '</th>';
$currentMonth->modify('+1 month');
}
?>
</tr>
</thead>
<tbody>
<tr>
<td>Billing</td>
<?php
foreach ($billingData as $billingMonth) {
echo '<td><input type="number" class="form-control billing-amount" data-month="' . $billingMonth['month'] . '" value="' . htmlspecialchars($billingMonth['amount']) . '"></td>';
}
?>
</tr>
</tbody>
</table>
</div>
</div>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/billing.js?v=<?php echo time(); ?>"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const projectId = <?php echo json_encode($projectId); ?>;
initBillingPage(projectId);
});
</script>
</body>
</html>

View File

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS `billingMonthly` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`projectId` INT NOT NULL,
`month` DATE NOT NULL,
`amount` DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
FOREIGN KEY (`projectId`) REFERENCES `projects`(`id`) ON DELETE CASCADE,
UNIQUE KEY `project_month` (`projectId`, `month`)
);

46
export_billing.php Normal file
View File

@ -0,0 +1,46 @@
<?php
require_once __DIR__ . '/db/config.php';
$projectId = $_GET['id'] ?? null;
if (!$projectId) {
die("Project ID is required.");
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT name FROM projects WHERE id = :id");
$stmt->execute([':id' => $projectId]);
$project = $stmt->fetch(PDO::FETCH_ASSOC);
$projectName = $project ? $project['name'] : 'project';
$stmt = $pdo->prepare("SELECT month, amount FROM billingMonthly WHERE projectId = :projectId ORDER BY month");
$stmt->execute([':projectId' => $projectId]);
$billingData = $stmt->fetchAll(PDO::FETCH_ASSOC);
$filename = "billing_" . strtolower(str_replace(' ', '_', $projectName)) . ".csv";
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '"');
$output = fopen('php://output', 'w');
// Header row
$header = ['Billing'];
foreach ($billingData as $row) {
$header[] = date("M Y", strtotime($row['month']));
}
fputcsv($output, $header);
// Data row
$data = ['Billing'];
foreach ($billingData as $row) {
$data[] = $row['amount'];
}
fputcsv($output, $data);
fclose($output);
exit();
} catch (PDOException $e) {
die("Database error: " . $e->getMessage());
}

View File

@ -137,6 +137,7 @@ if (!$project) {
<div class="page-header">
<h1 class="h2">Project: <?php echo htmlspecialchars($project['name']); ?></h1>
<div class="header-actions">
<a href="billing.php?id=<?php echo $project['id']; ?>" class="btn btn-primary"><i class="bi bi-credit-card-fill me-2"></i>Billing</a>
<a href="forecasting.php?projectId=<?php echo $project['id']; ?>" class="btn btn-success"><i class="bi bi-bar-chart-line-fill me-2"></i>Forecasting</a>
</div>
</div>

36
save_billing.php Normal file
View File

@ -0,0 +1,36 @@
<?php
header('Content-Type: application/json');
require_once __DIR__ . '/db/config.php';
$response = ['success' => false, 'error' => 'Invalid request'];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$projectId = $_POST['projectId'] ?? null;
$month = $_POST['month'] ?? null;
$amount = $_POST['amount'] ?? null;
if ($projectId && $month && $amount !== null) {
try {
$pdo = db();
$stmt = $pdo->prepare("UPDATE billingMonthly SET amount = :amount WHERE projectId = :projectId AND month = :month");
$stmt->execute([
':amount' => $amount,
':projectId' => $projectId,
':month' => $month
]);
if ($stmt->rowCount() > 0) {
$response['success'] = true;
unset($response['error']);
} else {
$response['error'] = 'No record found to update.';
}
} catch (PDOException $e) {
$response['error'] = 'Database error: ' . $e->getMessage();
}
} else {
$response['error'] = 'Missing required fields.';
}
}
echo json_encode($response);