batch approve

This commit is contained in:
2025-09-19 16:56:33 +08:00
parent e00eae5930
commit 8801e60604
28 changed files with 12926 additions and 0 deletions

View File

@@ -0,0 +1,283 @@
(function () {
"use strict";
/**
* 一括承認処理を管理するクラス
* 特定のビューでレコードの一括承認機能を提供する
*/
class BatchApprovalHandler {
/**
* コンストラクタ
* @param {number} mineView - マイビューのID
* @param {string} classifyField - 分類フィールドのプレースホルダー
* @param {Object} btnClassifyMap - 年齢分類マッピングオブジェクト
* @param {Object} actionMap - アクション名マッピングオブジェクト
* @param {Function} getStatusPrefix - ステータス接頭辞を取得する関数
* @param {Object} statusFieldMap
* @param {string} classifyFormField
*/
constructor(mineView, classifyField, btnClassifyMap, actionMap, getStatusPrefix, statusFieldMap, classifyFormField = "学年") {
this.mineView = mineView;
this.classifyField = classifyField;
this.btnClassifyMap = btnClassifyMap;
this.actionMap = actionMap;
this.getStatusPrefix = getStatusPrefix;
this.statusFieldMap = statusFieldMap;
this.classifyFormField = classifyFormField;
this.stopFlag = false;
}
stopProcess() {
this.stopFlag = true;
}
/**
* 一括承認ボタンを作成する
* @method
*/
createApproveBtn() {
const headerSpace = getHeaderSpace('single-label-line');
const elements = createBtnGroupArea('batch-approve-area', '一括承認', this.handleApproveData.bind(this), {
btnElId: 'batch-btn'
});
if (elements && headerSpace) {
headerSpace.appendChild(elements['batch-approve-area']);
}
}
/**
* 一括承認処理を実行する
* @method
* @param {Event} e - イベントオブジェクト
*/
async handleApproveData(e) {
this.stopFlag = false;
const appId = kintone.app.getId();
const loginUser = kintone.getLoginUser();
loading(true, '一括承認処理中...');
showError(false);
const api = new KintoneRestAPIClient();
// レコードを取得
const records = await this.getRecords(api, appId, loginUser.code);
if (!records || records.length === 0) {
loading(false);
return e;
}
// ステータスを更新
const result = await this.updateRecordsStatus(api, appId, records);
if (result && result.success > 0) {
// 承認者情報を更新
await this.updateRecords(api, appId, records, result.successIds, loginUser);
await this.showSuccessDialog(result);
}
loading(false);
}
/**
* 成功ダイアログを表示する
* @method
* @param {Object} result - 処理結果
*/
async showSuccessDialog(result) {
const contentEl = document.createElement('div');
contentEl.style.fontSize = '16px';
if (result.success === result.total) {
contentEl.innerHTML = `✅ 全${result.total}件のレコードを承認しました`;
} else {
const successText = `${result.success}件のレコードを承認済み`;
const failedText = `⚠️ ${result.total - result.success}件の承認に失敗`;
contentEl.innerHTML = `${successText}<br>${failedText}<br><br>合計: ${result.total}`;
}
showDialog({
title: '一括承認完了',
content: contentEl,
ok: '更新して確認',
cancel: false,
onClose: () => { location.reload(); }
});
}
/**
* アクションプロセスマップを読み込む
* @method
* @param {Object} api - Kintone REST API クライアント
* @param {number} appId - アプリID
*/
async loadActionProcessMap(api, appId) {
try {
const result = await api.app.getProcessManagement({
app: appId,
preview: false
});
const actionProcessMap = {};
result.actions.forEach(action => {
actionProcessMap[action.from + ":" + action.name] = action;
});
return actionProcessMap;
} catch (error) {
showError(true, 'プロセス管理情報の読み取りに失敗しました\n - ' + error.message);
}
}
/**
* レコードを取得する
* @method
* @param {Object} api - Kintone REST API クライアント
* @param {number} appId - アプリID
* @param {string} loginUserCd - ログインユーザーコード
*/
async getRecords(api, appId, loginUserCd) {
try {
return await api.record.getAllRecordsWithId({
app: appId,
condition: `作業者 in ("${loginUserCd}")`
});
} catch (error) {
showError(true, 'レコードの読み取りに失敗しました\n - ' + error.message);
return null;
}
}
/**
* レコードを更新する
* @method
* @param {Object} api - Kintone REST API クライアント
* @param {number} appId - アプリID
* @param {Array} oldRecords - 更新前レコード
* @param {Set} successIds - 成功したレコードID
* @param {Object} loginUser - ログインユーザー情報
*/
async updateRecords(api, appId, oldRecords, successIds, loginUser) {
const actionProcessMap = await this.loadActionProcessMap(api, appId);
if (!actionProcessMap) {
return;
}
const updateFieldRecords = [];
for (const record of oldRecords) {
const updateKey = this.getRecordAutoUpdateField(record, actionProcessMap, successIds);
if (updateKey) {
updateFieldRecords.push({
'id': record.$id.value,
'record': {
[updateKey]: {
'value': loginUser.name
}
}
});
}
}
if (updateFieldRecords.length > 0) {
try {
await api.record.updateAllRecords({
app: appId,
records: updateFieldRecords
});
} catch (error) {
showError(true, 'レコード更新に失敗しました\n - ' + error.message);
}
}
}
/**
* 自動更新フィールド名を取得する
* @method
* @param {Object} record - レコードオブジェクト
* @param {Object} actionProcessMap - アクションプロセスマップ
* @param {Set} successIds - 成功したレコードID
*/
getRecordAutoUpdateField(record, actionProcessMap, successIds) {
const id = record.$id.value;
if (!successIds.has(id)) {
return null;
}
const oldStatus = record.ステータス.value;
const actionName = record.action;
const action = actionProcessMap[oldStatus + ":" + actionName];
return action ? this.statusFieldMap[action.to] : null;
}
/**
* レコードステータスを更新する
* @method
* @param {Object} api - Kintone REST API クライアント
* @param {number} appId - アプリID
* @param {Array} records - 更新対象レコード
*/
async updateRecordsStatus(api, appId, records) {
const updateRecords = []
for (const record of records) {
const actionToPerform = this.getActionName(record);
if (this.stopFlag) {
return
}
if (!!actionToPerform) {
record.action = actionToPerform;
updateRecords.push({
id: record.$id.value,
action: actionToPerform,
revision: record.$revision.value
});
}
}
const result = {
success: 0,
total: records.length,
successIds: new Set(),
};
const batchSize = 100; // Kintone APIの制限
for (let i = 0; i < updateRecords.length; i += batchSize) {
const batch = updateRecords.slice(i, i + batchSize);
try {
const response = await api.record.updateRecordsStatus({
app: appId,
records: batch
});
result.success += response.records.length;
response.records.forEach(record => {
result.successIds.add(record.id);
});
} catch (error) {
showError(true, `レコードステータス更新エラー\n - ${error.message}`);
}
}
return result;
}
/**
* アクション名を取得する
* @method
* @param {string} record - レコードオブジェクト
*/
getActionName(record) {
const yearStr = record[this.classifyFormField]?.value || '';
const status = record.ステータス.value;
const actionTemplate = this.actionMap[this.getStatusPrefix(status)] || '';
const btnClassify = this.btnClassifyMap[yearStr] || '';
return actionTemplate.replaceAll(this.classifyField, btnClassify);
}
}
// グローバルにクラスを公開
window.BatchApprovalHandler = BatchApprovalHandler;
})();

View File

@@ -8,6 +8,8 @@
'完了': '園長'
}
window._StatusFieldMap = statusFieldMap
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {

File diff suppressed because one or more lines are too long

2433
src/10.個別支援計画/kuc.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,54 @@
(function () {
"use strict";
// マイビューのID
const mineView = 13353711;
// 分類フィールドのプレースホルダー
const classifyField = "${classify}";
// ボタン分類マッピング
const btnClassifyMap = {
"0歳児": "0~2歳",
"1歳児": "0~2歳",
"2歳児": "0~2歳",
"3歳児": "3~5歳",
"4歳児": "3~5歳",
"5歳児": "3~5歳",
}
// アクションマッピング
const actionMap = {
"担任作成中": `指導教諭確認依頼${classifyField}`,
"指導教諭確認中": `承認する`,
"園長確認中": `承認する${classifyField}`
}
/**
* ステータスから接頭辞を取得する
* @param {string} status - ステータス文字列
*/
const getStatusPrefix = (status) => {
return status.split("")[0];
};
// kintoneイベントの登録
kintone.events.on("app.record.index.show", (event) => {
// 特定のビューの場合のみ処理を実行
if (event.viewId === mineView) {
// BatchApprovalHandlerクラスのインスタンスを作成
const batchApproval = new BatchApprovalHandler(
mineView,
classifyField,
btnClassifyMap,
actionMap,
getStatusPrefix,
_StatusFieldMap
);
// 一括承認ボタンを作成
batchApproval.createApproveBtn();
}
return event;
});
})();

View File

@@ -10,6 +10,8 @@
"完了": "園長3"
}
window._StatusFieldMap = statusFieldMap
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {

View File

@@ -8,6 +8,8 @@
'完了': '園長'
}
window._StatusFieldMap = statusFieldMap
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {

View File

@@ -9,6 +9,8 @@
'完了': '園長'
}
window._StatusFieldMap = statusFieldMap
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,50 @@
(function () {
"use strict";
// マイビューのID
const mineView = 8352036;
// 分類フィールドのプレースホルダー
const classifyField = "${classify}";
// ボタン分類マッピング
const btnClassifyMap = {
"0歳児": "",
}
// アクションマッピング
const actionMap = {
"担任作成中": `指導教諭確認依頼`,
"指導教諭確認中": `承認する`,
"主幹確認中": `承認する`,
"園長確認中": `承認する`
}
/**
* ステータスから接頭辞を取得する
* @param {string} status - ステータス文字列
*/
const getStatusPrefix = (status) => {
return status.split("")[0];
};
// kintoneイベントの登録
kintone.events.on("app.record.index.show", (event) => {
// 特定のビューの場合のみ処理を実行
if (event.viewId === mineView) {
// BatchApprovalHandlerクラスのインスタンスを作成
const batchApproval = new BatchApprovalHandler(
mineView,
classifyField,
btnClassifyMap,
actionMap,
getStatusPrefix,
_StatusFieldMap
);
// 一括承認ボタンを作成
batchApproval.createApproveBtn();
}
return event;
});
})();

View File

@@ -6,6 +6,8 @@
'完了': '園長'
}
window._StatusFieldMap = statusFieldMap
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {

File diff suppressed because one or more lines are too long

2433
src/3.保育計画 月案/kuc.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,55 @@
(function () {
"use strict";
// マイビューのID
const mineView = 8352036;
// 分類フィールドのプレースホルダー
const classifyField = "${classify}";
// ボタン分類マッピング
const btnClassifyMap = {
"1歳児": "0~2歳",
"2歳児": "0~2歳",
"3歳児": "3~5歳",
"4歳児": "3~5歳",
"5歳児": "3~5歳",
"5歳児": "3~5歳",
}
// アクションマッピング
const actionMap = {
"担任作成中": `指導教諭確認依頼${classifyField}`,
"指導教諭確認中": `承認する`,
"主幹確認中": `承認する`,
"園長確認中": `承認する${classifyField}`
}
/**
* ステータスから接頭辞を取得する
* @param {string} status - ステータス文字列
*/
const getStatusPrefix = (status) => {
return status.split("")[0];
};
// kintoneイベントの登録
kintone.events.on("app.record.index.show", (event) => {
// 特定のビューの場合のみ処理を実行
if (event.viewId === mineView) {
// BatchApprovalHandlerクラスのインスタンスを作成
const batchApproval = new BatchApprovalHandler(
mineView,
classifyField,
btnClassifyMap,
actionMap,
getStatusPrefix,
_StatusFieldMap
);
// 一括承認ボタンを作成
batchApproval.createApproveBtn();
}
return event;
});
})();

View File

@@ -0,0 +1,19 @@
(function () {
"use strict";
const statusFieldMap = {
'園長確認中': '指導',
'完了': '園長'
}
window._StatusFieldMap = statusFieldMap
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {
event.record[field].value = kintone.getLoginUser().name;
}
return event;
});
})();

View File

@@ -0,0 +1,17 @@
(function () {
"use strict";
var events = [
"app.record.create.show",
"app.record.edit.show",
"app.record.create.change.担当",
"app.record.edit.change.担当"
];
kintone.events.on(events, function(event) {
var record = event.record;
record.担任.value = record.担当.value.map(function(user) {return user.name}).join(",");
return event;
});
})();

File diff suppressed because one or more lines are too long

2433
src/4.保育計画 週案/kuc.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,154 @@
(function () {
"use strict";
// マイビューのID
const mineView = 8352036;
// 分類フィールドのプレースホルダー
const classifyField = "${classify}";
// ボタン分類マッピング
const btnClassifyMap = {
"0歳児": "0~2歳",
"1歳児": "0~2歳",
"2歳児": "0~2歳",
"3歳児": "3~5歳",
"4歳児": "3~5歳",
"5歳児": "3~5歳",
}
// アクションマッピング
const actionMap = {
"担任作成中": `指導教諭確認依頼(${classifyField}`,
"指導教諭確認中": `承認する`,
"園長確認中": `承認する(${classifyField}`
}
// ダイアログ要素と次のステータスを格納する変数
let askDialogEl;
let nextStatus;
// 次のステータスのテンプレートオブジェクト
const nextStatusTemplate = {
"next": `承認する(${classifyField}、5週目作成依頼`,
"end": `承認する(${classifyField}、完了)`
}
/**
* ステータスから接頭辞を取得する
* @param {string} status - ステータス文字列
*/
const getStatusPrefix = (status) => {
return status.split("")[0];
};
// kintoneイベントの登録
kintone.events.on("app.record.index.show", (event) => {
// 特定のビューの場合のみ処理を実行
if (event.viewId === mineView) {
// BatchApprovalHandlerクラスのインスタンスを作成
const batchApproval = new BatchApprovalHandler(
mineView,
classifyField,
btnClassifyMap,
actionMap,
getStatusPrefix,
_StatusFieldMap,
"担当学年"
);
// 元のgetActionNameメソッドを保存
const originalGetActionName = batchApproval.getActionName;
// getActionNameメソッドをオーバーライド
batchApproval.getActionName = function (record) {
const status = record.ステータス.value;
// 「園長確認中4週目」以外のステータスは元の処理を実行
if (status !== '園長確認中4週目') {
return originalGetActionName.call(this, record);
}
// 週目に応じて異なるステータスに遷移する場合、ユーザーに選択させる
if (!nextStatus) {
askNextStatus(batchApproval)
batchApproval.stopProcess();
return
}
return getSpecialActionName(record);
};
// 一括承認ボタンを作成
batchApproval.createApproveBtn();
}
return event;
});
/**
* 特殊なアクション名を取得する
* @param {Object} record - レコードオブジェクト
* @returns {string} アクション名
*/
function getSpecialActionName(record) {
const template = nextStatusTemplate[nextStatus];
const yearStr = record.担当学年.value || '';
const btnClassify = btnClassifyMap[yearStr] || '';
return template.replaceAll(classifyField, btnClassify);
}
/**
* 次のステータスをユーザーに問い合わせる
* @param {Object} batchApproval - BatchApprovalHandlerインスタンス
*/
function askNextStatus(batchApproval) {
askDialogEl = new Kuc.Dialog({
title: '一括承認を一時停止',
content: makeDialog(batchApproval),
footerVisible: false
});
askDialogEl.open();
return '';
}
/**
* ダイアログ内容を作成する
* @param {Object} batchApproval - BatchApprovalHandlerインスタンス
* @returns {HTMLElement} ダイアログのコンテンツ要素
*/
function makeDialog(batchApproval) {
const contentEl = document.createElement('div');
contentEl.innerHTML = `
<p>
「園長確認中4週目」の記録について、次のステータスを選択してください
</p>
`;
const divEl = document.createElement('div');
divEl.style.textAlign = 'center';
// 5週目作成依頼ボタン
const nextButton = new Kuc.Button({
text: '承認する5週目作成依頼',
type: 'submit',
});
nextButton.style.paddingRight = '16px';
nextButton.addEventListener('click', (event) => {
askDialogEl.close();
nextStatus = "next";
batchApproval.handleApproveData()
});
divEl.appendChild(nextButton);
// 完了ボタン
const endButton = new Kuc.Button({
text: '承認する(完了)',
type: 'submit',
});
endButton.addEventListener('click', (event) => {
askDialogEl.close();
nextStatus = "end";
batchApproval.handleApproveData()
});
divEl.appendChild(endButton);
contentEl.appendChild(divEl);
return contentEl;
}
})();

View File

@@ -9,6 +9,8 @@
'完了5週目': '園長',
}
window._StatusFieldMap = statusFieldMap
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {

View File

@@ -0,0 +1,54 @@
(function () {
"use strict";
// マイビューのID
const mineView = 13352233;
// 分類フィールドのプレースホルダー
const classifyField = "${classify}";
// ボタン分類マッピング
const btnClassifyMap = {
"0歳児": "0~2歳",
"1歳児": "0~2歳",
"2歳児": "0~2歳",
"3歳児": "3~5歳",
"4歳児": "3~5歳",
"5歳児": "3~5歳",
}
// アクションマッピング
const actionMap = {
"担任作成中": `指導教諭確認依頼${classifyField}`,
"指導教諭確認中": `承認する`,
"園長確認中": `承認する${classifyField}`
}
/**
* ステータスから接頭辞を取得する
* @param {string} status - ステータス文字列
*/
const getStatusPrefix = (status) => {
return status.split("")[0];
};
// kintoneイベントの登録
kintone.events.on("app.record.index.show", (event) => {
// 特定のビューの場合のみ処理を実行
if (event.viewId === mineView) {
// BatchApprovalHandlerクラスのインスタンスを作成
const batchApproval = new BatchApprovalHandler(
mineView,
classifyField,
btnClassifyMap,
actionMap,
getStatusPrefix,
_StatusFieldMap
);
// 一括承認ボタンを作成
batchApproval.createApproveBtn();
}
return event;
});
})();

View File

@@ -63,6 +63,8 @@
'完了': '園長_3月'
}
window._StatusFieldMap = statusFieldMap
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {

File diff suppressed because one or more lines are too long

2433
src/8.学期反省・評価/kuc.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,54 @@
(function () {
"use strict";
// マイビューのID
const mineView = 13352759;
// 分類フィールドのプレースホルダー
const classifyField = "${classify}";
// ボタン分類マッピング
const btnClassifyMap = {
"0歳児": "0~2歳",
"1歳児": "0~2歳",
"2歳児": "0~2歳",
"3歳児": "3~5歳",
"4歳児": "3~5歳",
"5歳児": "3~5歳",
}
// アクションマッピング
const actionMap = {
"担任作成中": `指導教諭確認依頼${classifyField}`,
"指導教諭確認中": `承認する`,
"園長確認中": `承認する${classifyField}`
}
/**
* ステータスから接頭辞を取得する
* @param {string} status - ステータス文字列
*/
const getStatusPrefix = (status) => {
return status.split("")[0];
};
// kintoneイベントの登録
kintone.events.on("app.record.index.show", (event) => {
// 特定のビューの場合のみ処理を実行
if (event.viewId === mineView) {
// BatchApprovalHandlerクラスのインスタンスを作成
const batchApproval = new BatchApprovalHandler(
mineView,
classifyField,
btnClassifyMap,
actionMap,
getStatusPrefix,
_StatusFieldMap
);
// 一括承認ボタンを作成
batchApproval.createApproveBtn();
}
return event;
});
})();

View File

@@ -10,6 +10,8 @@
'完了3学期': '園長3',
}
window._StatusFieldMap = statusFieldMap
kintone.events.on("app.record.detail.process.proceed", (event) => {
const field = statusFieldMap[event.nextStatus.value];
if (field) {