多维表格怎么做出纳日记账

在excel里要作个出纳日记账都很很简单,但使用上仍有很多缺点,数据多了公式很卡,不方便随地编辑等等,而在多维表里就方便多了,但在操作数据上仍有很多地方不如excel灵活:

对于我这样普能的职场人,在此之前想去通过脚本来实现根本不可能的事,在官方的开放平台上翻了很多脚本,都是碎片的凑到一起,根本实现不了。

最近看到李伟坚老师的SKILL,于是偿试着用脚本做,还真实现了,分享这个不是表格本身,而是通过与李伟坚老师的SKILL约束ai来写脚本真的很容易,这个记账表主要有几点我认为是可以值得分享的:

  1. 当首行的期初余额填了金额,那么此行的借贷字段都不能再填金额,如果“期初余额”字段没有金额,那么借或贷方就可以正常输入金额

  1. “期初余额”这字段只有首行才可以输金额,往下的这个字段里就不能输入金额。

  1. 同一行的借贷如果有一方有金额,另一方就不能有金额

  1. 如果余额不小心被手动改了,能立马重新更新余额

/**
 * 财务记账 - 借方更新时执行(全表稳健版 + 期初余额支持 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();
浏览 148
收藏
8
分享
8 +1
+1
全部评论