import { actionAddins } from "."; import { IAction,IActionResult, IActionNode, IActionProperty, IField, IContext } from "../types/ActionTypes"; import { ConditionTree } from '../types/Conditions'; /** * アクションの属性定義 */ interface IInsertValueProps{ field:IField; condition:string; value:string; show:string; } /** * */ export class InsertValueAction implements IAction{ name: string; actionProps: IActionProperty[]; props:IInsertValueProps; constructor(){ this.name="値を挿入する";// DBに登録したアクション名 this.actionProps=[]; //プロパティ属性の初期化 this.props={ field:{code:''}, condition:'', value:'', show:'' } //アクションを登録する this.register(); } /** * 空白文字を空白文字が非対応のフィールドに挿入しようとしていないか、必須項目フィールドに挿入しようとしていないかチェックする * @param {string} inputValue - 挿入する値 * @return {boolean} -入力値が有効な日付形式の場合はtrueを返し、そうでない場合は例外を発生させる */ checkInputBlank(fieldType :string | undefined,inputValue :string,fieldCode :string,fieldRequired :boolean | undefined,event :any): boolean{ //正規表現チェック let blankCheck= inputValue.match(/^(\s| )+?$/);//半角スペース・タブ文字・改行・改ページ・全角スペース if(blankCheck !== null){ //空白文字を空白文字が非対応のフィールドに挿入しようとしている場合、例外を発生させる if(fieldType === "NUMBER" || fieldType === "DATE" || fieldType === "DATETIME" || fieldType === "TIME" || fieldType === "USER_SELECT" || fieldType === "ORGANIZATION_SELECT" || fieldType === "GROUP_SELECT" || fieldType === "RADIO_BUTTON" || fieldType === "DROP_DOWN" || fieldType === "CHECK_BOX" || fieldType === "MULTI_SELECT"){ event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドには、空白文字は挿入できません。"; //レコードにエラーを表示 throw new Error("「"+fieldCode+"」"+"フィールドには、空白文字は挿入できません。「値を挿入する」コンポーネントの処理を中断しました。"); //空白文字を必須項目フィールドに挿入しようとしている場合、例外を発生させる }else if(fieldRequired){ event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドは必須項目のため、空白文字は挿入できません。"; //レコードにエラーを表示 throw new Error("「"+fieldCode+"」"+"フィールドは必須項目のため、空白文字は挿入できません。「値を挿入する」コンポーネントの処理を中断しました。"); } } return true; } /** * 入力値が半角数字かチェックする関数 * @param {string} inputValue - 挿入する値 * @return {boolean} -入力値が有効な数値の場合はtrueを返し、そうでない場合は例外を発生させる */ checkInputNumber(inputValue :string,fieldCode :string,event :any): boolean{ let inputNumberValue = Number(inputValue);//数値型に変換 //有限数かどうか判定s if(!isFinite(inputNumberValue)){ event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な日付形式です。"; //レコードにエラーを表示 throw new Error("「"+fieldCode+"」"+"フィールドに入れようとした値は、有効な数値ではありません。「値を挿入する」コンポーネントの処理を中断しました。"); } return true; } /** 入力値が有効な日付形式かチェックする関数 * @param {string} inputValue - 挿入する値 * @return {boolean} -入力値が有効な日付形式の場合はtrueを返し、そうでない場合は例外を発生させる */ checkInputDate(inputValue :string,fieldCode :string,event :any): boolean{ //正規表現チェック let twoDigitMonthDay = inputValue.match(/(\d{4})-(\d{2})-(\d{2})$/);//4桁の数字-2桁の数字-2桁の数字 let singleDigitMonthDay = inputValue.match(/(\d{4})-(\d{1})-(\d{1})$/);//4桁の数字-1桁の数字-2桁の数字 let singleDigitMonth = inputValue.match(/(\d{4})-(\d{1})-(\d{2})$/);//4桁の数字-1桁の数字-2桁の数字 let singleDigitDay = inputValue.match(/(\d{4})-(\d{2})-(\d{1})$/);//4桁の数字-2桁の数字-1桁の数字 let dateTimeMilliSecond = inputValue.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).(\d{2,3})Z$/);//時刻入りのUTCの日付形式(ミリ秒) let dateTime = inputValue.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/);//時刻入りのUTCの日付形式 let date; //date型に変換 date = new Date(inputValue); //date型変換できたか確認 if(date !== undefined && !isNaN(date.getDate())){ //正規表現チェック確認 if(twoDigitMonthDay === null && singleDigitMonth === null && singleDigitDay === null && singleDigitMonthDay === null && dateTime === null && dateTimeMilliSecond === null){ event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な日付形式です。"; //レコードにエラーを表示 throw new Error("「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な日付形式です。「値を挿入する」コンポーネントの処理を中断しました。"); } } return true; } /** 入力値が有効な時刻形式かチェックする関数 * @param {string} inputValue - 挿入する値 * @return {boolean} -入力値が有効な日付形式の場合はtrueを返し、そうでない場合は例外を発生させる */ checkInputTime(inputValue :string,fieldCode :string,event :any): boolean{ //正規表現チェック let timeFormat =inputValue.match(/^([0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/); //正規表現チェック確認 if(timeFormat === null){ event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な時刻形式です。"; //レコードにエラーを表示 throw new Error("「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な時刻形式です。「値を挿入する」コンポーネントの処理を中断しました。"); } return true; } /** * 入力値のフィールドタイプがDATETIMEであれば、時刻ありの日付形式変換し、DATEであれば時刻なしの日付形式変換する関数 * @param {string} inputValue -挿入する値 * @param {string} fieldType -フィールドタイプ * @return {string} -入力値が日付形式に変換できた場合は文字列を返し、そうでない場合は例外を発生させる */ changeDateFormat(inputValue :string, fieldType :string,fieldCode :string,event :any): string{ let dateTime; let date; //挿入する値をdate型に変換 date = new Date(inputValue); //date型変換できたか確認 if(date !== undefined && !isNaN(date.getDate())){ //日時フィールドの場合、時刻ありの日付形式変換 if(fieldType === "DATETIME"){ dateTime =date.toISOString(); return dateTime; } //日付フィールドの場合、時刻なしの日付形式変換 let dateArray=inputValue.match(/(\d{4})-(\d{1,2})-(\d{1,2})$/);//4桁の数字-1~2桁の数字-1~2桁の数字 if(dateArray !== null){ let yearIndex = 1; let monthIndex = 2; let dayIndex = 3; let dateFormatted=`${dateArray[yearIndex]}-${dateArray[monthIndex]}-${dateArray[dayIndex]}` return dateFormatted; } } event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な日付形式です。"; //レコードにエラーを表示 throw new Error("「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な日付形式です。「値を挿入する」コンポーネントの処理を中断しました。"); } /** * 入力値がフィールドタイプ(ラジオボタン・ドロップダウン・複数選択・ドロップダウン)の選択肢に存在する値かチェックする関数 * @param {string} inputValue - 挿入する値 * @return {boolean} -入力値が有効な値の場合はtrueを返し、そうでない場合は例外を発生させる */ checkInputOption(inputValue :string,fieldOptions :string | undefined,fieldCode :string,event :any): boolean { //入力値が選択肢に存在する値かチェックし、存在したらtrueを返す if(fieldOptions !== undefined){ let options = Object.keys(fieldOptions); for(var optionsIndex in options){ if(options[optionsIndex] === inputValue){ return true; } } } event.record[fieldCode]['error']="「"+fieldCode+"」"+"には、存在しない値を挿入しようとしたため、処理を中断しました。"; throw new Error("「"+fieldCode+"」"+"には、存在しない値を挿入しようとしたため、処理を中断しました。「値を挿入する」コンポーネントの処理を中断しました。"); } /** * 入力値がフィールドタイプ(ユーザー選択)で、ユーザー情報に存在する値かチェックする関数 * @param {string} inputValue - 挿入する値 * @return {string | boolean} 入力値が登録されているユーザー情報から見つかった場合、trueを返し、見つからなかった場合、falseを返す */ async setInputUser(inputValue :string): Promise{ //ユーザー名を格納する変数 let usersName; const usersInfoColumnIndex=0; try{ //APIでユーザー情報を取得する const resp =await kintone.api(kintone.api.url('/v1/users', true), 'GET', {codes:[inputValue ]}) //入力されたログイン名(メールアドレス)がユーザー情報に登録されている場合、そのユーザー名を取得する if (resp.users[usersInfoColumnIndex].code === inputValue) { usersName=resp.users[usersInfoColumnIndex].name; } //ユーザー名が取得できた場合、ログイン名とユーザー名をフィールドにセットする if(usersName === undefined){ throw new Error(); } }catch{ return false; } return usersName; } /** * 入力値がフィールドタイプ(組織選択)で、組織情報に存在する値かチェックする関数 * @param {string} inputValue - 挿入する値 * @return {string | boolean} 入力値が登録されている組織情報から見つかった場合、trueを返し、見つからなかった場合、falseを返す */ async setInputOrganization(inputValue :string): Promise{ //組織名を格納する変数 let organizationName; const organizationsInfoColumnIndex=0; try{ //APIで組織情報を取得する const resp =await kintone.api(kintone.api.url('/v1/organizations.json', true), 'GET', {codes:[inputValue ]}) //入力された組織コードが組織情報に登録されている場合、その組織名を取得する if (resp.organizations[organizationsInfoColumnIndex].code === inputValue) { organizationName=resp.organizations[organizationsInfoColumnIndex].name; } //組織名が取得できた場合、組織コードと組織名をフィールドにセットする if(organizationName === undefined){ throw new Error(); } }catch{ return false; } return organizationName; } /** * 入力値がフィールドタイプ(グループ選択)で、グループ情報に存在する値かチェックする関数 * @param {string} inputValue - 挿入する値 * @return {string | boolean} 入力値が登録されているグループ情報から見つかった場合、trueを返し、見つからなかった場合、falseを返す */ async setInputGroup(inputValue :string): Promise{ //グループ名を格納する変数 let groupsName; const groupsInfoColumnIndex=0; try{ //APIでグループ情報を取得する const resp =await kintone.api(kintone.api.url('/v1/groups.json', true), 'GET', {codes:[inputValue ]}) //入力されたグループコードがグループ情報に登録されている場合、そのグループ名を取得する if (resp.groups[groupsInfoColumnIndex].code === inputValue) { groupsName=resp.groups[groupsInfoColumnIndex].name; } //グループ名が取得できた場合、グループコードとグループ名をフィールドにセットする if(groupsName === undefined){ throw new Error(); } }catch{ return false; } return groupsName; } /** * アクションの実行を呼び出す * @param actionNode * @param event * @returns */ async process(actionNode:IActionNode,event:any,context:IContext):Promise { let result={ canNext:true, result:false }; try{ //属性設定を取得する this.actionProps = actionNode.actionProps; if (!('field' in actionNode.ActionValue) && !('value' in actionNode.ActionValue)) { return result } const fieldColumnIndex=1; const valueColumnIndex=3; //プロパティで選択されたフィールド const field=this.actionProps[fieldColumnIndex].props.modelValue.type; //プロパティの挿入する値 const value=this.actionProps[valueColumnIndex].props.modelValue; //条件式の結果を取得 const conditionResult = this.getConditionResult(context); if(!conditionResult){ return result; } //プロパティの値を挿入するフィールドが未選択の場合、例外を発生させる if(field === null){ throw new Error("「値を挿入する」コンポーネントで、値を挿入するフィールドが指定されていなかったため、処理が中断されました。"); } //プロパティの値を挿入するフィールドが非対応フィールドの場合、例外を発生させる //添付ファイル・テーブル・カテゴリー・ステータス・作成者・更新者・作業者・リビジョン番号・レコード番号・レコードID・計算・作成日時・更新日時フィールドが選択されている場合、例外を発生させる if(field === "FILE" || field === "SUBTABLE" || field === "CATEGORY" || field === "STATUS" || field === "STATUS_ASSIGNEE" || field === "CREATOR" || field === "MODIFIER" || field === "__REVISION__" || field === "RECORD_NUMBER"|| field === "__ID__" || field ==="CALC" || field === "CREATED_TIME" || field === "UPDATED_TIME" ){ throw new Error("「値を挿入する」コンポーネントで、選択されたフィールドは、値を挿入するコンポーネントでは非対応のフィールドのため、処理を中断しました。"); } //プロパティの挿入する値が未入力の場合、例外を発生させる if(value === ""){ throw new Error("「値を挿入する」コンポーネントで、フィールドに挿入する値が指定されていなかったため、処理が中断されました。"); } //既定のプロパティのインターフェースへ変換する this.props = actionNode.ActionValue as IInsertValueProps; //挿入する値を取得 let fieldValue = this.props.value; //フィールドの種類を取得 const fieldType = this.props.field.type; //フィールドが必須項目なのか取得 const fieldRequired=this.props.field.required; //挿入するフィールドのコードを取得 const fieldCode=this.props.field.code; //挿入する値の形式(手入力か変数)を取得 const insertValueType=this.props.show; //ラジオボタン・チェックボックス・複数選択・ドロップダウンの選択肢を取得 let fieldOptions =this.props.field.options; //変数の値を格納する let variableValue; //変数の場合、値が取得できるかチェック if(insertValueType === "変数" && conditionResult){ variableValue = context.variables[fieldValue]; if(variableValue === undefined){ throw new Error("「"+fieldCode+"」"+"フィールドに入れようとした変数は、無効な入力形式です。"); } fieldValue = variableValue; } //入力値チェック後、形式変換、型変換した値を格納する変数 let correctFormattedValue; //入力値チェック後、形式変換、型変換した値を格納する配列 let correctValues :string[] = []; //入力エラー(空白文字の混入)がないことをチェック let notInputError=this.checkInputBlank(fieldType,fieldValue,fieldCode,fieldRequired,event); //条件式の結果がtrue、入力エラー(空白文字の混入)がない場合、挿入する値をフィールドタイプ別にチェックする if(conditionResult && notInputError){ //文字列型のフィールドに挿入しようとしている値が適切の場合、correctFormattedValueに代入する if(fieldType === "SINGLE_LINE_TEXT" || fieldType === "MULTI_LINE_TEXT" || fieldType === "RICH_TEXT" || fieldType === "LINK" ){ correctFormattedValue = fieldValue; //数値型のフィールドに挿入しようとしている値が適切の場合、数値型に型変換してcorrectFormattedValueに代入する }else if(fieldType === "NUMBER" ){ if(this.checkInputNumber(fieldValue,fieldCode,event)){//入力値チェック correctFormattedValue = Number(fieldValue);//型変換 } //日付・日時型のフィールドに挿入しようとしている値が適切の場合、指定の日付・日時に形式変換してcorrectFormattedValueに代入する }else if(fieldType === "DATE" || fieldType === "DATETIME" ){ if(this.checkInputDate(fieldValue,fieldCode,event)){//入力値チェック let formattedDate = this.changeDateFormat(fieldValue,fieldType,fieldCode,event) if(formattedDate){ correctFormattedValue = formattedDate } } //時刻フィールドに挿入しようとしている値が適切の場合、correctFormattedValueに代入する }else if(fieldType === "TIME"){ if(this.checkInputTime(fieldValue,fieldCode,event)){//入力値チェック correctFormattedValue = fieldValue; } //ラジオボタン・ドロップダウンのフィールドの選択肢と入力値が一致した場合、correctFormattedValueに代入する }else if(fieldType === "RADIO_BUTTON" || fieldType === "DROP_DOWN"){ if(this.checkInputOption(fieldValue,fieldOptions,fieldCode,event)){//入力値チェック correctFormattedValue = fieldValue; } //チェックボックス・複数選択のフィールドの選択肢と入力値が一致した場合、correctValuesの配列に代入する }else if(fieldType === "CHECK_BOX" || fieldType === "MULTI_SELECT" ){ if(this.checkInputOption(fieldValue,fieldOptions,fieldCode,event)){//入力値チェック correctValues[0] = fieldValue; } //ユーザー情報フィードに挿入しようとした値が適切な場合、correctFormattedValueに代入する }else if(fieldType === "USER_SELECT"){ //挿入する値がユーザー情報から見つかれば、ユーザー名を格納 let users=await this.setInputUser(fieldValue); //ユーザー名が格納できている場合、ログイン名とユーザー名をcorrectFormattedValueに代入する if(!users){ event.record[fieldCode]['error']="ユーザー選択に、挿入しようとしたユーザー情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。"; throw new Error("ユーザー選択に、挿入しようとしたユーザー情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。"); }else{ correctFormattedValue=[{ code: fieldValue, name: users }]; } //組織情報フィードに挿入しようとした値が適切な場合、correctFormattedValueに代入する }else if(fieldType === "ORGANIZATION_SELECT"){ //挿入する値が組織情報から見つかれば、組織名を格納 let organizations=await this.setInputOrganization(fieldValue); //組織名が格納できている場合、組織コードと組織名をcorrectFormattedValueに代入する if(!organizations){ event.record[fieldCode]['error']="組織選択フィールドに、挿入しようとした組織情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。"; throw new Error("組織選択フィールドに、挿入しようとした組織情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。"); }else{ correctFormattedValue=[{ code: fieldValue, name: organizations}]; } //グループ情報フィードに挿入しようとした値が適切な場合、correctFormattedValueに代入する }else if(fieldType === "GROUP_SELECT"){ //挿入する値がグループ情報から見つかれば、グループ名を格納 let groups=await this.setInputGroup(fieldValue); //グループ名が格納できている場合、グループコードとグループ名をcorrectFormattedValueに代入する if(!groups){ event.record[fieldCode]['error']="グループ選択フィールドに、挿入しようとしたグループ情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。"; throw new Error("グループ選択フィールドに、挿入しようとしたグループ情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。"); }else{ correctFormattedValue=[{ code: fieldValue, name: groups}]; } } } //保存成功イベントの場合、kintone async/await による非同期処理でフィールドに値を挿入する if(!event.type.includes('success')){ //条件式の結果がtrueかつ挿入する値が変換できた場合、フィールド(ラジオボタン・ドロップダウン・チェックボックス・複数選択・文字列一行・文字列複数行・リッチエディタ・数値・日付・日時・時刻)にセット if(conditionResult && (correctFormattedValue || correctValues)){ //条件式の結果がtureかつ、値を正しい形式に変換できた場合、フィールドに値をセットする if(correctFormattedValue){ event.record[fieldCode].value = correctFormattedValue; //条件式の結果がtureかつ、値を正しい形式(配列)に変換できた場合、フィールドに値(配列)をセットする }else if(correctValues.length > 0){ event.record[fieldCode].value = correctValues; } } }else{ //kintone async/await による非同期処理(保存成功時イベントREST API処理時) async function updateRecord(fieldCode:string,event:any,insertValue:any) { return new Promise((resolve, reject) => { var updatedRecord = { app: event.appId, id: event.recordId, record: {[fieldCode]:{"value":insertValue}} }; kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', updatedRecord, (resp) => { resolve(resp); }, (error) => { reject(error); }); }); } //条件式の結果がtrueかつ挿入する値が変換できた場合、フィールド(ラジオボタン・ドロップダウン・チェックボックス・複数選択・文字列一行・文字列複数行・リッチエディタ・数値・日付・日時・時刻)にセット if(conditionResult && (correctFormattedValue || correctValues)){ //条件式の結果がtureかつ、値を正しい形式に変換できた場合、フィールドに値をセットする if(correctFormattedValue){ event.record[fieldCode].value = correctFormattedValue; //条件式の結果がtureかつ、値を正しい形式(配列)に変換できた場合、フィールドに値(配列)をセットする }else if(correctValues.length > 0){ event.record[fieldCode].value = correctValues; } } //kintone async/await による非同期処理(レコード更新) await updateRecord(fieldCode,event,correctFormattedValue); }; result= { canNext:true, result:true } return result; }catch(error:any){ event.record; event.error=error.message; console.error(error); result.canNext=true;//次のノードは処理を続ける return result; } } /** * * @param context 条件式を実行する * @returns */ getConditionResult(context:any):boolean{ //プロパティ`condition`から条件ツリーを取得する const tree =this.getCondition(this.props.condition); if(!tree){ //条件を設定されていません return true; } return tree.evaluate(tree.root,context); } /** * @param condition 条件式ツリーを取得する * @returns */ getCondition(condition:string):ConditionTree|null{ try{ const tree = new ConditionTree(); tree.fromJson(condition); if(tree.getConditions(tree.root).length>0){ return tree; }else{ return null; } }catch(error){ return null; } } register(): void { actionAddins[this.name]=this; } } new InsertValueAction();