ken_nogi/pleasanter/develop2/四者MTG回覧板システム/scripts/01_回覧板システム.js
Kenichiro NOGI 88a402ce0f up
2026-07-10 18:13:30 +09:00

702 lines
28 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Pleasanter回顧板システム
// Copyright (c) 2024 Ken Nogi
//
// 四者MTG回覧
//
let apiKey = '6504c8a807677a3a576e10327f3c19876c55736ee45d4a845796b9e7f5e087bfd4bd0d8184863da8cf1733ca635111432ab334aea59102b06a96ee6d2c05190d';
//let kairanId = '483435'; //回覧板種類ID
//----------------------------------------------------------------------
let pageLoading = 0;
let hostUrl = location.protocol + '//' + location.host;
//初期処理
window.addEventListener('load', async function () {
if (pageLoading == 0) {
pageLoading = 1;
console.log('Window load Event');
await $p.ex.onLoad();
}
});
/*
$p.events.on_editor_load = function () {
if (pageLoading == 0) {
pageLoading = 1;
console.log('on Editor Load');
$p.ex.onLoad();
}
}
*/
$p.ex.onLoad = async function () {
//回顧板システム
console.log('回顧板システム onLoad');
//「依頼履歴」テーブルの読み込み完了後にボタン生成
waitForElement('#Results_ClassX', function () {
//回覧確認のチェック
$p.ex.kairanChecker();
//確認済みユーザー名のチェック
$p.ex.checkUserList();
});
//マスターデータの取得(完了を待つ)
await $p.ex.getMasterData();
if ($p.action() == 'new') {
console.log('新規作成時の処理');
$p.ex.setMSSData();
//回覧資料マスタ設定
let kairanName = $('#Results_ClassZ').val();
$p.set($('#Results_DescriptionX'), masterDocumentsList[kairanName].dataList);
} else {
console.log('更新時の処理');
//更新のチェック
$p.ex.checkUpdate();
//ファイルをまとめてDLするボタンの生成
$p.ex.createDLButton();
}
//マスターシート名の差し替え
if ($('#Results_ClassA').text().includes('?')) {
$('#Results_ClassA').text($('#Results_ClassC').val());
}
//確認用チェックボックスをOFF
$p.set($('#Results_CheckA'), false);
}
//新規作成時、マスターシートお客様データファイルから飛んできた場合
$p.ex.setMSSData = function () {
let mssId = $('#Results_ClassU').val();
if (mssId != '') {
$p.set($('#Results_ClassA'), mssId); //マスターシートIDを項目にセット
}
}
$p.events.before_validate = async function (args) {
await $p.ex.getMasterData();
}
$p.events.after_set_Update = function () {
console.log('after set Update');
//回覧確認のチェック
$p.ex.kairanChecker();
//確認済みユーザー名のチェック
$p.ex.checkUserList();
//更新のチェック
$p.ex.checkUpdate();
//ファイルをまとめてDLするボタンの生成
$p.ex.createDLButton();
//マスターシート名の差し替え
if ($('#Results_ClassA').text().includes('?')) {
$('#Results_ClassA').text($('#Results_ClassC').val());
}
}
//ファイルをまとめてDLするボタンの生成
$p.ex.createDLButton = function () {
let numA = $('#Results_NumA').val();
let mssName = $('#Results_ClassC').val();
let kairanName = $('#Results_ClassZ').val();
let zipFileName = mssName + '■' + kairanName + '■資料一式';
let guidListString = $('#Results_Description001').val();
if (numA > 0 && guidListString.length > 0) {
let button = document.createElement("button");
//if ($('#dlFileButton').length) {
// return;
//}
button.type = 'button';
button.id = 'dlFileButton';
button.innerHTML = "ファイルをまとめてDL";
button.setAttribute('onclick', 'dlAllFiles("' + zipFileName + '")');
button.style.margin = '3px';
button.style.float = 'left';
$('#Results_NumAField').after(button);
$('#dlFileButton').button({
icon: 'ui-icon-disk'
});
}
}
/*
ClassW : エリア
ClassG : 主担当営業
ClassH : 副担当営業
ClassI : 営業設計u
ClassJ : 担当監督u
ClassK : 担当ICu
ClassL : 管理設計u
ClassY : 所属長
Class194 : お客様データファイルID
*/
//マスターシートからデータを取得
$p.on('change', 'ClassA', function () {
if (!$('#Results_CheckA').is(':checked')) {
if ($('#Results_ClassA').val() != '') {
console.log('マスターシートの情報を取得しています...');
$p.set($('#Results_CheckA'), true); //確認用チェックボックスをON
//モーダルを表示(閉じるボタンなし)
$('body').append('<div id="custom-modal" style="position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0,0,0,0.5);z-index:9999;display:flex;align-items:center;justify-content:center;"><div style="background:#fff;padding:30px 50px;border-radius:8px;text-align:center;">マスターシートからデータを取得しています...</div></div>');
$p.ex.getMSSData();
}
}
});
//マスターシートからデータを取得
$p.ex.getMSSData = function () {
let resultId = $("#Results_ClassA").val();
$p.apiGet({
'id': resultId,
'async': false,
'done': function (data) {
//console.log(data);
if (data.Response.TotalCount == '1') {
let mssData = data.Response.Data[0];
//マスターシートの情報を保存
$p.set($('#Results_ClassB'), mssData.ClassHash.ClassW) //エリア
$p.set($('#Results_ClassC'), mssData.ItemTitle) //マスターシート名
$p.set($('#Results_ClassF'), mssData.ClassHash.ClassG) //主担当営業
$p.set($('#Results_ClassG'), mssData.ClassHash.ClassK) //担当IC
$p.set($('#Results_ClassH'), mssData.ClassHash.ClassL) //管理設計
$p.set($('#Results_ClassI'), mssData.ClassHash.ClassJ) //担当監督
$p.set($('#Results_ClassJ'), mssData.ClassHash.ClassY) //所属長
let tantoList = [];
tantoList.push(mssData.ClassHash.ClassG); //主担当営業
tantoList.push(mssData.ClassHash.ClassK); //担当IC
tantoList.push(mssData.ClassHash.ClassL); //管理設計
tantoList.push(mssData.ClassHash.ClassJ); //担当監督
tantoList.push(mssData.ClassHash.ClassY); //所属長
$p.set($('#Results_Class010'), JSON.stringify(tantoList)); //担当者リスト
let documentId = mssData.ClassHash.Class194; //お客様データファイルID
if (documentId != '') {
$p.set($('#Results_ClassT'), documentId); //お客様データファイルIDを項目にセット
//$p.ex.getMSSDocumentsData(documentId);
}
let areaCode = mssData.ClassHash.ClassW; //エリアコード
if (areaCode != '' && templateMasterData[areaCode] && areaNotificationList[areaCode]) {
let template = templateMasterData[areaCode].template; //テンプレートマスターデータ
$p.set($('#Results_DescriptionB'), template); //テンプレートマスターデータを内容にセット
let areaList = areaNotificationList[areaCode]; //エリア別通知先マスターデータ}
let list1 = areaList.list1 ? JSON.parse(areaList.list1) : [];
let list2 = areaList.list2 ? JSON.parse(areaList.list2) : [];
let list3 = areaList.list3 ? JSON.parse(areaList.list3) : [];
let list4 = areaList.list4 ? JSON.parse(areaList.list4) : [];
let list5 = areaList.list5 ? JSON.parse(areaList.list5) : [];
//let list6 = JSON.parse(areaList.list6);
let allList = list1.concat(list2, list3, list4, list5); //全てのリストを結合
console.log(JSON.stringify(allList));
//console.log(areaList.list6);
$p.set($('#Results_Class004'), JSON.stringify(allList)); //エリア別通知先マスターデータを項目にセット
//$p.set($('#Results_Class005'), areaList.list6); //エリア別通知先マスターデータを項目にセット
}
$p.clearMessage();
$p.ex.setMyMessage('new-status-lime', 'マスターシートの情報を取得しました');
$('#custom-modal').remove();
console.log('マスターシートの情報を取得しました。');
}
},
'fail': function (data) {
console.log(data);
}
});
}
//お客様データファイルからファイル情報を取得
$p.ex.getMSSDocumentsData = function (documentId) {
//$p.ex.getDocumentFolder();
console.log('お客様データファイルの情報を取得します。');
let resultId = documentId;
$p.apiGet({
'id': resultId,
'async': false,
'done': function (data) {
console.log(data);
if (data.Response.TotalCount == '1') {
let docData = data.Response.Data[0];
$.each(documentList, function (index, value) {
let label = value.id;
let docName = value.name;
let docValue = docData.AttachmentsHash[label];
//console.log(docValue);
documentList[index].list = docValue;
});
$p.ex.setDocumentList();
}
console.log('お客様データファイルの情報を取得しました。');
},
'fail': function (data) {
console.log(data);
}
});
return;
}
//ドキュメントリストを内容にセット
$p.ex.setDocumentList = function () {
console.log('ドキュメントリストをセットします。');
let data = ['[md]'];
let data2 = [];
let guidList = [];
let documentCount = 0;
$.each(documentList, function (index, value) {
let listName = value.name;
let attachList = value.list;
if (attachList.length > 0) {
data.push('■■■ ' + listName + ' ■■■');
data2.push('■■■ ' + listName + ' ■■■');
$.each(attachList, function (i, attach) {
let attachMarkdown = `▷ [${attach.Name}](${hostUrl}/pleasanter/binaries/${attach.Guid}/show) [▶DL](${hostUrl}/pleasanter/binaries/${attach.Guid}/download)`;
let attachFileName = `${attach.Name}`;
let attachFileLink = ` ▶DL ${hostUrl}/pleasanter/binaries/${attach.Guid}/download`;
data.push(attachMarkdown);
data2.push(attachFileName);
data2.push(attachFileLink);
guidList.push(attach.Guid);
documentCount++;
});
data.push('');
}
});
$.each(documentList, function (index, value) {
console.log(value.name + ': ' + value.list.length + ' files');
});
let markdown = data.join(' \n');
let fileList = data2.join('\n');
let guIdListString = guidList.join(',');
$p.set($('#Results_DescriptionA'), markdown);
$p.set($('#Results_DescriptionZ'), fileList);
$p.set($('#Results_NumA'), documentCount);
$p.set($('#Results_Description001'), guIdListString);
console.log('ドキュメントリストをセットしました。');
return;
}
//メッセージ表示
$p.ex.setMyMessage = function (cssColor = 'alert-success', message, clear = 1) {
clear && $p.clearMessage();
$p.setMessage('#Message', JSON.stringify({
Css: 'alert-success ' + cssColor,
Text: message
}));
}
//ドキュメントフォルダの情報を取得
let targetDocuments = [];
let documentList = [];
$p.ex.getDocumentFolder = function () {
targetDocuments = [];
documentList = [];
waitForElement('#Results_DescriptionX', function () {
let descX = $('#Results_DescriptionX').val().split('\n');
//forループに変更する
for (let i = 0; i < descX.length; i++) {
let documentId = descX[i].split(';')[0].trim();
targetDocuments.push(documentId);
}
$p.apiGetSite({
'id': '212512',
'async': false,
'done': function (data) {
console.log(data);
console.log(targetDocuments);
//$.eachではなくfor文を使うと非同期処理の順序が保証される
for (let i = 0; i < targetDocuments.length; i++) {
let documentId = targetDocuments[i];
let columnsList = data.Response.Data.SiteSettings.Columns;
for (let j = 0; j < columnsList.length; j++) {
let column = columnsList[j];
if (column.ColumnName == documentId) {
//console.log(column);
//console.log('ドキュメントフォルダの情報を取得しました。');
let label = {
'id': documentId,
'name': column.LabelText,
'list': []
};
documentList.push(label);
}
}
}
console.log('サイト情報の取得に成功しました。');
//$p.ex.getMSSData();
$p.ex.getMSSDocumentsData($('#Results_ClassT').val());
}
});
});
}
//テンプレートのマスターデータの取得
let templateMasterData = {};
let areaNotificationList = {};
let fitNotificationList = {};
let masterDocumentsList = {};
//マスタデータ取得
$p.ex.getMasterData = async function () {
let templateMaster = JSON.parse($('#templateMaster').val());
$.each(templateMaster, function (area, data) {
let templateString = data.template;
// Base64 文字列を UTF-8 として復元する。
// デコードに失敗した場合は生の文字列をそのまま使う。
try {
if (typeof data.template === 'string' && data.template.length > 0) {
const binary = atob(data.template);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
templateString = new TextDecoder('utf-8').decode(bytes);
}
} catch (e) {
templateString = data.template;
}
templateMasterData[area] = {
"template": templateString
};
});
console.log('テンプレートマスタデータを取得しました。');
//console.log(templateMasterData);
//エリア別通知先マスターデータの取得
areaNotificationList = $('#areaNotificationList').val() ? JSON.parse($('#areaNotificationList').val()) : {};
console.log('エリア別通知先マスタデータを取得しました。');
//console.log(areaNotificationList);
//FIT通知先マスターの取得とセット
fitNotificationList = $('#fitNotificationList').val() ? JSON.parse($('#fitNotificationList').val()) : {};
console.log('fit通知先マスタデータを取得しました。');
//console.log(fitNotificationList);
masterDocumentsList = $('#masterDocumentsList').val() ? JSON.parse($('#masterDocumentsList').val()) : {};
console.log('回覧資料マスタデータを取得しました。');
//console.log(masterDocumentsList);
//項目にセット
if ($('#Results_Class001').val().length == 0 && fitNotificationList['list1'] != '[]') {
waitForElement('#Results_Class001', function () {
$p.set($('#Results_Class001'), fitNotificationList['list1']);
});
}
if ($('#Results_Class002').val().length == 0 && fitNotificationList['list2'] != '[]') {
waitForElement('#Results_Class002', function () {
$p.set($('#Results_Class002'), fitNotificationList['list2']);
});
}
if ($('#Results_Class003').val().length == 0 && fitNotificationList['list3'] != '[]') {
waitForElement('#Results_Class003', function () {
$p.set($('#Results_Class003'), fitNotificationList['list3']);
});
}
return;
}
//プロセス実行前の処理
$p.ex.processScript = async function (proccessButton) {
//ユーザー名とIDを取得
let userName = $p.userName();
let userId = $p.userId().toString();
//押されたボタンからプロセス番号を取得
let procNum = proccessButton[0].id.replace('Process_', '');
console.log('processScript ' + procNum);
let popMessage = '';
// プロセス番号に応じた処理
switch (procNum) {
case '1': //日程回覧 ------------------------------------------------------------------
//let numA = parseFloat($('#Results_NumA').val());
//if (numA == 0) {
// popMessage = '回覧確認用の添付ファイルが1つもありません。マスターシートを確認してください。';
// $p.ex.setMyMessage('new-status-red', popMessage, 1);
// return;
//}
popMessage = '日程回覧を発行します。よろしいですか?';
if (!confirm(popMessage)) return;
//回覧通知先を全て連結して保存
let list1 = $('#Results_Class001').val(); //FIT通訳
let list2 = $('#Results_Class002').val(); //FITメンバー
let list3 = $('#Results_Class003').val(); //確認申請課
let list4 = $('#Results_Class004').val(); //エリア別担当者
let list5 = $('#Results_Class005').val(); //その他
let list10 = $('#Results_Class010').val(); //物件担当者
let allList = list1.concat(list2, list3, list4, list5, list10);
$p.set($('#Results_ClassY'), JSON.stringify(allList)); //エリア別通知先マスターデータを項目にセット
//四者日程DateDの日付を取得し、1週間前の日付をDateEにセット JSTで処理
let dateD = $('#Results_DateD').val();
if (dateD != '') {
let date = new Date(dateD);
let oneWeekBeforeDate = new Date(date.setDate(date.getDate() - 7));
//YYYY/MM/DD形式に変換
let year = oneWeekBeforeDate.getFullYear();
let month = ('0' + (oneWeekBeforeDate.getMonth() + 1)).slice(-2);
let day = ('0' + oneWeekBeforeDate.getDate()).slice(-2);
let oneWeekBeforeStr = `${year}/${month}/${day}`;
$p.set($('#Results_DateE'), oneWeekBeforeStr);
} else {
popMessage = '四者日程を設定してください。';
$p.ex.setMyMessage('new-status-red', popMessage, 1);
return;
}
$p.ex.logging('日程回覧: ' + userName);
break;
case '2': //資料回覧 ------------------------------------------------------------------
popMessage = '資料回覧を発行します。よろしいですか?';
if (!confirm(popMessage)) return;
//ステータスが資料回覧以降になってから資料をセット
//お客様データのIDが空の場合、マスターシートから取得する
if ($('#Results_ClassT').val() == '') {
//お客様データファイルIDが空の場合、マスターシートから取得
$p.ex.getMSSData();
}
//お客様データファイルIDがそれでも空の場合、エラーを表示して処理を中断
if ($('#Results_ClassT').val() == '') {
popMessage = '回覧資料が取得できません。マスターシートを確認してください。';
$p.ex.setMyMessage('new-status-red', popMessage, 1);
return;
}
//お客様データファイルの情報を取得
$p.ex.getDocumentFolder();
//$p.ex.getMSSDocumentsData($('#Results_ClassT').val());
//回覧資料数をチェック
let numA = parseFloat($('#Results_NumA').val());
if (numA == 0) {
popMessage = '資料回覧用の添付ファイルが1つもありません。マスターシートを確認してください。';
$p.ex.setMyMessage('new-status-red', popMessage, 1);
return;
}
$p.ex.logging('資料回覧: ' + userName);
break;
case '3': //回覧確認 ------------------------------------------------------------------
popMessage = '本回覧の内容を確認済みにします。よろしいですか?';
if (!confirm(popMessage)) return;
let classX = $('#Results_ClassX').val();
classX.push(userId);
$p.set($('#Results_ClassX'), JSON.stringify(classX)); //回覧確認者リストに追加
$p.ex.logging('回覧確認: ' + userName);
//Results_ClassY と Results_ClassX に含まれるユーザーが完全に一致した場合
let classY = $('#Results_ClassY').val();
//配列を並び替え
classX.sort();
classY.sort();
//JSON.stringifyで比較
if (JSON.stringify(classX) === JSON.stringify(classY)) {
$p.ex.logging('回覧完了: ' + userName);
pronNum = '9'; //回覧完了プロセスを実行
}
break;
case '4': //回覧完了 ------------------------------------------------------------------
popMessage = '本回覧をクローズします。よろしいですか?';
if (!confirm(popMessage)) return;
$p.ex.logging('回覧完了: ' + userName);
break;
case '5': //回覧保留 ------------------------------------------------------------------
popMessage = '本回覧を保留にします。よろしいですか?';
if (!confirm(popMessage)) return;
$p.ex.logging('回覧保留: ' + userName);
break;
case '6': //回覧取消 ------------------------------------------------------------------
popMessage = '本回覧を取り消します。よろしいですか?';
if (!confirm(popMessage)) return;
$p.ex.logging('回覧取消: ' + userName);
break;
case '7': //回覧差戻し ------------------------------------------------------------------
popMessage = '本回覧を回覧発行前に差戻します。よろしいですか?';
if (!confirm(popMessage)) return;
$p.ex.logging('回覧差戻し: ' + userName);
break;
case '8': //コメント送信 ------------------------------------------------------------------
if ($('#Comments').val() == '') {
popMessage = 'コメントが入力されていません。コメントを入力してください。';
$p.ex.setMyMessage('new-status-red', popMessage, 1);
return;
}
popMessage = 'コメントを送信します。よろしいですか?';
if (!confirm(popMessage)) return;
$p.ex.logging('コメント送信: ' + userName);
break;
}
// プロセス実行
$p.execProcess($('#Process_' + procNum));
}
//ログ記録
$p.ex.logging = function (message) {
let now = new Date();
let timestamp = now.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' });
console.log(`[${timestamp}] ${message}`);
let logData = $('#Results_Body').val();
if (logData) {
logList = logData.split('\n');
logList.push(`[${timestamp}] ${message}`);
} else {
logList = [`[${timestamp}] ${message}`];
}
$p.set($('#Results_Body'), logList.join('\n'));
}
//確認済みかどうかのチェック
$p.ex.kairanChecker = function () {
let classX = $('#Results_ClassX').val();
let classY = $('#Results_ClassY').val();
let userId = $p.userId().toString();
if (classY.includes(userId)) {
$('#kairanCheck').remove();
if (!classX.includes(userId)) {
$('#HeaderTitleContainer').after('<div id="kairanCheck" class="kairanNotChecked">未確認</div>');
} else {
$('#HeaderTitleContainer').after('<div id="kairanCheck" class="kairanChecked">確認済み</div>');
$('#Process_3').hide(); //回覧確認ボタンを非表示
}
}
console.log('回覧確認のチェックをしました。');
}
//確認済みユーザー名のチェック
$p.ex.checkUserList = function () {
//let classX = $('#Results_ClassX').val();
//let classY = $('#Results_ClassY').val();
let classYlist = $('#Results_ClassY_ms > span:nth-child(2)').text().split(',').map(item => item.trim());
let classXlist = $('#Results_ClassX_ms > span:nth-child(2)').text().split(',').map(item => item.trim());
let classYorg = $('#Results_ClassY_ms > span:nth-child(2)').text();
//classXlistのユーザーIDがclassYlistに含まれているかチェック 含まれる名前の前後に<span class="checkDone">をつける
classXlist.forEach(function (userName) {
if (classYlist.includes(userName)) {
console.log('確認済みユーザー: ' + userName);
//let userName = $('#Results_ClassY_ms > span:nth-child(2)').text().split(',').find(name => name.includes(userId));
classYorg = classYorg.replace(userName, '<span class="checkDone">' + userName + '</span>');
}
});
$('#Results_ClassY_ms > span:nth-child(2)').html(classYorg);
console.log('確認済みユーザー名のチェックをしました。');
}
//要素が読み込まれるまで待機し、返す
function waitForElement(selector, callback, interval = 100, timeout = 5000) {
const startTime = Date.now();
const checkExist = setInterval(function () {
if ($(selector).length) {
clearInterval(checkExist); //要素が見つかったら停止
callback();
} else if (Date.now() - startTime > timeout) {
clearInterval(checkExist); //タイムアウト処理
console.warn(`指定した要素 '${selector}' が ${timeout / 1000} 秒以内に見つかりませんでした。`);
}
}, interval);
}
//更新のチェック 5秒ごとにAPIを叩いて更新時間を確認、更新されていたら更新時間を赤くする
$p.ex.checkUpdate = function () {
let id = null;
function startInterval() {
if (id !== null) return false;
id = setInterval(function () {
$p.apiGet({
'id': $p.id(),
'done': function (data) {
const body = "#" + $("#ReferenceType").val() + "_Body";
const time = "#" + $("#ReferenceType").val() + "_UpdatedTime";
const update_time = $(time).attr("datetime");
if (data.Response.Data[0].UpdatedTime != update_time) {
//$(time).css("color", "red");
$p.clearMessage();
$p.ex.setMyMessage('new-status-yellow', 'この回覧は他のユーザーによって更新されました。更新・確認ボタンを押す前に再読み込みしてください。');
}
},
'fail': function (data) {
console.log(data);
}
});
}, 5000);
}
startInterval();
}