Phase G: Add Budgeting and Year-End Close
Budgeting: - Budget + BudgetLine entities with Jan–Dec monthly columns per GL account - BudgetsController: Index, Create, Edit, SetDefault, Copy, Delete - Copy action rolls a budget forward to a new fiscal year - Budget vs. Actual report (BudgetVsActual): compares monthly budget amounts to real P&L by calling GetProfitAndLossAsync once per month; variance shown as favorable/unfavorable; year + budget selectors in header - Views: Budgets/Index, Create, Edit with inline annual totals via budget-edit.js - Nav link + report card on Landing Year-End Close: - YearEndClose entity records each closed year + JE reference for audit trail - AccountsController.YearEndClose GET (history + form) + CloseYear POST - Close zeroes all Revenue and Expense/COGS account balances into Retained Earnings via IAccountBalanceService and posts a supporting JE dated Dec 31 - Idempotency: rejects attempt to close an already-closed year - Pre-close checklist in view to guide the workflow - Nav link under Finance Migration AddBudgetsAndYearEndClose applied Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Update row annual totals as cells change
|
||||
function updateRowTotal(rowIdx) {
|
||||
var cells = document.querySelectorAll('.budget-cell[data-row="' + rowIdx + '"]');
|
||||
var sum = 0;
|
||||
cells.forEach(function (c) { sum += parseFloat(c.value) || 0; });
|
||||
var span = document.querySelector('.annual-total[data-row="' + rowIdx + '"]');
|
||||
if (span) span.textContent = sum.toFixed(2);
|
||||
}
|
||||
|
||||
document.querySelectorAll('.budget-cell').forEach(function (input) {
|
||||
var row = input.getAttribute('data-row');
|
||||
updateRowTotal(row);
|
||||
input.addEventListener('input', function () { updateRowTotal(row); });
|
||||
});
|
||||
|
||||
// "Spread Annual Evenly" — prompts annual amount and distributes equally across 12 months
|
||||
var fillBtn = document.getElementById('fillEvenlyBtn');
|
||||
if (fillBtn) {
|
||||
fillBtn.addEventListener('click', function () {
|
||||
var annual = parseFloat(prompt('Enter the annual amount to spread evenly across all months for ALL rows (overwrites existing):'));
|
||||
if (isNaN(annual)) return;
|
||||
var monthly = (annual / 12).toFixed(2);
|
||||
var allRows = new Set();
|
||||
document.querySelectorAll('.budget-cell').forEach(function (c) {
|
||||
c.value = monthly;
|
||||
allRows.add(c.getAttribute('data-row'));
|
||||
});
|
||||
allRows.forEach(function (r) { updateRowTotal(r); });
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var copyModal = document.getElementById('copyModal');
|
||||
if (copyModal) {
|
||||
copyModal.addEventListener('show.bs.modal', function (e) {
|
||||
var btn = e.relatedTarget;
|
||||
document.getElementById('copyBudgetId').value = btn.getAttribute('data-budget-id');
|
||||
document.getElementById('copyBudgetName').textContent = btn.getAttribute('data-budget-name');
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user