// ======================= // 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 = ''; 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) => ` ${i + 1} ${t.payerType === 'member' ? t.memberId : t.payerName} ${t.payerType} ${t.month} ${t.amount} ${t.category} ${t.description || ''} `).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 = ` Contribution (Income)${summary.contribution} Donation (Income)${summary.donation} Sales (Income)${summary.sale} Total Income from Members${summary.memberIncome} Total Income from Non-Members${summary.nonMemberIncome} Total Expenses${summary.expense} Net Total${(summary.contribution + summary.donation + summary.sale) - summary.expense} `; } 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(); };