Web-page-generator / accounting.js
sosunakuflom3's picture
Upload 9 files
14d2101 verified
raw
history blame
8.56 kB
// =======================
// Load members into accounting dropdown
// =======================
async function loadMembersForAccounting() {
try {
const res = await fetch('/api/members');
if (!res.ok) throw new Error('Failed to fetch members');
const members = await res.json();
const select = document.getElementById('memberSelect');
select.innerHTML = '<option value="">Select Member</option>';
members.forEach(m => {
const opt = document.createElement('option');
opt.value = m.id;
opt.textContent = `${m.name} (#${m.id})`;
select.appendChild(opt);
});
} catch (error) {
console.error(error);
}
}
// =======================
// Show/hide payerName input based on payer type
// =======================
document.getElementById('payerType').addEventListener('change', (e) => {
const isNonMember = e.target.value === 'non-member';
document.getElementById('payerName').style.display = isNonMember ? 'inline-block' : 'none';
});
// =======================
// Load transactions and apply filters
// =======================
async function loadTransactions() {
try {
const res = await fetch('/api/accounting');
if (!res.ok) throw new Error('Failed to fetch transactions');
let transactions = await res.json();
// Apply filters (assumes filters object exists with start/end properties)
if (filters?.start || filters?.end) {
transactions = transactions.filter(tx => {
const txDate = new Date(tx.month + "-01");
const start = filters.start ? new Date(filters.start + "-01") : null;
const end = filters.end ? new Date(filters.end + "-01") : null;
return (!start || txDate >= start) && (!end || txDate <= end);
});
}
const tbody = document.getElementById('accountingBody');
tbody.innerHTML = transactions.map((t, i) => `
<tr class="${t.category === 'expense' ? 'expense' : 'income'}">
<td>${i + 1}</td>
<td>${t.payerType === 'member' ? t.memberId : t.payerName}</td>
<td>${t.payerType}</td>
<td>${t.month}</td>
<td>${t.amount}</td>
<td>${t.category}</td>
<td>${t.description || ''}</td>
<td>
<button class="btn btn-sm btn-primary" onclick="openActionModal(${t.id})">Actions</button>
</td>
</tr>
`).join('');
// Update summary and charts with the filtered transactions
updateAccountingSummary(transactions);
renderMonthlyChart(transactions);
renderIncomeExpenseChart(transactions);
} catch (error) {
console.error(error);
}
}
// =======================
// Update accounting summary table
// =======================
async function updateAccountingSummary(transactionsParam = null) {
try {
const transactions = transactionsParam || await (await fetch('/api/accounting')).json();
const summary = {
contribution: 0,
donation: 0,
sale: 0,
expense: 0,
memberIncome: 0,
nonMemberIncome: 0
};
transactions.forEach(tx => {
if (tx.category === 'contribution') summary.contribution += tx.amount;
else if (tx.category === 'donation') summary.donation += tx.amount;
else if (tx.category === 'sale') summary.sale += tx.amount;
else if (tx.category === 'expense') summary.expense += tx.amount;
if (tx.payerType === 'member' && tx.category !== 'expense') summary.memberIncome += tx.amount;
if (tx.payerType === 'non-member' && tx.category !== 'expense') summary.nonMemberIncome += tx.amount;
});
const tbody = document.getElementById('summaryBody');
tbody.innerHTML = `
<tr class="income-summary"><td>Contribution (Income)</td><td>${summary.contribution}</td></tr>
<tr class="income-summary"><td>Donation (Income)</td><td>${summary.donation}</td></tr>
<tr class="income-summary"><td>Sales (Income)</td><td>${summary.sale}</td></tr>
<tr class="income-summary"><td>Total Income from Members</td><td>${summary.memberIncome}</td></tr>
<tr class="income-summary"><td>Total Income from Non-Members</td><td>${summary.nonMemberIncome}</td></tr>
<tr class="expense-summary"><td>Total Expenses</td><td>${summary.expense}</td></tr>
<tr class="income-summary"><td>Net Total</td><td>${(summary.contribution + summary.donation + summary.sale) - summary.expense}</td></tr>
`;
} catch (error) {
console.error(error);
}
}
// =======================
// Handle transaction form submission
// =======================
document.getElementById('transactionForm').addEventListener('submit', async (e) => {
e.preventDefault();
const category = document.getElementById('transactionCategory').value;
const transaction = {
payerType: document.getElementById('payerType').value,
memberId: document.getElementById('memberSelect').value || null,
payerName: document.getElementById('payerName').value || null,
month: document.getElementById('transactionMonth').value,
amount: parseFloat(document.getElementById('transactionAmount').value),
category,
type: category === 'expense' ? 'expense' : 'income', // additional field for convenience
description: document.getElementById('transactionDesc').value.trim()
};
try {
const res = await fetch('/api/accounting', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(transaction)
});
if (!res.ok) throw new Error('Failed to add transaction');
document.getElementById('transactionForm').reset();
loadTransactions();
updateAccountingSummary();
renderMonthlyChart();
} catch (error) {
console.error(error);
alert('Failed to save transaction');
}
});
// =======================
// Export accounting data as JSON
// =======================
document.getElementById('exportAccounting').addEventListener('click', () => {
window.location.href = '/api/accounting/export';
});
// =======================
// Initial data load
// =======================
loadMembersForAccounting();
loadTransactions();
updateAccountingSummary();
let selectedTransaction = null;
// Open modal and load transaction data
window.openActionModal = async function (id) {
const res = await fetch('/api/accounting');
const transactions = await res.json();
selectedTransaction = transactions.find(t => t.id === id);
if (!selectedTransaction) return alert('Transaction not found');
// Fill the form
document.getElementById('editTransactionId').value = selectedTransaction.id;
document.getElementById('editTransactionMonth').value = selectedTransaction.month;
document.getElementById('editTransactionAmount').value = selectedTransaction.amount;
document.getElementById('editTransactionCategory').value = selectedTransaction.category;
document.getElementById('editTransactionDesc').value = selectedTransaction.description || '';
// Show modal
document.getElementById('transactionModal').classList.remove('hidden');
};
window.closeModal = function () {
document.getElementById('transactionModal').classList.add('hidden');
selectedTransaction = null;
};
// Save edited transaction
document.getElementById('editTransactionForm').addEventListener('submit', async function (e) {
e.preventDefault();
const id = document.getElementById('editTransactionId').value;
const updatedTx = {
...selectedTransaction,
month: document.getElementById('editTransactionMonth').value,
amount: parseFloat(document.getElementById('editTransactionAmount').value),
category: document.getElementById('editTransactionCategory').value,
description: document.getElementById('editTransactionDesc').value
};
const res = await fetch(`/api/accounting/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updatedTx)
});
if (!res.ok) {
alert('Failed to update transaction');
return;
}
closeModal();
loadTransactions();
});
// Delete transaction
window.deleteTransaction = async function () {
const id = document.getElementById('editTransactionId').value;
if (!confirm('Are you sure you want to delete this transaction?')) return;
const res = await fetch(`/api/accounting/${id}`, {
method: 'DELETE'
});
if (!res.ok) {
alert('Failed to delete transaction');
return;
}
closeModal();
loadTransactions();
};