diff --git a/static/modules/plan.js b/static/modules/plan.js
index e162e66..8c5525e 100644
--- a/static/modules/plan.js
+++ b/static/modules/plan.js
@@ -1010,26 +1010,29 @@ function renderPlanOverview() {
annualSums.payment += fm[i].payment; annualSums.paid += fm[i].paid;
}
const mLbl = ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'];
- const LABELS = ['合同金额','确收金额','确收毛利','成本','已付'];
+ const LABELS = ['合同金额','确收金额','确收毛利','成本','已付','回款','项目现金流'];
// 每行的年度累计 + 12个月值
const annualVals = [
annualSums.sign, annualSums.revenue, annualSums.gross, annualSums.cost, annualSums.paid,
+ annualSums.payment, annualSums.payment - annualSums.paid,
];
- // 字段映射: sign, revenue, gross, cost, profit(=gross-cost), payment, paid, cashflow(=payment-paid)
- const fmFields = ['sign','revenue','gross','cost','paid'];
+ // 字段映射: sign, revenue, gross, cost, paid, payment, cashflow(=payment-paid)
+ const fmFields = ['sign','revenue','gross','cost','paid','payment',null];
var thead = '
| 指标 | 年度累计 | ';
for (var mi = 0; mi < 12; mi++) { thead += '' + mLbl[mi] + ' | '; }
thead += '
';
var tbody = '';
- for (var ri = 0; ri < 5; ri++) {
+ for (var ri = 0; ri < 7; ri++) {
var rowHtml = '| ' + LABELS[ri] + ' | ';
rowHtml += '' + moneyIntL(annualVals[ri]) + ' | ';
for (var mi = 0; mi < 12; mi++) {
var val = 0;
if (fm[mi]) {
- var fk = fmFields[ri]; if (fk) val = fm[mi][fk] || 0;
+ if (ri === 6) { val = (fm[mi].payment || 0) - (fm[mi].paid || 0); }
+ else { var fk = fmFields[ri]; if (fk) val = fm[mi][fk] || 0; }
}
var cls = 'text-slate-700';
+ if (ri === 6) cls = val >= 0 ? 'text-green-600' : 'text-red-600';
rowHtml += '' + moneyIntL(val) + ' | ';
}
tbody += rowHtml + '
';
diff --git a/static/modules/plan_expense.js b/static/modules/plan_expense.js
index f46a2f4..88b6312 100644
--- a/static/modules/plan_expense.js
+++ b/static/modules/plan_expense.js
@@ -184,36 +184,35 @@ window.savePlanExpense = async function(event) {
if (data.amount === '' || data.amount === undefined) data.amount = 0;
if (data.incurred_amount === '' || data.incurred_amount === undefined) data.incurred_amount = 0;
if (!data.status) data.status = '计入费用';
- var isEdit = !!data.expense_id;
+ var id = data.expense_id;
delete data.expense_id;
+ data.tenant = state.tenant;
- var id = form.querySelector('[name="expense_id"]').value;
- var url = isEdit ? "/api/planExpense/" + id : "/api/planExpense";
- var method = isEdit ? "PUT" : "POST";
-
- var resp = await fetch(url, { method: method, headers: { "Content-Type": "application/json" }, body: JSON.stringify({ data: Object.assign({}, data, { tenant: state.tenant }) }) });
- if (!resp.ok) { var err = await resp.text(); alert("保存失败:" + err); return; }
-
- var bResp = await fetch("/api/bootstrap?tenant=" + encodeURIComponent(state.tenant));
- if (bResp.ok) {
- var bd = await bResp.json();
- state.data = bd;
- applyUserTenants();
- renderPlanExpense();
+ try {
+ if (id) {
+ await api("/api/planExpense/" + id, { method: "PUT", body: JSON.stringify({ data }) });
+ } else {
+ await api("/api/planExpense", { method: "POST", body: JSON.stringify({ data }) });
+ }
+ } catch (error) {
+ toast("保存失败:" + error.message, "error");
+ return;
}
closePlanExpModal();
+ await load();
+ renderPlanExpense();
};
window.deletePlanExpItem = async function() {
var id = document.querySelector("#planExpenseModal form [name='expense_id']").value;
if (!id || !confirm("确定删除?")) return;
- var resp = await fetch("/api/planExpense/" + id, { method: "DELETE" });
- if (!resp.ok) { alert("删除失败"); return; }
- var bResp = await fetch("/api/bootstrap?tenant=" + encodeURIComponent(state.tenant));
- if (bResp.ok) {
- var bd = await bResp.json();
- state.data = bd;
- applyUserTenants();
- renderPlanExpense();
+ try {
+ await api("/api/planExpense/" + id, { method: "DELETE" });
+ } catch (error) {
+ toast("删除失败:" + error.message, "error");
+ return;
}
+ closePlanExpModal();
+ await load();
+ renderPlanExpense();
};
diff --git a/templates/index.html b/templates/index.html
index 88cd957..1cbbd3f 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -80,7 +80,7 @@
-
OPC Manager v1.0.0.32
+
OPC Manager v1.0.0.33