kintone
投稿日:

「出荷指示書、毎回FAXで送っていませんか?」─ kintoneで出荷管理をペーパーレス化する

皆さんこんにちは。kintoneアプリエンジニアのtomiokaです。

先日、食品メーカーのお客様から「出荷ミスを減らしたい」と相談がありました。

従業員80名ほどの会社で、受注が入ると営業担当がExcelで出荷指示書を作り、それをFAXで倉庫に送る。倉庫側はFAXを見ながらピッキングして出荷する、、、、という流れでした。問題は、FAXの文字が読みにくくて商品コードを読み間違えたり、FAXが倉庫のどこかに紛れてしまったりして、月に2〜3件の誤出荷が発生していたことです。

今回は、この出荷管理をkintoneで構築し、受注から出荷指示、出荷実績の記録、在庫の自動更新までを一気通貫で管理できるようにした事例を紹介します。従業員50〜200名規模の製造業・メーカーで、出荷指示をFAXや紙ベースでやっている方に参考になる内容です。

構築したアプリの全体像

今回は3つのアプリを連携させました。

アプリ名役割
受注アプリ営業担当が受注情報を登録する
出荷指示アプリ受注データから出荷指示を自動生成し、倉庫担当に通知する
在庫アプリ商品ごとの在庫数を管理し、出荷時に自動で減算する

ポイントは、受注アプリにレコードを登録するだけで出荷指示が自動的に作られ、倉庫担当のスマホに通知が届く仕組みにしたことです。FAXは完全に不要になりました。

受注アプリのフィールド構成

フィールド名フィールドタイプフィールドコード備考
受注番号文字列(1行)order_number自動採番
受注日日付order_date
得意先名文字列(1行)customer_name
商品名ドロップダウンproduct_nameマスタから選択
商品コード文字列(1行)product_codeルックアップで自動取得
数量数値quantity
出荷希望日日付shipping_date
ステータスドロップダウンstatus受注済/出荷指示済/出荷完了
営業担当ユーザー選択sales_person

出荷指示の自動生成

受注アプリでレコードが登録されたタイミングで、出荷指示アプリにレコードを自動生成するカスタマイズです。

(function() {
  'use strict';

  var SHIPPING_APP_ID = 100; // 出荷指示アプリのID

  kintone.events.on('app.record.create.submit.success', function(event) {
    var record = event.record;

    var shippingRecord = {
      app: SHIPPING_APP_ID,
      record: {
        order_number: { value: record.order_number.value },
        customer_name: { value: record.customer_name.value },
        product_name: { value: record.product_name.value },
        product_code: { value: record.product_code.value },
        quantity: { value: record.quantity.value },
        shipping_date: { value: record.shipping_date.value },
        shipping_status: { value: '未出荷' }
      }
    };

    kintone.api(kintone.api.url('/k/v1/record.json', true), 'POST', shippingRecord)
      .then(function() {
        // 受注アプリのステータスを「出荷指示済」に更新
        return kintone.api(kintone.api.url('/k/v1/record.json', true), 'PUT', {
          app: kintone.app.getId(),
          id: event.recordId,
          record: {
            status: { value: '出荷指示済' }
          }
        });
      })
      .catch(function(error) {
        console.error('出荷指示の作成に失敗しました:', error);
      });

    return event;
  });
})();

app.record.create.submit.successイベントを使っているのがポイントです。submit(保存実行前)ではなくsubmit.success(保存成功後)で発火させることで、受注レコードが確実に保存された後に出荷指示を作ります。保存に失敗した場合は出荷指示が作られないので、データの整合性が保たれます。

出荷完了時の在庫自動更新

倉庫担当が出荷指示アプリで「出荷完了」に変更したタイミングで、在庫アプリの該当商品の在庫数を自動で減算します。

(function() {
  'use strict';

  var INVENTORY_APP_ID = 101; // 在庫アプリのID

  kintone.events.on('app.record.edit.submit', function(event) {
    var record = event.record;

    if (record.shipping_status.value !== '出荷完了') {
      return event;
    }

    var productCode = record.product_code.value;
    var shippedQuantity = Number(record.quantity.value);

    // 在庫アプリから該当商品のレコードを取得
    var query = 'product_code = "' + productCode + '"';
    var params = {
      app: INVENTORY_APP_ID,
      query: query
    };

    return kintone.api(kintone.api.url('/k/v1/records.json', true), 'GET', params)
      .then(function(resp) {
        if (resp.records.length === 0) {
          event.error = '在庫アプリに該当商品が見つかりません: ' + productCode;
          return event;
        }

        var inventoryRecord = resp.records[0];
        var currentStock = Number(inventoryRecord.stock_quantity.value);
        var newStock = currentStock - shippedQuantity;

        if (newStock < 0) {
          event.error = '在庫が不足しています。現在庫: ' + currentStock + ', 出荷数: ' + shippedQuantity;
          return event;
        }

        return kintone.api(kintone.api.url('/k/v1/record.json', true), 'PUT', {
          app: INVENTORY_APP_ID,
          id: inventoryRecord.$id.value,
          record: {
            stock_quantity: { value: String(newStock) }
          }
        }).then(function() {
          return event;
        });
      })
      .catch(function(error) {
        event.error = '在庫更新に失敗しました: ' + error.message;
        return event;
      });
  });
})();

ここではapp.record.edit.submit(保存実行前)を使っています。在庫不足の場合はevent.errorにメッセージを設定して保存を中断します。これにより、在庫がマイナスになるような出荷を防げます。

倉庫担当へのモバイル通知

kintoneの通知機能を使って、出荷指示が作られたら倉庫担当にスマホ通知が届くようにしています。これはカスタマイズではなく、kintoneの標準機能で設定できます。

出荷指示アプリのアプリ設定から「通知」→「レコードの条件通知」で、以下のように設定します。

  • 条件:shipping_statusが「未出荷」のレコードが追加されたとき
  • 通知先:倉庫担当グループ

倉庫担当はスマホのkintoneアプリで通知を受け取り、出荷指示の内容を確認して作業に入れます。FAXを待つ必要がなくなり、受注から出荷指示が届くまでのタイムラグもほぼゼロになりました。

ハマりどころ

submit.successイベントでの非同期処理

app.record.create.submit.successイベントでもPromiseを返せばkintoneは処理完了を待ってくれます。ただし、submit.successではevent.errorによる保存の取り消しができません(保存は既に完了しているため)。つまり、出荷指示の作成中にエラーが起きても、受注レコードの保存を巻き戻すことはできません。

今回のコードでは、意図的にPromiseを返さずreturn event;を即座に返しています。出荷指示の作成が数秒遅れても業務上問題がなく、エラー時はconsole.errorに記録して運用で対応する設計にしたためです。もし出荷指示の作成完了を確実に待ちたい場合は、Promiseを返す形に変更すれば対応できます、、、、ただしエラー時のリカバリ設計は別途必要です。

在庫更新の排他制御

複数の倉庫担当が同時に異なる出荷指示を「出荷完了」にした場合、在庫の更新が競合する可能性があります。kintoneのREST APIにはレコードのリビジョン番号を使った楽観的排他制御があるので、本番運用ではPUTリクエストにrevisionパラメータを含めることを推奨します。

今回のサンプルコードでは簡略化のため省略していますが、実運用では以下のようにrevisionを含めます。

return kintone.api(kintone.api.url('/k/v1/record.json', true), 'PUT', {
  app: INVENTORY_APP_ID,
  id: inventoryRecord.$id.value,
  revision: inventoryRecord.$revision.value,
  record: {
    stock_quantity: { value: String(newStock) }
  }
});

revisionが一致しない場合はAPIがエラーを返すので、リトライ処理を入れるか、ユーザーに「他の人が更新中です」と伝える設計にします。

数値フィールドの型

kintoneの数値フィールドの値は、JavaScript上では文字列として返ってきます。計算する際はNumber()で変換し、REST APIにPUTする際はString()で文字列に戻す必要があります。正直、最初はここでハマりました、、、、

導入結果

このお客様では、導入から1ヶ月後に以下の変化がありました。

  • FAXによる出荷指示が完全にゼロになった
  • 受注から出荷指示が届くまでの時間が「FAX送信→確認」の10〜15分から、ほぼリアルタイムに短縮された
  • 商品コードの読み間違いによる誤出荷がゼロになった(導入前は月2〜3件)
  • 在庫数がリアルタイムで把握できるようになり、「出荷したのに在庫表が更新されていない」問題が解消された

お客様からは「FAXを使わなくなっただけでこんなに楽になるとは思わなかった」と言っていただけました。

まとめ

今回は、受注→出荷指示→在庫更新をkintoneで一気通貫管理する仕組みを紹介しました。

  • 受注アプリの保存成功時に出荷指示を自動生成する(submit.successイベント)
  • 出荷完了時に在庫を自動減算する(submitイベントで在庫不足チェックも実装)
  • kintone標準の通知機能で倉庫担当にモバイル通知を送る

FAXや紙ベースの出荷管理は、読み間違い・紛失・タイムラグという3つの問題を構造的に抱えています。kintoneに置き換えることで、これらの問題を根本から解消できます。

株式会社ファストコーディングでは、kintoneのカスタマイズや業務アプリの構築を承っています。「出荷管理をデジタル化したい」「受注と在庫を連携させたい」という方は、お問い合わせフォームからお気軽にご連絡ください。