在庫表の数字が合わないとき、現在庫のセルだけを直しても、なぜずれたのかは残りません。入庫、出庫、訂正の記録を別に持ち、そこから残数を計算する形にすると、差が出た理由を後から追いやすくなります。
例として、架空の商品Aの在庫を扱います。初期在庫が10個で、3個入庫し、4個出庫した場合の残数は9個です。以下は台帳の設計例で、実際の在庫や業務シートを更新してはいません。
現在庫と入出庫記録の分離
表は商品を管理する一覧と、増減を記録する履歴に分けます。商品一覧には商品ID、商品名、単位、初期在庫を置きます。履歴には処理ID、商品ID、増減数、発生日、担当者、理由を残します。
商品名だけを識別子にすると、名前の変更や同名商品に対応しにくくなります。履歴を結び付ける値は商品IDにし、表示名とは分けます。個、箱、セットのような単位も明記してください。1箱を1個として加算してよいかは、コードを書く前に決める内容です。
| 処理ID | 商品ID | 増減数 | 理由 |
|---|---|---|---|
| IN-001 | A001 | 3 | 仕入れ |
| OUT-001 | A001 | -4 | 出荷 |
同じ作業を再実行する場合にも、処理IDを同じ記録として見つけられるようにします。単に最終行へ追記するだけでは、二度実行したときに出庫を二重に数えるおそれがあります。
入力を受け付ける条件
個数で管理する例なら、数量は整数、商品IDは登録済み、処理IDは重複しない、という条件を決めます。空欄、文字、別単位の値が来たら、残数を更新する前に確認待ちへ戻します。
残数が負になる場合も、必ず入力ミスとは限りません。未入荷の引当を認める業務もあれば、実在庫を超える出庫を止める業務もあります。この例では負数を止めますが、実際の判断は在庫管理の担当者と決めてください。
計算だけを確かめる例
次の関数はシートへ接続せず、固定したデータから残数を計算します。GASで表を読み書きする処理を足す前に、数量と重複の扱いを確かめるための例です。
function calculateStock(opening, movements) {
if (!Number.isSafeInteger(opening) || opening < 0) {
throw new Error('初期在庫を確認してください');
}
const seen = new Set();
let stock = opening;
movements.forEach(function (item) {
if (!item.id || seen.has(item.id) ||
!Number.isSafeInteger(item.delta)) {
throw new Error('処理IDか数量を確認してください');
}
seen.add(item.id);
stock += item.delta;
if (!Number.isSafeInteger(stock) || stock < 0) {
throw new Error('残数を確認してください');
}
});
return stock;
}
正しい入力だけでなく、同じ処理IDが2件ある場合、出庫が残数を超える場合、数量が文字列の場合も確認します。計算が止まったときは、どの商品と処理IDで止まったかを別の確認表へ残すと、担当者が調べられます。
GASで読み書きする際の競合
GASからSheetsを扱う基本は、GoogleのSheets開発ガイドで確認できます。表を読む範囲、履歴を書き込む先、残数を更新する先を分け、どこまで保存できたかを記録します。
同じ在庫を複数のスクリプトが更新するなら、読み取ってから書くまでの間に別の処理が入ることも考えます。LockServiceは同時に動くコードの区間を制御するための機能です。共有シートの人による編集まで、在庫更新の取引としてまとめてくれる機能ではありません。
履歴の追記と現在庫の更新も、途中で片方だけ終わる場合にどう照合するかを決めます。処理ID、実行時刻、保存結果を残し、現在庫が合わないときは履歴から再計算できる形を用意します。
最初は商品1種類のコピーした表で、正常、重複、数量不足の3つを確認してください。通知や他部門への転記を足すのは、在庫の計算と保存の結果を説明できるようになってからです。
データの不備を見つける工程は取得データの欠損検知、GASの前提はGAS入門も参考になります。
