多维表格怎么做出纳日记账
在excel里要作个出纳日记账都很很简单,但使用上仍有很多缺点,数据多了公式很卡,不方便随地编辑等等,而在多维表里就方便多了,但在操作数据上仍有很多地方不如excel灵活:
对于我这样普能的职场人,在此之前想去通过脚本来实现根本不可能的事,在官方的开放平台上翻了很多脚本,都是碎片的凑到一起,根本实现不了。
最近看到李伟坚老师的SKILL,于是偿试着用脚本做,还真实现了,分享这个不是表格本身,而是通过与李伟坚老师的SKILL约束ai来写脚本真的很容易,这个记账表主要有几点我认为是可以值得分享的:
当首行的期初余额填了金额,那么此行的借贷字段都不能再填金额,如果“期初余额”字段没有金额,那么借或贷方就可以正常输入金额
“期初余额”这字段只有首行才可以输金额,往下的这个字段里就不能输入金额。
同一行的借贷如果有一方有金额,另一方就不能有金额
如果余额不小心被手动改了,能立马重新更新余额
/**
* 财务记账 - 借方更新时执行(全表稳健版 + 期初余额支持 v3)
*
* 核心规则:
* 1. 首行:期初余额、借方、贷方 三者有且仅有一个可以非零。
* 如果期初余额不为0(正或负),借方和贷方自动清0,余额 = 期初余额。
* 如果期初余额为0,余额 = 借方 - 贷方(借方贷方互斥)。
* 2. 非首行:期初余额强制为0,借方和贷方互斥,余额累加上条余额。
* 3. 全面异常保护 + 零失败设计。
*
* 触发条件:自动化流程"当借方被更新时"
*/
var CONFIG = {
sheetName: "财务记账",
fieldOpening: "期初余额",
fieldDebit: "借方",
fieldCredit: "贷方",
fieldBalance: "期末余额"
};
function ok(msg, data, count) {
return { ok: true, msg: msg || "执行成功", count: count || 0, data: data || null };
}
function fail(msg, data) {
return { ok: false, msg: msg || "执行失败", count: 0, data: data || null };
}
function ensureFieldName(fieldName) {
if (!fieldName) return "";
return fieldName.charAt(0) === "@" ? fieldName : "@" + fieldName;
}
function getSheet(sheetName) {
if (!sheetName) throw new Error("sheetName 不能为空");
return Application.Sheets(sheetName);
}
function parseNumber(value) {
if (value === null || value === undefined || value === "") return 0;
var n = Number(value);
return isNaN(n) ? 0 : n;
}
function main() {
try {
var data = Context.argv || {};
var sheet = getSheet(CONFIG.sheetName);
var rng = sheet.RecordRange;
var count = rng.Count;
if (!count || count <= 0) {
return ok("表中暂无记录");
}
var fOpening = CONFIG.fieldOpening ? ensureFieldName(CONFIG.fieldOpening) : "";
var fDebit = ensureFieldName(CONFIG.fieldDebit);
var fCredit = ensureFieldName(CONFIG.fieldCredit);
var fBalance = ensureFieldName(CONFIG.fieldBalance);
var records = [];
var clearedCount = 0;
for (var i = 1; i <= count; i++) {
try {
var row = rng.Item(i);
var isFirstRow = (i === 1);
var opening = 0;
var d = parseNumber(row.Item(1, fDebit).Value);
var c = parseNumber(row.Item(1, fCredit).Value);
if (fOpening) {
opening = parseNumber(row.Item(1, fOpening).Value);
}
if (isFirstRow && fOpening) {
if (opening !== 0) {
// v3 修正:期初余额不为0(含负数),借方和贷方必须清0
if (d !== 0) {
row.Item(1, fDebit).Value = 0;
d = 0;
clearedCount++;
}
if (c !== 0) {
row.Item(1, fCredit).Value = 0;
c = 0;
clearedCount++;
}
} else {
if (d > 0 && c > 0) {
row.Item(1, fCredit).Value = 0;
c = 0;
clearedCount++;
}
}
} else {
if (fOpening && opening !== 0) {
row.Item(1, fOpening).Value = 0;
opening = 0;
clearedCount++;
}
if (d > 0 && c > 0) {
row.Item(1, fCredit).Value = 0;
c = 0;
clearedCount++;
}
}
var currBal = parseNumber(row.Item(1, fBalance).Value);
records.push({
row: row,
isFirstRow: isFirstRow,
opening: opening,
debit: d,
credit: c,
currentBalance: currBal
});
} catch (rowErr) {
console.log("读取第 " + i + " 行异常: " + (rowErr.message || rowErr));
}
}
if (records.length <= 0) {
return ok("未读取到有效记录,但脚本执行正常");
}
var balance = 0;
var updated = 0;
for (var j = 0; j < records.length; j++) {
try {
var rec = records[j];
if (rec.isFirstRow) {
// v3 修正:期初余额不为0(含负数)时,余额 = 期初余额
if (rec.opening !== 0) {
balance = rec.opening;
} else {
balance = rec.debit - rec.credit;
}
} else {
balance = balance + rec.debit - rec.credit;
}
if (Math.abs(rec.currentBalance - balance) > 0.0001) {
rec.row.Item(1, fBalance).Value = balance;
updated++;
}
} catch (calcErr) {
console.log("计算第 " + (j + 1) + " 行余额异常: " + (calcErr.message || calcErr));
}
}
return ok(
"全表重算完成,清理/修正 " + clearedCount + " 个字段,更新 " + updated + " 条余额,最终余额:" + balance
);
} catch (e) {
console.log("脚本顶层异常: " + (e.message || e));
return ok("脚本遇到异常但已安全处理,建议检查表结构或手动点击全表重算按钮");
}
}
return main();