311 lines
9.0 KiB
TypeScript
311 lines
9.0 KiB
TypeScript
import {
|
|
IAction,
|
|
IActionResult,
|
|
IActionNode,
|
|
IActionProperty,
|
|
IContext,
|
|
IField,
|
|
} from "../types/ActionTypes";
|
|
|
|
import { actionAddins } from ".";
|
|
import type { Record} from "@kintone/rest-api-client/lib/src/client/types";
|
|
import { KintoneAllRecordsError, KintoneRestAPIClient} from "@kintone/rest-api-client";
|
|
import "./auto-lookup.scss";
|
|
import "bootstrap/js/dist/modal";
|
|
// import "bootstrap/js/dist/spinner";
|
|
import {Modal} from "bootstrap"
|
|
import $ from "jquery";
|
|
|
|
interface IAutoLookUpProps {
|
|
displayName: string;
|
|
lookupField: LookupField;
|
|
condition: Condition;
|
|
}
|
|
|
|
interface Condition {
|
|
queryString: string;
|
|
index: number;
|
|
type: string;
|
|
children: Child[];
|
|
parent: null;
|
|
logicalOperator: string;
|
|
}
|
|
|
|
interface Child {
|
|
index: number;
|
|
type: string;
|
|
parent: string;
|
|
object: any;
|
|
operator: string;
|
|
value: string;
|
|
}
|
|
|
|
interface LookupField {
|
|
app: App;
|
|
fields: Field[];
|
|
}
|
|
|
|
interface Field {
|
|
name: string;
|
|
type: string;
|
|
code: string;
|
|
label: string;
|
|
noLabel: boolean;
|
|
required: boolean;
|
|
lookup: Lookup;
|
|
}
|
|
|
|
interface Lookup {
|
|
relatedApp: RelatedApp;
|
|
relatedKeyField: string;
|
|
fieldMappings: FieldMapping[];
|
|
lookupPickerFields: any[];
|
|
filterCond: string;
|
|
sort: string;
|
|
}
|
|
|
|
interface FieldMapping {
|
|
field: string;
|
|
relatedField: string;
|
|
}
|
|
|
|
interface RelatedApp {
|
|
app: string;
|
|
code: string;
|
|
}
|
|
|
|
interface App {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
createdate: string;
|
|
}
|
|
|
|
export class AutoLookUpAction implements IAction {
|
|
name: string;
|
|
actionProps: IActionProperty[];
|
|
props: IAutoLookUpProps;
|
|
constructor() {
|
|
this.name = "ルックアップ更新";
|
|
this.actionProps = [];
|
|
this.props = {} as IAutoLookUpProps;
|
|
this.register();
|
|
}
|
|
|
|
/***
|
|
* アクセスのメインの処理関数
|
|
*/
|
|
async process(
|
|
actionNode: IActionNode,
|
|
event: any,
|
|
context: IContext
|
|
): Promise<IActionResult> {
|
|
this.actionProps = actionNode.actionProps;
|
|
this.props = {
|
|
...actionNode.ActionValue,
|
|
condition: JSON.parse((actionNode.ActionValue as any).condition),
|
|
} as IAutoLookUpProps;
|
|
// console.log(context);
|
|
|
|
let result = {
|
|
canNext: true,
|
|
result: "",
|
|
} as IActionResult;
|
|
try {
|
|
const lookUpFields = this.props.lookupField.fields.filter(
|
|
(f) => f.lookup && f.lookup.relatedApp.app === String(kintone.app.getId())
|
|
);
|
|
if (!lookUpFields || lookUpFields.length===0) {
|
|
throw new Error(
|
|
`ルックアップの設定は不正です。${this.props.lookupField.fields[0].label} `
|
|
);
|
|
}
|
|
const lookUpField = this.props.lookupField.fields[0];
|
|
const key = event.record[lookUpField.lookup.relatedKeyField].value;
|
|
const targetRecords = await this.getUpdateRecords(lookUpField, key);
|
|
//更新対象がない時にスキップ
|
|
if(targetRecords.length===0){
|
|
return result;
|
|
}
|
|
const updateRecords = this.convertForLookup(targetRecords,lookUpField,key);
|
|
console.log("updateRecords", updateRecords);
|
|
this.showSpinnerModel(this.props.lookupField.app,lookUpField);
|
|
const updateResult = await this.updateLookupTarget(updateRecords);
|
|
if(updateResult){
|
|
this.showResult(this.props.lookupField.app,lookUpField,updateRecords.length);
|
|
}
|
|
} catch (error) {
|
|
this.closeDialog();
|
|
context.errors.handleError(error,actionNode,"ルックアップ更新中例外が発生しました");
|
|
result.canNext = false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* REST API用クエリ作成
|
|
* TODO:共通関数として作成
|
|
* @param lookUpField
|
|
* @param key
|
|
* @returns
|
|
*/
|
|
makeQuery=(lookUpField:Field,key:any)=>{
|
|
let query ="";
|
|
if(typeof key==='number'){
|
|
query = `${lookUpField.code} = ${key}`
|
|
}
|
|
if(typeof key==='string'){
|
|
query = `${lookUpField.code} = "${key}"`
|
|
}
|
|
if(this.props.condition.queryString!==''){
|
|
query = `${query} and (${this.props.condition.queryString})`
|
|
}
|
|
return query;
|
|
}
|
|
|
|
/**
|
|
* 更新対象のレコードを取得する
|
|
*/
|
|
getUpdateRecords = async (lookUpField:Field,key:any):Promise< Record[]>=>{
|
|
const client=new KintoneRestAPIClient();
|
|
const resp = await client.record.getAllRecords({
|
|
app:this.props.lookupField.app.id,
|
|
fields:["$id"],
|
|
condition:this.makeQuery(lookUpField,key)
|
|
});
|
|
return resp;
|
|
}
|
|
|
|
/**
|
|
* ルックアップ更新用レコードに変換する
|
|
* @param targetRecords 更新対象レコード
|
|
* @param lookUpField ルックアップフィールド
|
|
* @param key ルックアップフィールドの値
|
|
* @returns
|
|
*/
|
|
convertForLookup = (targetRecords:Record[],lookUpField:Field,key:any):Array<any>=>{
|
|
return targetRecords.map((r) => ({
|
|
id: Number(r["$id"].value),
|
|
record: { [lookUpField.code]: { value: key } },
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* ルックアップ先を更新する
|
|
* @param updateRecords
|
|
*/
|
|
updateLookupTarget = async (updateRecords:Array<any>):Promise<boolean>=>{
|
|
if (updateRecords && updateRecords.length > 0) {
|
|
try{
|
|
const client=new KintoneRestAPIClient();
|
|
const result = await client.record.updateAllRecords({
|
|
app:this.props.lookupField.app.id,
|
|
records:updateRecords
|
|
});
|
|
return true;
|
|
}catch(error ){
|
|
if(error instanceof KintoneAllRecordsError){
|
|
this.showError(this.props.lookupField.app,
|
|
this.props.lookupField.fields[0],
|
|
error as KintoneAllRecordsError,updateRecords.length);
|
|
return false;
|
|
}else{
|
|
throw error;
|
|
}
|
|
}
|
|
// await kintone.api(kintone.api.url("/k/v1/records.json", true), "PUT", {
|
|
// app: this.props.lookupField.app.id,
|
|
// records: updateRecords
|
|
// });
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 更新中のダイアログ表示
|
|
* @param app
|
|
*/
|
|
showSpinnerModel = (app:App,lookup:Field) => {
|
|
let dialog = $("#alcLookupModal");
|
|
if(dialog.length===0){
|
|
const modalHTML = `<div class="bs-scope">
|
|
<div class="modal" id="alcLookupModal" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
|
<div class="modal-dialog-centered">
|
|
<div class="modal-dialog modal-content">
|
|
<div class="modal-header">
|
|
<h1 class="modal-title fs-5" id="alcLookupModalLabel">ルックアップ同期処理</h1>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row" id="app${app.id}_${lookup.code}">
|
|
<div class="spinner-border text-secondary col-1 " role="alert"></div>
|
|
<div class="col">${app.name}</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">OK</button>
|
|
</div>
|
|
</div></div></div></div>`;
|
|
$(modalHTML).appendTo("body");
|
|
dialog = $("#alcLookupModal");
|
|
dialog.get()[0].addEventListener('hidden.bs.modal',(ev)=>{
|
|
Modal.getOrCreateInstance(dialog.get()[0]).dispose();
|
|
$("#alcLookupModal").parent().remove();
|
|
});
|
|
}else{
|
|
const dialogBody=$("#alcLookupModal .modal-body");
|
|
const htmlrow=`
|
|
<div class="row" id="app${app.id}_${lookup.code}">
|
|
<div class="spinner-border text-secondary col-1 " role="alert">
|
|
</div>
|
|
<div class="col">${app.name}</div>
|
|
<div>`;
|
|
dialogBody.append(htmlrow);
|
|
}
|
|
Modal.getOrCreateInstance(dialog.get()[0]).show();
|
|
}
|
|
|
|
/**
|
|
* 更新結果を表示する
|
|
* @param app 更新先アプリ情報
|
|
* @param count 更新件数
|
|
*/
|
|
showResult=(app:App,lookup:Field,count:number)=>{
|
|
const dialogBody=$(`#alcLookupModal .modal-body #app${app.id}_${lookup.code}`);
|
|
const html=` <div class="col-1 text-success">✔</div>
|
|
<div class="col">${app.name}</div>
|
|
<div class="col">更新件数:${count}件</div>`;
|
|
dialogBody.html(html);
|
|
}
|
|
|
|
/**
|
|
* 更新結果を表示する
|
|
* @param app 更新先アプリ情報
|
|
* @param count 更新件数
|
|
*/
|
|
showError=(app:App,lookup:Field,error:KintoneAllRecordsError,allCount:Number)=>{
|
|
const message=error.error.message;
|
|
const proRecords = error.numOfProcessedRecords;
|
|
const allRecords=error.numOfAllRecords;
|
|
const dialogBody=$(`#alcLookupModal .modal-body #app${app.id}_${lookup.code}`);
|
|
const html=`<div class="col-1 text-danger">✖</div>
|
|
<div class="col">${app.name}</div>
|
|
<div class="col">更新件数:${proRecords}/${allRecords}</div>
|
|
<div class="row text-danger">${message}<div>`;
|
|
dialogBody.html(html);
|
|
}
|
|
/**
|
|
* ダイアログ画面を閉じる
|
|
*/
|
|
closeDialog=()=>{
|
|
const dialog = $("#alcLookupModal");
|
|
Modal.getOrCreateInstance(dialog.get()[0]).dispose();
|
|
$("#alcLookupModal").parent().remove();
|
|
}
|
|
|
|
register(): void {
|
|
actionAddins[this.name] = this;
|
|
}
|
|
}
|
|
|
|
new AutoLookUpAction(); |