Compare commits

..

2 Commits

Author SHA1 Message Date
mac
56b8bfbf84 fix: 删除 finance.js/plan.js 明细行中残留的 exp 变量引用
- plan.js 第403行 var eCost = parseFloat(exp.cost...) 残留引用已删
- finance.js 第385行 var eCost = parseFloat(exp.cost...) 残留引用已删
- exp 变量已在上一版聚合改造中移除,残留引用导致 ReferenceError
2026-07-17 14:40:29 +08:00
mac
d3a7252e26 fix: 同月多条费用被 Array.find 丢弃导致数据不完整
- plan.js/finance.js 所有 ed.find 改为 filter+forEach 聚合同月 cost/paid
- computePlanMonthly 同步修复 bd.find 和 ed.find
- 明细行(expand panel) 的双重 find 也全部聚合
- 月度视图的 else 分支同改
2026-07-17 14:03:21 +08:00
2 changed files with 53 additions and 23 deletions

View File

@@ -367,15 +367,21 @@ function renderFinance() {
expenseArr.forEach(function(e) { allMonths[e.month || ''] = true; });
var detailRows = Object.keys(allMonths).filter(function(m) {
var b = budgetArr.find(function(x) { return x.month === m; }) || {};
var exp = expenseArr.find(function(x) { return x.month === m; }) || {};
var bRev = parseFloat(b.rev || 0), bGross = parseFloat(b.gross || 0), bPay = parseFloat(b.payment || 0);
var eCost = parseFloat(exp.cost || 0), ePaid = parseFloat(exp.paid || 0);
var eCost = 0, ePaid = 0;
expenseArr.filter(function(x) { return x.month === m; }).forEach(function(e) {
eCost += parseFloat(e.cost || 0) || 0;
ePaid += parseFloat(e.paid || 0) || 0;
});
return bRev || bGross || bPay || eCost || ePaid;
}).sort().map(function(m) {
var b = budgetArr.find(function(x) { return x.month === m; }) || {};
var exp = expenseArr.find(function(x) { return x.month === m; }) || {};
var eCost = 0, ePaid = 0;
expenseArr.filter(function(x) { return x.month === m; }).forEach(function(e) {
eCost += parseFloat(e.cost || 0) || 0;
ePaid += parseFloat(e.paid || 0) || 0;
});
var bRev = parseFloat(b.rev || 0), bGross = parseFloat(b.gross || 0), bPay = parseFloat(b.payment || 0);
var eCost = parseFloat(exp.cost || 0), ePaid = parseFloat(exp.paid || 0);
var bCf = bPay - ePaid;
return '<tr class="bg-slate-50"><td class="px-2 py-px"></td><td class="px-2 py-px"></td><td class="px-2 py-px text-center text-xs text-slate-600">' + m + '</td><td class="px-2 py-px text-center text-xs text-brand-600">' + (bRev ? money(bRev) : '—') + '</td><td class="px-2 py-px text-center text-xs text-green-600">' + (bGross ? money(bGross) : '—') + '</td><td class="px-2 py-px"></td><td class="px-2 py-px text-center text-xs text-amber-600">' + (bPay ? money(bPay) : '—') + '</td><td class="px-2 py-px"></td><td class="px-2 py-px text-center text-xs text-rose-600">' + (eCost ? money(eCost) : '—') + '</td><td class="px-2 py-px text-center text-xs text-purple-600">' + (ePaid ? money(ePaid) : '—') + '</td><td class="px-2 py-px"></td><td class="px-2 py-px text-center text-xs ' + (bCf >= 0 ? 'text-green-600' : 'text-red-600') + '">' + (bCf ? money(bCf) : '—') + '</td><td class="px-2 py-px text-center text-xs ' + (bRev - bPay > 0 ? 'text-red-600' : bRev - bPay < 0 ? 'text-green-600' : 'text-slate-300') + '">' + (bRev - bPay ? money(bRev - bPay) : '—') + '</td></tr>';
}).join('');
@@ -405,12 +411,16 @@ function renderFinance() {
rev = Math.round(rev); payment = Math.round(payment); gross = Math.round(gross); cost = Math.round(cost); paid = Math.round(paid);
} else {
const b = bd.find(x => (x.month || "") === selMonth) || {};
const e = ed.find(x => (x.month || "") === selMonth) || {};
let cost2 = 0, paid2 = 0;
ed.filter(x => (x.month || "") === selMonth).forEach(e => {
cost2 += parseFloat(e.cost || 0) || 0;
paid2 += parseFloat(e.paid || 0) || 0;
});
rev = Math.round(parseFloat(b.rev || 0) || 0);
payment = Math.round(parseFloat(b.payment || 0) || 0);
gross = Math.round(parseFloat(b.gross || 0) || 0);
cost = Math.round(parseFloat(e.cost || 0) || 0);
paid = Math.round(parseFloat(e.paid || 0) || 0);
cost = Math.round(cost2);
paid = Math.round(paid2);
}
if (!rev && !payment && !cost && !paid && !gross) return;
dataItems.push({

View File

@@ -383,16 +383,23 @@ function renderPlan() {
budgetArr.forEach(function(b) { allMonths[b.month || ''] = true; });
expenseArr.forEach(function(e) { allMonths[e.month || ''] = true; });
var detailRows = Object.keys(allMonths).filter(function(m) {
if (state.planYear !== 0 && parseInt(m.substring(0, 4)) !== state.planYear) return false;
var b = budgetArr.find(function(x) { return x.month === m; }) || {};
var exp = expenseArr.find(function(x) { return x.month === m; }) || {};
var bRev = parseFloat(b.rev || 0), bGross = parseFloat(b.gross || 0), bPay = parseFloat(b.payment || 0);
var eCost = parseFloat(exp.cost || 0), ePaid = parseFloat(exp.paid || 0);
var eCost = 0, ePaid = 0;
expenseArr.filter(function(x) { return x.month === m; }).forEach(function(e) {
eCost += parseFloat(e.cost || 0) || 0;
ePaid += parseFloat(e.paid || 0) || 0;
});
return bRev || bGross || bPay || eCost || ePaid;
}).sort().map(function(m) {
var b = budgetArr.find(function(x) { return x.month === m; }) || {};
var exp = expenseArr.find(function(x) { return x.month === m; }) || {};
var eCost = 0, ePaid = 0;
expenseArr.filter(function(x) { return x.month === m; }).forEach(function(e) {
eCost += parseFloat(e.cost || 0) || 0;
ePaid += parseFloat(e.paid || 0) || 0;
});
var bRev = parseFloat(b.rev || 0), bGross = parseFloat(b.gross || 0), bPay = parseFloat(b.payment || 0);
var eCost = parseFloat(exp.cost || 0), ePaid = parseFloat(exp.paid || 0);
var bCf = bPay - ePaid;
return '<tr class="bg-slate-50"><td class="px-2 py-px"></td><td class="px-2 py-px text-center text-xs text-slate-600">' + m + '</td><td class="px-2 py-px text-center text-xs text-brand-600">' + (bRev ? money(bRev) : '—') + '</td><td class="px-2 py-px text-center text-xs text-green-600">' + (bGross ? money(bGross) : '—') + '</td><td class="px-2 py-px"></td><td class="px-2 py-px text-center text-xs text-amber-600">' + (bPay ? money(bPay) : '—') + '</td><td class="px-2 py-px"></td><td class="px-2 py-px text-center text-xs text-rose-600">' + (eCost ? money(eCost) : '—') + '</td><td class="px-2 py-px text-center text-xs text-purple-600">' + (ePaid ? money(ePaid) : '—') + '</td><td class="px-2 py-px"></td><td class="px-2 py-px text-center text-xs ' + (bCf >= 0 ? 'text-green-600' : 'text-red-600') + '">' + (bCf ? money(bCf) : '—') + '</td><td class="px-2 py-px text-center text-xs ' + (bRev - bPay > 0 ? 'text-red-600' : bRev - bPay < 0 ? 'text-green-600' : 'text-slate-300') + '">' + (bRev - bPay ? money(bRev - bPay) : '—') + '</td></tr>';
}).join('');
@@ -417,12 +424,17 @@ function renderPlan() {
let bd = []; try { bd = JSON.parse(pf.budget_data || "[]"); } catch (e) {}
let ed = []; try { ed = JSON.parse(pf.expense_data || "[]"); } catch (e) {}
const b = bd.find(x => (x.month || "") === selMonth) || {};
const e = ed.find(x => (x.month || "") === selMonth) || {};
const rev = Math.round(parseFloat(b.rev || 0) || 0);
const payment = Math.round(parseFloat(b.payment || 0) || 0);
const gross = Math.round(parseFloat(b.gross || 0) || 0);
const cost = Math.round(parseFloat(e.cost || 0) || 0);
const paid = Math.round(parseFloat(e.paid || 0) || 0);
let rev = Math.round(parseFloat(b.rev || 0) || 0);
let payment2 = Math.round(parseFloat(b.payment || 0) || 0);
let gross = Math.round(parseFloat(b.gross || 0) || 0);
let cost = 0, paid = 0;
ed.filter(x => (x.month || "") === selMonth).forEach(e => {
cost += parseFloat(e.cost || 0) || 0;
paid += parseFloat(e.paid || 0) || 0;
});
cost = Math.round(cost);
paid = Math.round(paid);
payment = payment2;
if (!rev && !payment && !cost && !paid && !gross) return;
dataItems.push({
pf: pf,
@@ -511,9 +523,15 @@ function renderPlan() {
let budget = []; try { budget = JSON.parse(pf.budget_data || "[]"); } catch (e) {}
let expense = []; try { expense = JSON.parse(pf.expense_data || "[]"); } catch (e) {}
let rev = 0, payment = 0, gross = 0;
budget.forEach(b => { rev += parseFloat(b.rev || 0) || 0; gross += parseFloat(b.gross || 0) || 0; payment += parseFloat(b.payment || 0) || 0; });
budget.forEach(b => {
if (state.planYear !== 0 && parseInt((b.month || '').substring(0, 4)) !== state.planYear) return;
rev += parseFloat(b.rev || 0) || 0; gross += parseFloat(b.gross || 0) || 0; payment += parseFloat(b.payment || 0) || 0;
});
let cost = 0, paid = 0;
expense.forEach(e => { cost += parseFloat(e.cost || 0) || 0; paid += parseFloat(e.paid || 0) || 0; });
expense.forEach(e => {
if (state.planYear !== 0 && parseInt((e.month || '').substring(0, 4)) !== state.planYear) return;
cost += parseFloat(e.cost || 0) || 0; paid += parseFloat(e.paid || 0) || 0;
});
return { rev: Math.round(rev), payment: Math.round(payment), cost: Math.round(cost), paid: Math.round(paid), gross: Math.round(gross) };
};
const allPfs = pfs;
@@ -1049,11 +1067,13 @@ function computePlanMonthly(planFinances, year) {
var pf = pfs[j];
if ((pf.sign_month || '') === month) sign += parseFloat(pf.sign_amount || 0);
var bd = []; try { bd = JSON.parse(pf.budget_data || '[]'); } catch(e) {}
var b = bd.find(function(x) { return (x.month || '') === month; });
if (b) { revenue += parseFloat(b.rev || 0); gross += parseFloat(b.gross || 0); payment += parseFloat(b.payment || 0); }
bd.filter(function(x) { return (x.month || '') === month; }).forEach(function(b) {
revenue += parseFloat(b.rev || 0); gross += parseFloat(b.gross || 0); payment += parseFloat(b.payment || 0);
});
var ed = []; try { ed = JSON.parse(pf.expense_data || '[]'); } catch(e) {}
var e = ed.find(function(x) { return (x.month || '') === month; });
if (e) { cost += parseFloat(e.cost || 0); paid += parseFloat(e.paid || 0); }
ed.filter(function(x) { return (x.month || '') === month; }).forEach(function(e) {
cost += parseFloat(e.cost || 0); paid += parseFloat(e.paid || 0);
});
}
data.push({ month: month, sign: Math.round(sign), revenue: Math.round(revenue), gross: Math.round(gross), payment: Math.round(payment), cost: Math.round(cost), paid: Math.round(paid) });
}