From 8801e606040d37e7a16d97bdf4a7d234c009ff5f Mon Sep 17 00:00:00 2001 From: xue jiahao Date: Fri, 19 Sep 2025 16:56:33 +0800 Subject: [PATCH] batch approve --- src/ProcessBatchApprovalHandler.js | 283 ++ .../processStateHandler.js | 2 + .../KintoneRestAPIClient.min.js | 1 + src/10.個別支援計画/kuc.min.js | 2433 +++++++++++++++++ .../processBatchApproval.js | 54 + src/10.個別支援計画/processStateHandler.js | 2 + .../processStateHandler.js | 2 + .../processStateHandler.js | 2 + .../KintoneRestAPIClient.min.js | 1 + src/2.保育計画 月案(0歳児用)/kuc.min.js | 2433 +++++++++++++++++ .../processBatchApproval.js | 50 + .../processStateHandler.js | 2 + .../pvc.main.js | 0 .../KintoneRestAPIClient.min.js | 1 + src/3.保育計画 月案/kuc.min.js | 2433 +++++++++++++++++ src/3.保育計画 月案/processBatchApproval.js | 55 + src/3.保育計画 月案/processStateHandler.js | 19 + src/3.保育計画 月案/pvc.main.js | 17 + .../KintoneRestAPIClient.min.js | 1 + src/4.保育計画 週案/kuc.min.js | 2433 +++++++++++++++++ src/4.保育計画 週案/processBatchApproval.js | 154 ++ src/4.保育計画 週案/processStateHandler.js | 2 + src/6.個別配慮/processBatchApproval.js | 54 + src/6.個別配慮/processStateHandler.js | 2 + .../KintoneRestAPIClient.min.js | 1 + src/8.学期反省・評価/kuc.min.js | 2433 +++++++++++++++++ .../processBatchApproval.js | 54 + src/8.学期反省・評価/processStateHandler.js | 2 + 28 files changed, 12926 insertions(+) create mode 100644 src/ProcessBatchApprovalHandler.js create mode 100644 src/10.個別支援計画/KintoneRestAPIClient.min.js create mode 100644 src/10.個別支援計画/kuc.min.js create mode 100644 src/10.個別支援計画/processBatchApproval.js create mode 100644 src/2.保育計画 月案(0歳児用)/KintoneRestAPIClient.min.js create mode 100644 src/2.保育計画 月案(0歳児用)/kuc.min.js create mode 100644 src/2.保育計画 月案(0歳児用)/processBatchApproval.js rename src/{2&3.保育計画 月案 => 2.保育計画 月案(0歳児用)}/processStateHandler.js (86%) rename src/{2&3.保育計画 月案 => 2.保育計画 月案(0歳児用)}/pvc.main.js (100%) create mode 100644 src/3.保育計画 月案/KintoneRestAPIClient.min.js create mode 100644 src/3.保育計画 月案/kuc.min.js create mode 100644 src/3.保育計画 月案/processBatchApproval.js create mode 100644 src/3.保育計画 月案/processStateHandler.js create mode 100644 src/3.保育計画 月案/pvc.main.js create mode 100644 src/4.保育計画 週案/KintoneRestAPIClient.min.js create mode 100644 src/4.保育計画 週案/kuc.min.js create mode 100644 src/4.保育計画 週案/processBatchApproval.js create mode 100644 src/6.個別配慮/processBatchApproval.js create mode 100644 src/8.学期反省・評価/KintoneRestAPIClient.min.js create mode 100644 src/8.学期反省・評価/kuc.min.js create mode 100644 src/8.学期反省・評価/processBatchApproval.js diff --git a/src/ProcessBatchApprovalHandler.js b/src/ProcessBatchApprovalHandler.js new file mode 100644 index 0000000..961b39e --- /dev/null +++ b/src/ProcessBatchApprovalHandler.js @@ -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}
${failedText}

合計: ${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; + +})(); \ No newline at end of file diff --git a/src/1.園児別出欠簿入力/processStateHandler.js b/src/1.園児別出欠簿入力/processStateHandler.js index 1d36c1e..ced813b 100644 --- a/src/1.園児別出欠簿入力/processStateHandler.js +++ b/src/1.園児別出欠簿入力/processStateHandler.js @@ -8,6 +8,8 @@ '完了': '園長' } + window._StatusFieldMap = statusFieldMap + kintone.events.on("app.record.detail.process.proceed", (event) => { const field = statusFieldMap[event.nextStatus.value]; if (field) { diff --git a/src/10.個別支援計画/KintoneRestAPIClient.min.js b/src/10.個別支援計画/KintoneRestAPIClient.min.js new file mode 100644 index 0000000..dc9c93d --- /dev/null +++ b/src/10.個別支援計画/KintoneRestAPIClient.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.KintoneRestAPIClient=t():e.KintoneRestAPIClient=t()}(window,(function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=74)}([function(e,t,r){(function(t){var r=function(e){return e&&e.Math==Math&&e};e.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof t&&t)||Function("return this")()}).call(this,r(40))},function(e,t,r){var n=r(0),o=r(41),i=r(4),u=r(45),s=r(46),a=r(78),c=o("wks"),f=n.Symbol,l=a?f:f&&f.withoutSetter||u;e.exports=function(e){return i(c,e)||(s&&i(f,e)?c[e]=f[e]:c[e]=l("Symbol."+e)),c[e]}},function(e,t,r){"use strict";var n=r(65),o=Object.prototype.toString;function i(e){return"[object Array]"===o.call(e)}function u(e){return void 0===e}function s(e){return null!==e&&"object"==typeof e}function a(e){return"[object Function]"===o.call(e)}function c(e,t){if(null!=e)if("object"!=typeof e&&(e=[e]),i(e))for(var r=0,n=e.length;rh;h++)if((y=f?g(n(b=e[h])[0],b[1]):g(e[h]))&&y instanceof c)return y;return new c(!1)}p=d.call(e)}for(m=p.next;!(b=m.call(p)).done;)if("object"==typeof(y=a(p,g,b.value,f))&&y&&y instanceof c)return y;return new c(!1)}).stop=function(e){return new c(!0,e)}},function(e,t){e.exports=function(e){try{return{error:!1,value:e()}}catch(e){return{error:!0,value:e}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.injectPlatformDeps=t.platformDeps=void 0,t.platformDeps={readFileFromPath:function(){throw new Error("not implemented")},getRequestToken:function(){throw new Error("not implemented")},buildPlatformDependentConfig:function(){throw new Error("not implemented")},buildHeaders:function(){throw new Error("not implemented")}},t.injectPlatformDeps=function(e){t.platformDeps.readFileFromPath=e.readFileFromPath,t.platformDeps.getRequestToken=e.getRequestToken,t.platformDeps.buildPlatformDependentConfig=e.buildPlatformDependentConfig,t.platformDeps.buildHeaders=e.buildHeaders}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.buildPath=void 0,t.buildPath=function(e){var t=e.endpointName,r=e.guestSpaceId;return"/k"+(void 0!==r?"/guest/"+r:"")+"/v1"+(e.preview?"/preview":"")+"/"+t+".json"}},function(e,t,r){var n={};n[r(1)("toStringTag")]="z",e.exports="[object z]"===String(n)},function(e,t,r){var n=r(0),o=r(5);e.exports=function(e,t){try{o(n,e,t)}catch(r){n[e]=t}return t}},function(e,t,r){var n=r(0),o=r(9),i=n.document,u=o(i)&&o(i.createElement);e.exports=function(e){return u?i.createElement(e):{}}},function(e,t,r){var n=r(42),o=Function.toString;"function"!=typeof n.inspectSource&&(n.inspectSource=function(e){return o.call(e)}),e.exports=n.inspectSource},function(e,t,r){var n=r(41),o=r(45),i=n("keys");e.exports=function(e){return i[e]||(i[e]=o(e))}},function(e,t){e.exports={}},function(e,t){var r=Math.ceil,n=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?n:r)(e)}},function(e,t){e.exports=function(e){if(null==e)throw TypeError("Can't call method on "+e);return e}},function(e,t,r){var n=r(7),o=r(83),i=r(19),u=r(20),s=r(44),a=r(4),c=r(43),f=Object.getOwnPropertyDescriptor;t.f=n?f:function(e,t){if(e=u(e),t=s(t,!0),c)try{return f(e,t)}catch(e){}if(a(e,t))return i(!o.f.call(e,t),e[t])}},function(e,t){e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},function(e,t,r){var n=r(4),o=r(92),i=r(29),u=r(93),s=i("IE_PROTO"),a=Object.prototype;e.exports=u?Object.getPrototypeOf:function(e){return e=o(e),n(e,s)?e[s]:"function"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?a:null}},function(e,t,r){var n,o=r(3),i=r(94),u=r(34),s=r(30),a=r(54),c=r(27),f=r(29),l=f("IE_PROTO"),p=function(){},d=function(e){return"