【GAS】Googleスプレッドシートで在庫管理システムを作成する

在庫管理

こんにちは、せこしょーです。
今日は在庫管理システムを作っていきます。
Google Apps Scriptを利用した在庫管理システムで前回のバージョンアップ版で自由に列を追加できるようになったのでより実用的に活用できます。

動画はこちら↓↓

入庫する.gs

/**
 * 入庫処理をする(入荷商品の入荷数を在庫管理表に加算する)。
 */
function nyuko(ss = SpreadsheetApp.getActiveSpreadsheet(), zaikoSheet = ss.getSheetByName('在庫管理表'), nyukaSheet = ss.getSheetByName('入荷データ'), nyukaRirekiSheet = ss.getSheetByName('入荷履歴')) {
  // 処理開始ダイアログ
  const question = Browser.msgBox("入庫します。在庫数が更新されます。", Browser.Buttons.OK_CANCEL);
  if (question == "cancel") {
    return;
  }

  const Z_ITEM_NAME = "商品名";
  const Z_QUANTITY = "数量";
  const N_ITEM_NAME = "商品名";
  const N_QUANTITY = "入荷数";
  const N_DATE = "入荷日";

  //「在庫管理表」のデータ取得
  const zaikoData = zaikoSheet.getDataRange().getValues();
  const zaikoHeads = zaikoData.shift();
  const zItemColIdx = zaikoHeads.indexOf(Z_ITEM_NAME);
  const zQuantityColIdx = zaikoHeads.indexOf(Z_QUANTITY);
  const zLastRowNumber = zaikoSheet.getRange(zaikoSheet.getMaxRows(), zItemColIdx + 1).getNextDataCell(SpreadsheetApp.Direction.UP).getRow();

  //「入荷データ」のデータ取得
  const nyukaData = nyukaSheet.getDataRange().getValues();
  const nyukaHeads = nyukaData.shift();
  const nItemColIdx = nyukaHeads.indexOf(N_ITEM_NAME);
  const nQuantityColIdx = nyukaHeads.indexOf(N_QUANTITY);
  const nDateColIdx = nyukaHeads.indexOf(N_DATE);
  const nLastColNumber = nyukaSheet.getRange(1, nyukaSheet.getMaxColumns()).getNextDataCell(SpreadsheetApp.Direction.PREVIOUS).getColumn();

  //「入荷履歴」の最終行取得
  const rLastRowNumber = nyukaRirekiSheet.getLastRow();


  // 入庫対象の入荷データを取得する
  let nyukoData = [];
  let nyukoRowNumbers = [];
  const today = new Date();
  for (let i = 0; i < nyukaData.length; i++) {
    const rowData = nyukaData[i].flat();
    // 入荷日が今日以前か判定
    if (rowData[nDateColIdx] != "" && rowData[nDateColIdx] <= today) {
      // 入荷数が0以上の数値か判定
      if (isFinite(rowData[nQuantityColIdx]) && rowData[nQuantityColIdx] >= 0) {
        // 入庫対象としてリストに追加する
        nyukoData.push(rowData);
        nyukoRowNumbers.push(i + 2);
      } else {
        continue;
      }
    }
  }
  if (nyukoData.length == 0) {
    Browser.msgBox("入庫対象はありません。");
    console.log("入庫対象はありません。");
    return;
  }


  // 入庫する(対象商品の入荷数を在庫管理表に加算する)
  let shinShohinData = [];
  const zaikoShohin = getColData(zaikoData,zItemColIdx).flat();
  for (let i = 0; i < nyukoData.length; i++) {
    if (zaikoShohin.indexOf(nyukoData[i][nItemColIdx]) != -1) {
      const idx = zaikoShohin.indexOf(nyukoData[i][nItemColIdx]);
      const zQuantityBefore = zaikoData[idx][zQuantityColIdx];
      zaikoData[idx][zQuantityColIdx] = zQuantityBefore + nyukoData[i][nQuantityColIdx];
      console.log(nyukoData[i][nItemColIdx] + "の在庫数:" + zaikoData[idx][zQuantityColIdx] + "(現在庫" + zQuantityBefore + " + 入庫数" + nyukoData[i][nQuantityColIdx] + ")");
    } else {
      console.log("新商品:" + nyukoData[i][nItemColIdx] + "の在庫数:" + nyukoData[i][nQuantityColIdx]);
      shinShohinData.push(nyukoData[i]);
    }
  }
  // 在庫管理表を更新する
  if (zaikoData.length != 0) {
    zaikoSheet.getRange(2, zItemColIdx + 1, zaikoData.length, 1).setValues(getColData(zaikoData, zItemColIdx));
    zaikoSheet.getRange(2, zQuantityColIdx + 1, zaikoData.length, 1).setValues(getColData(zaikoData, zQuantityColIdx));
  }
  // 新商品データがあれば在庫管理表に追加する
  if (shinShohinData.length != 0) {
    zaikoSheet.getRange(zLastRowNumber + 1, zItemColIdx + 1, shinShohinData.length, 1).setValues(getColData(shinShohinData, nItemColIdx));
    zaikoSheet.getRange(zLastRowNumber + 1, zQuantityColIdx + 1, shinShohinData.length, 1).setValues(getColData(shinShohinData, nQuantityColIdx));
  }

  // 入庫対象を削除する(削除前に入荷履歴に転記)
  nyukaRirekiSheet.getRange(rLastRowNumber + 1, 1, nyukoData.length, nLastColNumber).setValues(nyukoData);
  const reversedNyukoRowNumbers = nyukoRowNumbers.reverse();
  for (let i = 0; i < reversedNyukoRowNumbers.length; i++) {
    nyukaSheet.deleteRow(reversedNyukoRowNumbers[i]);
  }

  // 処理終了ダイアログ
  Browser.msgBox("入庫が完了しました。");
  console.log("入庫が完了しました。");
}

出庫する.gs

/**
 * 出庫処理をする(出荷商品の出荷数を在庫管理表から減算する)。
 */
function shukko(ss = SpreadsheetApp.getActiveSpreadsheet(), zaikoSheet = ss.getSheetByName('在庫管理表'), shukkaSheet = ss.getSheetByName('出荷データ'), shukkaRirekiSheet = ss.getSheetByName('出荷履歴')) {
  // 処理開始ダイアログ
  const question = Browser.msgBox("出庫します。在庫数が更新されます。", Browser.Buttons.OK_CANCEL);
  if (question == "cancel") {
    return;
  }

  const Z_ITEM_NAME = "商品名";
  const Z_QUANTITY = "数量";
  const S_ITEM_NAME = "商品名";
  const S_QUANTITY = "出荷数";
  const S_DATE = "出荷日";

  //「在庫管理表」のデータ取得
  const zaikoData = zaikoSheet.getDataRange().getValues();
  const zaikoHeads = zaikoData.shift();
  const zItemColIdx = zaikoHeads.indexOf(Z_ITEM_NAME);
  const zQuantityColIdx = zaikoHeads.indexOf(Z_QUANTITY);
  const zLastRowNumber = zaikoSheet.getRange(zaikoSheet.getMaxRows(), zItemColIdx + 1).getNextDataCell(SpreadsheetApp.Direction.UP).getRow();

  //「出荷データ」のデータ取得
  const shukkaData = shukkaSheet.getDataRange().getValues();
  const shukkaHeads = shukkaData.shift();
  const sItemColIdx = shukkaHeads.indexOf(S_ITEM_NAME);
  const sQuantityColIdx = shukkaHeads.indexOf(S_QUANTITY);
  const sDateColIdx = shukkaHeads.indexOf(S_DATE);
  const sLastColNumber = shukkaSheet.getRange(1, shukkaSheet.getMaxColumns()).getNextDataCell(SpreadsheetApp.Direction.PREVIOUS).getColumn();

  //「出荷履歴」の最終行取得
  const rLastRowNumber = shukkaRirekiSheet.getLastRow();


  // 出庫対象の出荷データを取得する
  let shukkoData = [];
  let shukkoRowNumbers = [];
  const today = new Date();
  for (let i = 0; i < shukkaData.length; i++) {
    const rowData = shukkaData[i].flat();
    // 出荷日が今日以前か判定
    if (rowData[sDateColIdx] != "" && rowData[sDateColIdx] <= today) {
      // 出荷数が0以上の数値か判定
      if (isFinite(rowData[sQuantityColIdx]) && rowData[sQuantityColIdx] >= 0) {
        // 出庫対象としてリストに追加する
        shukkoData.push(rowData);
        shukkoRowNumbers.push(i + 2);
      } else {
        continue;
      }
    }
  }
  if (shukkoData.length == 0) {
    Browser.msgBox("出庫対象はありません。");
    console.log("出庫対象はありません。");
    return;
  }


  // 出庫する(対象商品の出荷数を在庫管理表に減算する)
  let shinShohinData = [];
  const zaikoShohin = getColData(zaikoData,zItemColIdx).flat();
  for (let i = 0; i < shukkoData.length; i++) {
    if (zaikoShohin.indexOf(shukkoData[i][sItemColIdx]) != -1) {
      const idx = zaikoShohin.indexOf(shukkoData[i][sItemColIdx]);
      const zQuantityBefore = zaikoData[idx][zQuantityColIdx];
      zaikoData[idx][zQuantityColIdx] = zQuantityBefore - shukkoData[i][sQuantityColIdx];
      console.log(shukkoData[i][sItemColIdx] + "の在庫数:" + zaikoData[idx][zQuantityColIdx] + "(現在庫" + zQuantityBefore + " - 出庫数" + shukkoData[i][sQuantityColIdx] + ")");
    } else {
      console.log("新商品:" + shukkoData[i][sItemColIdx] + "の在庫数:" + shukkoData[i][sQuantityColIdx]);
      shinShohinData.push(shukkoData[i]);
    }
  }
  // 在庫管理表を更新する
  if (zaikoData.length != 0) {
    zaikoSheet.getRange(2, zItemColIdx + 1, zaikoData.length, 1).setValues(getColData(zaikoData, zItemColIdx));
    zaikoSheet.getRange(2, zQuantityColIdx + 1, zaikoData.length, 1).setValues(getColData(zaikoData, zQuantityColIdx));
  }

  // 出庫対象を削除する(削除前に出荷履歴に転記)
  shukkaRirekiSheet.getRange(rLastRowNumber + 1, 1, shukkoData.length, sLastColNumber).setValues(shukkoData);
  const reversedShukkoRowNumbers = shukkoRowNumbers.reverse();
  for (let i = 0; i < reversedShukkoRowNumbers.length; i++) {
    shukkaSheet.deleteRow(reversedShukkoRowNumbers[i]);
  }

  // 処理終了ダイアログ
  Browser.msgBox("出庫が完了しました。");
  console.log("出庫が完了しました。");
}

共通.gs

/**
 * 指定の列のデータを返す(指定の列を縦方向にデータを取得したいときに使う)。
 * @param {Array} array 2次元配列
 * @param {Number} colIdx 列番号インデックス
 * @return {Array} colData 指定列データ(2次元配列)
 */
function getColData(array, colIdx) {
  let colData = [];
  for (let i = 0; i < array.length; i++) {
    colData.push([array[i][colIdx]]);
  }
  return colData;
}

コメント

  1. るな より:

    初めまして。

    在庫管理の動画を探していたらこちらにたどり着きました。

    在庫管理のページに項目を増やしたりもできるのでしょうか?

    • shogo shogo より:

      コメントありがとうございます。
      はい、こちらの最新版ではできます。
      列を追加してお試しください。