feature: Update database schema to separate payees from payments

This commit is contained in:
Keith Solomon
2025-02-09 12:02:00 -06:00
parent 2740050965
commit 62071c6645
6 changed files with 74 additions and 47 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
Temp/
node_modules/ node_modules/
*-bak* *-bak*
bills.db bills.db

Binary file not shown.

View File

@@ -18,15 +18,27 @@ try {
switch($action) { switch($action) {
case 'add': case 'add':
$data = [ $data = [
'date' => (new DateTime($_POST['date']))->format('Y-m-d'), // Normalize to YYYY-MM-DD 'date' => (new DateTime($_POST['date']))->format('Y-m-d'),
'billName' => $_POST['billName'], 'payeeId' => $_POST['payeeId'], // Payee ID instead of name
'amount' => (float)$_POST['amount'], 'amount' => (float)$_POST['amount'],
'paymentId' => $_POST['paymentId'], 'paymentId' => $_POST['paymentId'],
'year' => (int)explode('-', $_POST['date'])[0], 'comment' => $_POST['comment'] ?? '',
'comment' => $_POST['comment'] 'year' => (int)explode('-', $_POST['date'])[0]
]; ];
$result = Bill::add($data); $stmt = DB::connect()->prepare("
INSERT INTO bills (billDate, payeeId, amount, paymentId, comment, year)
VALUES (?, ?, ?, ?, ?, ?)
");
$result = $stmt->execute([
$data['date'],
$data['payeeId'],
$data['amount'],
$data['paymentId'],
$data['comment'],
$data['year']
]);
echo json_encode(['success' => $result]); echo json_encode(['success' => $result]);
break; break;
@@ -75,17 +87,6 @@ try {
} }
break; break;
case 'getPayees':
$stmt = DB::connect()->query("SELECT DISTINCT billName FROM bills ORDER BY billName ASC");
echo json_encode($stmt->fetchAll(PDO::FETCH_COLUMN));
break;
case 'addPayee':
$stmt = DB::connect()->prepare("INSERT INTO bills (billName) VALUES (?)");
$result = $stmt->execute([$_POST['billName']]);
echo json_encode(['success' => $result]);
break;
case 'getAll': case 'getAll':
echo json_encode(Bill::getAll()->fetchAll(PDO::FETCH_ASSOC)); echo json_encode(Bill::getAll()->fetchAll(PDO::FETCH_ASSOC));
break; break;
@@ -97,30 +98,56 @@ try {
echo json_encode($stmt->fetch(PDO::FETCH_ASSOC)); echo json_encode($stmt->fetch(PDO::FETCH_ASSOC));
break; break;
case 'getTotals': case 'getTotals':
echo json_encode(Bill::getYearlyTotals()->fetchAll(PDO::FETCH_ASSOC)); echo json_encode(Bill::getYearlyTotals()->fetchAll(PDO::FETCH_ASSOC));
break; break;
case 'getByYear': case 'getByYear':
$year = $_GET['year'] ?? date('Y'); $year = $_GET['year'] ?? date('Y');
$sort = $_GET['sort'] ?? 'date_asc'; // Default sorting $sort = $_GET['sort'] ?? 'date_desc';
$sortOptions = [ $sortOptions = [
'date_asc' => 'billDate ASC', 'date_asc' => 'bills.billDate ASC',
'date_desc' => 'billDate DESC', 'date_desc' => 'bills.billDate DESC',
'payee' => 'billName ASC, billDate ASC', 'payee' => 'payees.name ASC, bills.billDate ASC'
]; ];
$orderBy = $sortOptions[$sort] ?? $sortOptions['date_asc']; $orderBy = $sortOptions[$sort] ?? $sortOptions['date_asc'];
$stmt = DB::connect()->prepare("SELECT * FROM bills WHERE year = ? ORDER BY $orderBy"); $stmt = DB::connect()->prepare("
SELECT bills.*, payees.name AS payeeName
FROM bills
JOIN payees ON bills.payeeId = payees.id
WHERE bills.year = ?
ORDER BY $orderBy
");
$stmt->execute([$year]); $stmt->execute([$year]);
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC)); echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
break; break;
case 'addPayee':
$stmt = DB::connect()->prepare("INSERT INTO payees (name) VALUES (?)");
$result = $stmt->execute([$_POST['payeeName']]);
echo json_encode(['success' => $result]);
break;
case 'getPayees':
try {
$stmt = DB::connect()->query("SELECT id, name FROM payees ORDER BY name ASC");
$payees = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($payees);
} catch (Exception $e) {
echo json_encode(['error' => $e->getMessage()]);
}
break;
case 'getYears': case 'getYears':
$stmt = DB::connect()->query("SELECT DISTINCT year FROM bills ORDER BY year DESC"); try {
echo json_encode($stmt->fetchAll(PDO::FETCH_COLUMN)); $stmt = DB::connect()->query("SELECT DISTINCT year FROM bills ORDER BY year DESC");
$years = $stmt->fetchAll(PDO::FETCH_COLUMN);
echo json_encode($years);
} catch (Exception $e) {
echo json_encode(['error' => $e->getMessage()]);
}
break; break;
default: default:

View File

@@ -3,15 +3,22 @@ class DB {
public static function connect() { public static function connect() {
$db = new PDO('sqlite:'.__DIR__.'/../data/bills.db'); $db = new PDO('sqlite:'.__DIR__.'/../data/bills.db');
$db->exec("CREATE TABLE IF NOT EXISTS payees (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
createdAt TEXT DEFAULT CURRENT_TIMESTAMP
)");
$db->exec("CREATE TABLE IF NOT EXISTS bills ( $db->exec("CREATE TABLE IF NOT EXISTS bills (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
billDate TEXT NOT NULL, billDate TEXT NOT NULL,
billName TEXT NOT NULL, payeeId INTEGER NOT NULL,
amount REAL NOT NULL, amount REAL NOT NULL,
paymentId TEXT, paymentId TEXT,
year INTEGER NOT NULL, comment TEXT,
comment TEXT year INTEGER NOT NULL,
)"); FOREIGN KEY (payeeId) REFERENCES payees(id) ON DELETE CASCADE
)");
return $db; return $db;
} }

View File

@@ -41,7 +41,7 @@
<form id="addPayeeForm" class="border p-4 rounded shadow bg-gray-100"> <form id="addPayeeForm" class="border p-4 rounded shadow bg-gray-100">
<h2 class="text-2xl font-bold mb-4">Add New Payee</h2> <h2 class="text-2xl font-bold mb-4">Add New Payee</h2>
<input type="text" id="newPayeeName" name="billName" class="bg-white border rounded p-2 w-5/6" placeholder="New Payee Name"> <input type="text" id="newPayeeName" name="payeeName" class="bg-white border rounded p-2 w-5/6" placeholder="New Payee Name">
<button type="submit" class="mt-4 bg-blue-500 text-white px-4 py-2 rounded">Add Payee</button> <button type="submit" class="mt-4 bg-blue-500 text-white px-4 py-2 rounded">Add Payee</button>
</form> </form>

View File

@@ -31,21 +31,13 @@ document.addEventListener('DOMContentLoaded', () => {
const response = await fetch('/includes/api.php?action=getPayees'); const response = await fetch('/includes/api.php?action=getPayees');
const payees = await response.json(); const payees = await response.json();
const payeeSelect = document.getElementById('editFormBillName'); const payeeSelect = document.getElementById('billName');
payeeSelect.innerHTML = ''; // Clear existing options payeeSelect.innerHTML = ''; // Clear existing options
// Add default "Select Payee" option
const defaultOption = document.createElement('option');
defaultOption.value = '';
defaultOption.textContent = 'Select Payee';
defaultOption.disabled = true;
payeeSelect.appendChild(defaultOption);
// Populate payees
payees.forEach(payee => { payees.forEach(payee => {
const option = document.createElement('option'); const option = document.createElement('option');
option.value = payee; option.value = payee.id; // Use payee ID as the value
option.textContent = payee; option.textContent = payee.name; // Display payee name
payeeSelect.appendChild(option); payeeSelect.appendChild(option);
}); });
} }
@@ -77,7 +69,7 @@ document.addEventListener('DOMContentLoaded', () => {
billItem.innerHTML = ` billItem.innerHTML = `
<p><strong>Date:</strong> ${formattedDate}</p> <p><strong>Date:</strong> ${formattedDate}</p>
<p><strong>Bill Name:</strong> ${bill.billName}</p> <p><strong>Bill Name:</strong> ${bill.payeeName}</p>
<p><strong>Amount:</strong> $${bill.amount.toFixed(2)}</p> <p><strong>Amount:</strong> $${bill.amount.toFixed(2)}</p>
<p><strong>Payment ID:</strong> ${bill.paymentId || 'N/A'}</p> <p><strong>Payment ID:</strong> ${bill.paymentId || 'N/A'}</p>
<p><strong>Comment:</strong> ${bill.comment || 'N/A'}</p> <p><strong>Comment:</strong> ${bill.comment || 'N/A'}</p>