mirror of
https://github.com/Solo-Web-Works/BillTrak.git
synced 2026-01-29 08:40:34 +00:00
✨feature: Update database schema to separate payees from payments
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
Temp/
|
||||||
node_modules/
|
node_modules/
|
||||||
*-bak*
|
*-bak*
|
||||||
bills.db
|
bills.db
|
||||||
|
|||||||
Binary file not shown.
@@ -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;
|
||||||
@@ -103,24 +104,50 @@ try {
|
|||||||
|
|
||||||
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':
|
||||||
|
try {
|
||||||
$stmt = DB::connect()->query("SELECT DISTINCT year FROM bills ORDER BY year DESC");
|
$stmt = DB::connect()->query("SELECT DISTINCT year FROM bills ORDER BY year DESC");
|
||||||
echo json_encode($stmt->fetchAll(PDO::FETCH_COLUMN));
|
$years = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
echo json_encode($years);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -3,14 +3,21 @@ 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,
|
||||||
|
comment TEXT,
|
||||||
year INTEGER NOT NULL,
|
year INTEGER NOT NULL,
|
||||||
comment TEXT
|
FOREIGN KEY (payeeId) REFERENCES payees(id) ON DELETE CASCADE
|
||||||
)");
|
)");
|
||||||
|
|
||||||
return $db;
|
return $db;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
16
js/app.js
16
js/app.js
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user