上記の在庫管理でできることを増やしました。
なお、分かりやすさ重視でフィールドコードはフィールド名と一致させています。
JavaScript
(() => {
'use strict';
// レコード追加画面で保存したときの処理
kintone.events.on('app.record.create.submit', async (event) => {
const record = event.record;
const stockAppId = kintone.app.getLookupTargetAppId('商品コード');
const outBoundAppId = kintone.app.getId();
const tableRecords = record.出庫商品リスト.value;
let hasError = false;
let errorMessages = new Set();
let itemCodeSet = new Set();
try {
const client = new KintoneRestAPIClient();
const requests = [];
const stockUpdates = [];
for (const row of tableRecords) {
const itemCode = row.value.商品コード.value;
const outBoundNum = row.value.出庫数.value;
row.value.商品コード.error = null;
row.value.出庫数.error = null;
// [商品コード]のバリデーション
if (!itemCode) {
row.value.商品コード.error = '[商品コード]を入力してください。';
hasError = true;
errorMessages.add('[商品コード]が未入力の行があります。');
} else if (itemCodeSet.has(itemCode)) {
row.value.商品コード.error = '[商品コード]が重複しています。';
hasError = true;
errorMessages.add('[商品コード]が重複している行があります。');
} else {
itemCodeSet.add(itemCode);
}
// [出庫数]のバリデーション
if (!outBoundNum) {
row.value.出庫数.error = '[出庫数]を入力してください。';
hasError = true;
errorMessages.add('[出庫数]が未入力の行があります。');
} else if (!/^\d+$/.test(outBoundNum)) {
row.value.出庫数.error = '[出庫数]は半角数字で入力してください。';
hasError = true;
errorMessages.add('[出庫数]に半角数字以外が入力されている行があります。');
} else if (Number(outBoundNum) < 1) {
row.value.出庫数.error = '[出庫数]は1以上にしてください。';
hasError = true;
errorMessages.add('[出庫数]に0が入力されている行があります。');
}
if (!itemCode || !outBoundNum || !/^\d+$/.test(outBoundNum) || Number(outBoundNum) < 1) {
continue;
}
// 在庫アプリから[商品コード]が一致するレコードを取得
const { records: stockRecords } = await client.record.getRecords({
app: stockAppId,
query: '商品コード = "' + itemCode + '"',
fields: ['$id', '$revision', '在庫数']
});
// 出庫後の[在庫数]を計算
const stockNum = Number(stockRecords[0].在庫数.value);
const newStockNum = stockNum - Number(outBoundNum);
// 現時点の[在庫数]が足りているか確認
if (newStockNum < 0) {
row.value.出庫数.error = `現在の在庫は ${stockNum} です。`;
hasError = true;
errorMessages.add(`在庫が不足している商品があります。`);
continue;
}
// [在庫数]更新リクエストを作成
stockUpdates.push({
id: stockRecords[0].$id.value,
record: {
在庫数: { value: newStockNum }
},
revision: stockRecords[0].$revision.value
});
}
// エラーがあったら画面上部にエラーメッセージを表示
if (hasError) {
event.error = Array.from(errorMessages).join('\n');
return event;
}
// [在庫数]更新リクエストをバッチ送信
while (stockUpdates.length > 0) {
const batch = stockUpdates.splice(0, 99);
requests.push({
method: 'PUT',
api: '/k/v1/records.json',
payload: {
app: stockAppId,
records: batch
}
});
}
// 出庫アプリのレコード追加リクエストを作成
requests.push({
method: 'POST',
api: '/k/v1/record.json',
payload: {
app: outBoundAppId,
record: {
出庫先: { value: record.出庫先.value },
出庫商品リスト: { value: tableRecords }
}
}
});
try {
// リクエスト一括送信
const bulkResp = await client.bulkRequest({ requests });
location.href = '/k/' + outBoundAppId + '/show#record=' + bulkResp.results[requests.length - 1].id;
return false;
} catch (err) {
// リクエスト一括送信に失敗した場合のエラーハンドリング
console.log(err);
event.error = '出庫に失敗しました。';
return event;
}
} catch (err) {
// 在庫アプリからレコード取得に失敗した場合のエラーハンドリング
console.log(err);
event.error = '商品を取得できませんでした。';
return event;
}
});
// レコード追加画面で[取消]を編集不可にする
kintone.events.on('app.record.create.show', (event) => {
event.record.取消.disabled = true;
return event;
});
// レコード詳細画面を表示したときの処理
kintone.events.on('app.record.detail.show', async (event) => {
const record = event.record;
// [取消]に「済」のチェックがあるレコードでは何もしない
if (record.取消.value.includes('済')) {
return event;
}
// 取消ボタンを作成
const cancelButton = document.createElement('button');
cancelButton.id = 'cancel-button';
cancelButton.innerText = '取消';
cancelButton.style.marginTop = '18px';
cancelButton.style.marginLeft = '18px';
// 取消ボタンのクリックイベント
cancelButton.onclick = async () => {
if (confirm('本当にこの出庫処理を取消しますか?')) {
try {
const client = new KintoneRestAPIClient();
const stockAppId = kintone.app.getLookupTargetAppId('商品コード');
const recordId = kintone.app.record.getId();
const requests = [];
const stockUpdates = [];
// [出庫商品リスト]をループして[在庫数]を元に戻す
for (const row of record.出庫商品リスト.value) {
const itemCode = row.value.商品コード.value;
const outBoundNum = Number(row.value.出庫数.value);
const { records: stockRecords } = await client.record.getRecords({
app: stockAppId,
query: '商品コード = "' + itemCode + '"',
fields: ['$id', '$revision', '在庫数']
});
if (stockRecords.length === 0) {
continue;
}
const stockNum = Number(stockRecords[0].在庫数.value);
const newStockNum = stockNum + outBoundNum;
stockUpdates.push({
id: stockRecords[0].$id.value,
record: {
在庫数: { value: newStockNum }
},
revision: stockRecords[0].$revision.value
});
}
// [在庫数]更新リクエストをバッチ送信
while (stockUpdates.length > 0) {
const batch = stockUpdates.splice(0, 99);
requests.push({
method: 'PUT',
api: '/k/v1/records.json',
payload: {
app: stockAppId,
records: batch
}
});
}
// [取消]に「済」チェックを入れるリクエスト
requests.push({
method: 'PUT',
api: '/k/v1/record.json',
payload: {
app: kintone.app.getId(),
id: recordId,
record: {
取消: { value: ['済'] }
}
}
});
// リクエスト一括送信
await client.bulkRequest({ requests });
// 取消処理の結果通知
alert('取消が完了しました。');
location.reload();
} catch (err) {
console.error(err);
alert('取消に失敗しました。');
}
}
};
// パンくずリストの下に取消ボタンを設置
kintone.app.record.getHeaderMenuSpaceElement().appendChild(cancelButton);
return event;
});
})();