Compare commits
10 Commits
feature-co
...
mvp_step2_
| Author | SHA1 | Date | |
|---|---|---|---|
| 58e22dc55f | |||
| f861955b51 | |||
| 1e7d553bd6 | |||
| 35ae2539cb | |||
|
|
3e73799532 | ||
| 3159366560 | |||
| 5176cff2bd | |||
| 978aa723ae | |||
| 926c338f73 | |||
| 6ed17a50e5 |
@@ -9,7 +9,7 @@ import app.core.config as config
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from app.db.session import SessionLocal
|
from app.db.session import SessionLocal
|
||||||
from app.db.crud import get_flows_by_app,get_activedomain
|
from app.db.crud import get_flows_by_app,get_activedomain,get_kintoneformat
|
||||||
from app.core.auth import get_current_active_user,get_current_user
|
from app.core.auth import get_current_active_user,get_current_user
|
||||||
from app.core.apiexception import APIException
|
from app.core.apiexception import APIException
|
||||||
|
|
||||||
@@ -23,28 +23,134 @@ def getkintoneenv(user = Depends(get_current_user)):
|
|||||||
return kintoneevn
|
return kintoneevn
|
||||||
|
|
||||||
|
|
||||||
def getfieldsfromexcel(df):
|
def getkintoneformat():
|
||||||
|
db = SessionLocal()
|
||||||
|
formats = get_kintoneformat(db)
|
||||||
|
db.close()
|
||||||
|
return formats
|
||||||
|
|
||||||
|
|
||||||
|
def createkintonefields(property,value,trueformat):
|
||||||
|
if(type(value) == str):
|
||||||
|
value = value.replace("\"","⊡⊡")
|
||||||
|
p = []
|
||||||
|
if(property=="options"):
|
||||||
|
o=[]
|
||||||
|
for v in value.split(','):
|
||||||
|
o.append(f"\"{v.split('|')[0]}\":{{\"label\":\"{v.split('|')[0]}\",\"index\":\"{v.split('|')[1]}\"}}")
|
||||||
|
p.append(f"\"options\":{{{','.join(o)}}}")
|
||||||
|
elif(property =="expression"):
|
||||||
|
p.append(f"\"hideExpression\":true")
|
||||||
|
p.append(f"\"expression\":\"{value.split(':')[1]}\"")
|
||||||
|
elif(property =="required" or property =="unique" or property =="defaultNowValue" or property =="hideExpression" or property =="digit"):
|
||||||
|
if str(value) == trueformat:
|
||||||
|
p.append(f"\"{property}\":true")
|
||||||
|
else:
|
||||||
|
p.append(f"\"{property}\":false")
|
||||||
|
elif(property =="protocol"):
|
||||||
|
if(value == "メールアドレス"):
|
||||||
|
p.append("\"protocol\":\"MAIL\"")
|
||||||
|
elif(value == "Webサイト"):
|
||||||
|
p.append("\"protocol\":\"WEB\"")
|
||||||
|
elif(value == "電話番号"):
|
||||||
|
p.append("\"protocol\":\"CALL\"")
|
||||||
|
else:
|
||||||
|
p.append(f"\"{property}\":\"{value}\"")
|
||||||
|
return p
|
||||||
|
|
||||||
|
def getfieldsfromexcel(df,mapping):
|
||||||
|
startrow = mapping.startrow
|
||||||
|
startcolumn = mapping.startcolumn
|
||||||
|
typecolumn = mapping.typecolumn
|
||||||
|
codecolumn = mapping.codecolumn
|
||||||
|
property = mapping.field.split(",")
|
||||||
|
trueformat = mapping.trueformat
|
||||||
appname = df.iloc[0,2]
|
appname = df.iloc[0,2]
|
||||||
col=[]
|
col=[]
|
||||||
for row in range(5,len(df)):
|
for row in range(startrow,len(df)):
|
||||||
if pd.isna(df.iloc[row,1]):
|
if pd.isna(df.iloc[row,startcolumn]):
|
||||||
break
|
break
|
||||||
if not df.iloc[row,3] in config.KINTONE_FIELD_TYPE:
|
if not df.iloc[row,typecolumn] in config.KINTONE_FIELD_TYPE:
|
||||||
continue
|
continue
|
||||||
p=[]
|
p=[]
|
||||||
for column in range(1,7):
|
for column in range(startcolumn,startcolumn + len(property)):
|
||||||
if(not pd.isna(df.iloc[row,column])):
|
if(not pd.isna(df.iloc[row,column])):
|
||||||
if(property[column-1]=="options"):
|
propertyname =property[column-1]
|
||||||
o=[]
|
if(propertyname.find("[") == 0):
|
||||||
for v in df.iloc[row,column].split(','):
|
continue
|
||||||
o.append(f"\"{v.split('|')[0]}\":{{\"label\":\"{v.split('|')[0]}\",\"index\":\"{v.split('|')[1]}\"}}")
|
elif (propertyname =="remark"):
|
||||||
p.append(f"\"{property[column-1]}\":{{{','.join(o)}}}")
|
if (df.iloc[row,column].find("|") !=-1):
|
||||||
elif(property[column-1]=="required"):
|
propertyname = "options"
|
||||||
p.append(f"\"{property[column-1]}\":{df.iloc[row,column]}")
|
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||||
|
if (df.iloc[row,column] == "メールアドレス" or df.iloc[row,column] == "Webサイト" or df.iloc[row,column] == "電話番号"):
|
||||||
|
propertyname = "protocol"
|
||||||
|
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||||
|
if (df.iloc[row,column].find("桁区切り") !=-1):
|
||||||
|
propertyname = "digit"
|
||||||
|
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||||
|
if (df.iloc[row,column].find("前単位") !=-1):
|
||||||
|
propertyname = "unitPosition"
|
||||||
|
p = p + createkintonefields(propertyname, "BEFORE",trueformat)
|
||||||
|
if (df.iloc[row,column].find("後単位") !=-1):
|
||||||
|
propertyname = "unitPosition"
|
||||||
|
p = p + createkintonefields(propertyname, "AFTER",trueformat)
|
||||||
|
if (df.iloc[row,column].find("単位「") !=-1):
|
||||||
|
propertyname = "unit"
|
||||||
|
ids = df.iloc[row,column].index("単位「")
|
||||||
|
ide = df.iloc[row,column].index("」")
|
||||||
|
unit = df.iloc[row,column][ids+3:ide]
|
||||||
|
p = p + createkintonefields(propertyname, unit,trueformat)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
elif(propertyname =="mixValue"):
|
||||||
|
if(df.iloc[row,column].find("レコード登録時の日") != -1):
|
||||||
|
propertyname = "defaultNowValue"
|
||||||
|
df.iloc[row,column] = trueformat
|
||||||
|
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||||
|
elif(df.iloc[row,column].find("計:") != -1):
|
||||||
|
propertyname = "expression"
|
||||||
|
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||||
|
elif(df.iloc[row,column] !=""):
|
||||||
|
propertyname = "defaultValue"
|
||||||
|
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
elif(propertyname=="max" or propertyname == "min"):
|
||||||
|
if(df.iloc[row,typecolumn] == "NUMBER"):
|
||||||
|
propertyname = property[column-1] + "Value"
|
||||||
|
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||||
|
else:
|
||||||
|
propertyname = property[column-1] + "Length"
|
||||||
|
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||||
else:
|
else:
|
||||||
p.append(f"\"{property[column-1]}\":\"{df.iloc[row,column]}\"")
|
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||||
col.append(f"\"{df.iloc[row,2]}\":{{{','.join(p)}}}")
|
|
||||||
fields = ",".join(col).replace("False","false").replace("True","true")
|
# if(propertyname=="options"):
|
||||||
|
# o=[]
|
||||||
|
# for v in df.iloc[row,column].split(','):
|
||||||
|
# o.append(f"\"{v.split('|')[0]}\":{{\"label\":\"{v.split('|')[0]}\",\"index\":\"{v.split('|')[1]}\"}}")
|
||||||
|
# p.append(f"\"options\":{{{','.join(o)}}}")
|
||||||
|
# elif(propertyname=="expression"):
|
||||||
|
# p.append(f"\"hideExpression\":true")
|
||||||
|
# p.append(f"\"expression\":{df.iloc[row,column].split(':')[1]}")
|
||||||
|
# elif(propertyname=="required" or propertyname =="unique" or propertyname=="defaultNowValue" or propertyname=="hideExpression" or propertyname=="digit"):
|
||||||
|
# if (df.iloc[row,column] == trueformat):
|
||||||
|
# p.append(f"\"{propertyname}\":true")
|
||||||
|
# else:
|
||||||
|
# p.append(f"\"{propertyname}\":false")
|
||||||
|
# elif(propertyname =="protocol"):
|
||||||
|
# if(df.iloc[row,column] == "メールアドレス"):
|
||||||
|
# p.append("\"protocol\":\"MAIL\"")
|
||||||
|
# elif(df.iloc[row,column] == "Webサイト"):
|
||||||
|
# p.append("\"protocol\":\"WEB\"")
|
||||||
|
# elif(df.iloc[row,column] == "電話番号"):
|
||||||
|
# p.append("\"protocol\":\"CALL\"")
|
||||||
|
# else:
|
||||||
|
# p.append(f"\"{propertyname}\":\"{df.iloc[row,column]}\"")
|
||||||
|
|
||||||
|
|
||||||
|
col.append(f"\"{df.iloc[row,codecolumn]}\":{{{','.join(p)}}}")
|
||||||
|
fields = ",".join(col).replace("\\", "\\\\").replace("⊡⊡","\\\"")
|
||||||
return json.loads(f"{{{fields}}}")
|
return json.loads(f"{{{fields}}}")
|
||||||
|
|
||||||
def getsettingfromexcel(df):
|
def getsettingfromexcel(df):
|
||||||
@@ -129,8 +235,8 @@ def analysefields(excel,kintone):
|
|||||||
adds = excel.keys() - kintone.keys()
|
adds = excel.keys() - kintone.keys()
|
||||||
dels = kintone.keys() - excel.keys()
|
dels = kintone.keys() - excel.keys()
|
||||||
for key in updates:
|
for key in updates:
|
||||||
for p in property:
|
for p in config.KINTONE_FIELD_PROPERTY:
|
||||||
if excel[key].get(p) != None and kintone[key][p] != excel[key][p]:
|
if excel[key].get(p) != None and kintone[key].get(p) != None and kintone[key][p] != excel[key][p]:
|
||||||
updatefields[key] = excel[key]
|
updatefields[key] = excel[key]
|
||||||
break
|
break
|
||||||
for key in adds:
|
for key in adds:
|
||||||
@@ -412,10 +518,14 @@ async def createapp(request:Request,name:str,c:config.KINTONE_ENV=Depends(getkin
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise APIException('kintone:createapp',request.url._url, f"Error occurred while create app({c.DOMAIN_NAM}->{name}):",e)
|
raise APIException('kintone:createapp',request.url._url, f"Error occurred while create app({c.DOMAIN_NAM}->{name}):",e)
|
||||||
|
|
||||||
property=["label","code","type","required","defaultValue","options"]
|
|
||||||
|
|
||||||
@r.post("/createappfromexcel",)
|
@r.post("/createappfromexcel",)
|
||||||
async def createappfromexcel(request:Request,files:t.List[UploadFile] = File(...),env = Depends(getkintoneenv)):
|
async def createappfromexcel(request:Request,files:t.List[UploadFile] = File(...),format:int = 0,env = Depends(getkintoneenv)):
|
||||||
|
try:
|
||||||
|
mapping = getkintoneformat()[format]
|
||||||
|
except Exception as e:
|
||||||
|
raise APIException('kintone:createappfromexcel',request.url._url, f"Error occurred while get kintone format:",e)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.filename.endswith('.xlsx'):
|
if file.filename.endswith('.xlsx'):
|
||||||
try:
|
try:
|
||||||
@@ -425,7 +535,7 @@ async def createappfromexcel(request:Request,files:t.List[UploadFile] = File(...
|
|||||||
appname = df.iloc[0,2]
|
appname = df.iloc[0,2]
|
||||||
desc = df.iloc[2,2]
|
desc = df.iloc[2,2]
|
||||||
result = {"app":0,"revision":0,"msg":""}
|
result = {"app":0,"revision":0,"msg":""}
|
||||||
fields = getfieldsfromexcel(df)
|
fields = getfieldsfromexcel(df,mapping)
|
||||||
users = getkintoneusers(env)
|
users = getkintoneusers(env)
|
||||||
orgs = getkintoneorgs(env)
|
orgs = getkintoneorgs(env)
|
||||||
processes = getprocessfromexcel(df,users["users"], orgs["organizationTitles"])
|
processes = getprocessfromexcel(df,users["users"], orgs["organizationTitles"])
|
||||||
@@ -436,20 +546,25 @@ async def createappfromexcel(request:Request,files:t.List[UploadFile] = File(...
|
|||||||
if app.get("revision") != None:
|
if app.get("revision") != None:
|
||||||
result["revision"] = app["revision"]
|
result["revision"] = app["revision"]
|
||||||
app = addfieldstokintone(result["app"],fields,env)
|
app = addfieldstokintone(result["app"],fields,env)
|
||||||
if len(processes)> 0:
|
if len(processes["states"])> 0:
|
||||||
app = updateprocesstokintone(result["app"],processes,env)
|
app = updateprocesstokintone(result["app"],processes,env)
|
||||||
if app.get("revision") != None:
|
if app.get("revision") != None:
|
||||||
result["revision"] = app["revision"]
|
result["revision"] = app["revision"]
|
||||||
deoployappfromkintone(result["app"],result["revision"],env)
|
deoployappfromkintone(result["app"],result["revision"],env)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise APIException('kintone:createappfromexcel',request.url._url, f"Error occurred while parsing file ({env.DOMAIN_NAM}->{file.filename}):",e)
|
raise APIException('kintone:createappfromexcel',request.url._url, f"Error occurred while parsing file ({env.DOMAIN_NAME}->{file.filename}):",e)
|
||||||
else:
|
else:
|
||||||
raise APIException('kintone:createappfromexcel',request.url._url, f"File {file.filename} is not an Excel file",e)
|
raise APIException('kintone:createappfromexcel',request.url._url, f"File {file.filename} is not an Excel file",e)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@r.post("/updateappfromexcel")
|
@r.post("/updateappfromexcel")
|
||||||
async def updateappfromexcel(request:Request,app:str,files:t.List[UploadFile] = File(...),env = Depends(getkintoneenv)):
|
async def updateappfromexcel(request:Request,app:str,files:t.List[UploadFile] = File(...),format:int = 0,env = Depends(getkintoneenv)):
|
||||||
|
try:
|
||||||
|
mapping = getkintoneformat()[format]
|
||||||
|
except Exception as e:
|
||||||
|
raise APIException('kintone:updateappfromexcel',request.url._url, f"Error occurred while get kintone format:",e)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.filename.endswith('.xlsx'):
|
if file.filename.endswith('.xlsx'):
|
||||||
try:
|
try:
|
||||||
@@ -458,7 +573,7 @@ async def updateappfromexcel(request:Request,app:str,files:t.List[UploadFile] =
|
|||||||
excel = getsettingfromexcel(df)
|
excel = getsettingfromexcel(df)
|
||||||
kintone= getsettingfromkintone(app,env)
|
kintone= getsettingfromkintone(app,env)
|
||||||
settings = analysesettings(excel,kintone)
|
settings = analysesettings(excel,kintone)
|
||||||
excel = getfieldsfromexcel(df)
|
excel = getfieldsfromexcel(df,mapping)
|
||||||
kintone = getfieldsfromkintone(app,env)
|
kintone = getfieldsfromkintone(app,env)
|
||||||
users = getkintoneusers(env)
|
users = getkintoneusers(env)
|
||||||
orgs = getkintoneorgs(env)
|
orgs = getkintoneorgs(env)
|
||||||
@@ -493,7 +608,7 @@ async def updateappfromexcel(request:Request,app:str,files:t.List[UploadFile] =
|
|||||||
if deploy:
|
if deploy:
|
||||||
result = deoployappfromkintone(app,revision,env)
|
result = deoployappfromkintone(app,revision,env)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise APIException('kintone:updateappfromexcel',request.url._url, f"Error occurred while parsing file ({env.DOMAIN_NAM}->{file.filename}):",e)
|
raise APIException('kintone:updateappfromexcel',request.url._url, f"Error occurred while parsing file ({env.DOMAIN_NAME}->{file.filename}):",e)
|
||||||
else:
|
else:
|
||||||
raise APIException('kintone:updateappfromexcel',request.url._url, f"File {file.filename} is not an Excel file",e)
|
raise APIException('kintone:updateappfromexcel',request.url._url, f"File {file.filename} is not an Excel file",e)
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ DEPLOY_JS_URL = "https://ka-addin.azurewebsites.net/alc_runtime.js"
|
|||||||
|
|
||||||
KINTONE_FIELD_TYPE=["GROUP","GROUP_SELECT","CHECK_BOX","SUBTABLE","DROP_DOWN","USER_SELECT","RADIO_BUTTON","RICH_TEXT","LINK","REFERENCE_TABLE","CALC","TIME","NUMBER","ORGANIZATION_SELECT","FILE","DATETIME","DATE","MULTI_SELECT","SINGLE_LINE_TEXT","MULTI_LINE_TEXT"]
|
KINTONE_FIELD_TYPE=["GROUP","GROUP_SELECT","CHECK_BOX","SUBTABLE","DROP_DOWN","USER_SELECT","RADIO_BUTTON","RICH_TEXT","LINK","REFERENCE_TABLE","CALC","TIME","NUMBER","ORGANIZATION_SELECT","FILE","DATETIME","DATE","MULTI_SELECT","SINGLE_LINE_TEXT","MULTI_LINE_TEXT"]
|
||||||
|
|
||||||
|
KINTONE_FIELD_PROPERTY=['label','code','type','required','unique','maxValue','minValue','maxLength','minLength','defaultValue','defaultNowValue','options','expression','hideExpression','digit','protocol','displayScale','unit','unitPosition']
|
||||||
|
|
||||||
class KINTONE_ENV:
|
class KINTONE_ENV:
|
||||||
|
|
||||||
BASE_URL = ""
|
BASE_URL = ""
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ def create_access_token(*, data: dict, expires_delta: timedelta = None):
|
|||||||
if expires_delta:
|
if expires_delta:
|
||||||
expire = datetime.utcnow() + expires_delta
|
expire = datetime.utcnow() + expires_delta
|
||||||
else:
|
else:
|
||||||
expire = datetime.utcnow() + timedelta(minutes=15)
|
expire = datetime.utcnow() + timedelta(minutes=2880)
|
||||||
to_encode.update({"exp": expire})
|
to_encode.update({"exp": expire})
|
||||||
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||||
return encoded_jwt
|
return encoded_jwt
|
||||||
|
|||||||
@@ -290,4 +290,8 @@ def create_log(db: Session, error:schemas.ErrorCreate):
|
|||||||
db.add(db_log)
|
db.add(db_log)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(db_log)
|
db.refresh(db_log)
|
||||||
return db_log
|
return db_log
|
||||||
|
|
||||||
|
def get_kintoneformat(db: Session):
|
||||||
|
formats = db.query(models.KintoneFormat).order_by(models.KintoneFormat.id).all()
|
||||||
|
return formats
|
||||||
@@ -95,7 +95,18 @@ class EventAction(Base):
|
|||||||
|
|
||||||
class ErrorLog(Base):
|
class ErrorLog(Base):
|
||||||
__tablename__ = "errorlog"
|
__tablename__ = "errorlog"
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
|
||||||
title = Column(String(50))
|
title = Column(String(50))
|
||||||
location = Column(String(500))
|
location = Column(String(500))
|
||||||
content = Column(String(5000))
|
content = Column(String(5000))
|
||||||
|
|
||||||
|
class KintoneFormat(Base):
|
||||||
|
__tablename__ = "kintoneformat"
|
||||||
|
|
||||||
|
name = Column(String(50))
|
||||||
|
startrow =Column(Integer)
|
||||||
|
startcolumn =Column(Integer)
|
||||||
|
typecolumn =Column(Integer)
|
||||||
|
codecolumn =Column(Integer)
|
||||||
|
field = Column(String(5000))
|
||||||
|
trueformat = Column(String(10))
|
||||||
BIN
document/Kintone自動作成ツールのプラグインについて.xlsx
Normal file
BIN
document/Kintone自動作成ツールのプラグインについて.xlsx
Normal file
Binary file not shown.
BIN
document/action-property.png
Normal file
BIN
document/action-property.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
266
plugin/kintone-addins/readme.md
Normal file
266
plugin/kintone-addins/readme.md
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
# kintone自動化開発ツールのアクションのアドイン開発手順
|
||||||
|
|
||||||
|
## 1. アクションの登録
|
||||||
|
|
||||||
|
アクションプラグインをシステムに登録するためには、以下の情報をデータベースの`action`表に挿入する必要があります。
|
||||||
|
|
||||||
|
|列名 | 項目 | 説明 |
|
||||||
|
|----- |-------------|-------------------------------------------|
|
||||||
|
|name | 名前 | アクションプラグイン名(ユニークな名前が必要) |
|
||||||
|
|title |タイトル | タイトル (20文字以内) |
|
||||||
|
|subtitle|サブタイトル | サブタイトル |
|
||||||
|
|outputpoint|出力ポイント | 出力値に分岐がある場合の接続点 |
|
||||||
|
|property|プロパティ | アクションプラグインの属性(json形式) |
|
||||||
|
|
||||||
|
### 登録の例
|
||||||
|
|
||||||
|
以下は「表示/非表示」アクションプラグインを登録する例です。
|
||||||
|
|
||||||
|
- name: "表示/非表示"
|
||||||
|
- title: "指定項目の表示・非表示を設定する"
|
||||||
|
- subtitle: "表示/非表示"
|
||||||
|
- outputpoint: "[]"
|
||||||
|
- property:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"component": "FieldInput",
|
||||||
|
"props": {
|
||||||
|
"displayName": "フィールド",
|
||||||
|
"modelValue": {},
|
||||||
|
"name": "field",
|
||||||
|
"placeholder": "対象項目を選択してください"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "SelectBox",
|
||||||
|
"props": {
|
||||||
|
"displayName": "表示/非表示",
|
||||||
|
"options": ["表示", "非表示"],
|
||||||
|
"modelValue": "",
|
||||||
|
"name": "show",
|
||||||
|
"placeholder": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "ConditionInput",
|
||||||
|
"props": {
|
||||||
|
"displayName": "条件",
|
||||||
|
"modelValue": "",
|
||||||
|
"name": "condition",
|
||||||
|
"placeholder": "条件式を設定してください"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### プロパティ属性設定画面
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### 属性UIコンポーネントの共通属性
|
||||||
|
| 属性 | 設定値の例 | 説明 |
|
||||||
|
|-------------|--------------------|----------------------------------------------------------------------------|
|
||||||
|
| component | InputText | コンポーネントの種類を示しており、この場合は選択リストを意味します。<br>使用可能なコンポーネントを参照|
|
||||||
|
| displayName | 表示/非表示 | ユーザーに対して表示されるコンポーネントの名前です。 |
|
||||||
|
| options | ["表示", "非表示"] | ユーザーが選択できるオプションの配列です。<br>SelectBoxのみ使用可能 |
|
||||||
|
| modelValue | 空文字 | コンポーネントの初期値を設定します。<br>初期設定ないの場合は空文字で設定する。
|
||||||
|
| name | field | 属性の設定値の名前です。 |
|
||||||
|
| placeholder | 対象項目を選択してください| 入力フィールドに表示されるプレースホルダーのテキストです。この場合は設定されていません。 |
|
||||||
|
|
||||||
|
### 使用可能なコンポーネント
|
||||||
|
| No. | コンポーネント名 | コンポーネントタイプ | 説明 |
|
||||||
|
|-----|------------------|------------------|-----------------------------------------|
|
||||||
|
| 1 | テキストボックス | InputText | 一行のテキスト入力が可能なフィールドです。 |
|
||||||
|
| 2 | テキストボックス(改行可能) | MuiltInputText | 複数行のテキスト入力が可能なテキストエリアです。 |
|
||||||
|
| 3 | 日付 | DatePicker | 日付を選択するためのカレンダーコンポーネントです。 |
|
||||||
|
| 4 | フィールド選択 | FieldInput | システムのフィールドを選択するための入力コンポーネントです。 |
|
||||||
|
| 5 | 選択リスト | SelectBox | 複数のオプションから選択するためのドロップダウンリストです。 |
|
||||||
|
| 6 | 条件式設定 | ConditionInput | 条件式やロジックを入力するためのコンポーネントです。 |
|
||||||
|
| 7 | 色選択 | ColorPicker | 色を設定する(追加予定中) |
|
||||||
|
| 8 | 他のアプリのフィールド選択 | AppFieldPicker | 他のアプリのフィールドを選択する(追加予定中) |
|
||||||
|
| 9 |ユーザー選択 | UserPicker | ユーザーを選択する(追加予定中) |
|
||||||
|
|
||||||
|
## 2.アクションアドインの開発
|
||||||
|
|
||||||
|
### 1. Action pluginファイルの追加
|
||||||
|
アクションプラグインを作成するためには、以下のディレクトリ構造に`TypeScript`ファイルを追加します。
|
||||||
|
```
|
||||||
|
KintoneAppBuilder
|
||||||
|
└─ plugin
|
||||||
|
└─ kintone-addins
|
||||||
|
└─ src
|
||||||
|
└─ actions
|
||||||
|
└─ your-action.ts // ここにアクションプラグインのtsファイルを追加
|
||||||
|
```
|
||||||
|
### 2. アクションクラスの実装手順
|
||||||
|
`IAction` インターフェースに従ってアクションクラスを実装します。
|
||||||
|
```typescript
|
||||||
|
|
||||||
|
/**
|
||||||
|
* アクションのインターフェース
|
||||||
|
*/
|
||||||
|
export interface IAction{
|
||||||
|
// アクションのユニークな名前
|
||||||
|
name:string;
|
||||||
|
//属性設定情報
|
||||||
|
actionProps: Array<IActionProperty>;
|
||||||
|
//アクションのプロセス実行関数
|
||||||
|
process(prop:IActionNode,event:any,context:IContext):Promise<IActionResult>;
|
||||||
|
//アクションの登録関数
|
||||||
|
register():void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
#### サンプルコード
|
||||||
|
```ts
|
||||||
|
// アクションの属性定義
|
||||||
|
interface IShownProps{
|
||||||
|
field:IField;
|
||||||
|
show:string;
|
||||||
|
condition:string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表示/非表示アクション
|
||||||
|
export class FieldShownAction implements IAction{
|
||||||
|
name: string;
|
||||||
|
actionProps: IActionProperty[];
|
||||||
|
props:IShownProps;
|
||||||
|
constructor(){
|
||||||
|
this.name="表示/非表示"; // DBに登録したアクション名一致する必要があり
|
||||||
|
this.actionProps=[];
|
||||||
|
//プロパティ属性の初期化
|
||||||
|
this.props={
|
||||||
|
field:{code:''},
|
||||||
|
show:'',
|
||||||
|
condition:''
|
||||||
|
}
|
||||||
|
//アクションの自動登録
|
||||||
|
this.register();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* アクションの実行を呼び出す
|
||||||
|
* @param actionNode
|
||||||
|
* @param event
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async process(actionNode:IActionNode,event:any,context:IContext):Promise<IActionResult> {
|
||||||
|
// ... (アクション処理の実装)
|
||||||
|
}
|
||||||
|
|
||||||
|
register(): void {
|
||||||
|
actionAddins[this.name]=this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new FieldShownAction();
|
||||||
|
|
||||||
|
```
|
||||||
|
アクションプラグインを実装するには、`IAction`インターフェースの定義に従って、必要なメソッドとプロパティをクラスに実装します。
|
||||||
|
以下に、`IAction`インターフェースを用いて`表示/非表示`アクションを実装する手順を説明します。
|
||||||
|
1. **アクションの属性定義**
|
||||||
|
|
||||||
|
2. **アクションクラスの作成**:
|
||||||
|
- `IAction`インターフェースを実装する新しいクラス`FieldShownAction`を作成します。
|
||||||
|
|
||||||
|
3. **コンストラクタの定義**:
|
||||||
|
- アクション名や初期プロパティを設定します。
|
||||||
|
- このクラスのインスタンスが作成された際に、自動的にアクションが登録されるように、コンストラクタ内で`register`メソッドを呼び出します。
|
||||||
|
|
||||||
|
4. **プロセス実行関数の実装** (`process`):
|
||||||
|
- `process`メソッドは、アクションの主要なロジックを含み、アクションの実行時に呼び出されます。
|
||||||
|
|
||||||
|
|
||||||
|
- * 以下は`process`関数のパラメータとその用途を説明します。
|
||||||
|
|
||||||
|
| パラメータ名 | 型 | 用途 |
|
||||||
|
|----------|----------------|------------------------------------------------------------------------------------------------|
|
||||||
|
| actionNode | `IActionNode` | Kintone自動化ツールのアクションの設定やプロパティ情報を保持します。 |
|
||||||
|
| event |kintoneのイベント情報| レコードやエラー制御で使用します |
|
||||||
|
| context | `IContext` | 現在のレコード情報や変数など、実行に必要なデータへのアクセスを提供します。 |
|
||||||
|
|
||||||
|
- このメソッド内で、アクションに必要な処理を行います。
|
||||||
|
- 1. アクションプロパティの取得:
|
||||||
|
`Kitone自動化ツール`を設定したプロパティの値を取得する
|
||||||
|
|
||||||
|
```ts
|
||||||
|
//プロパティ設定を取得する
|
||||||
|
this.actionProps=actionNode.actionProps;
|
||||||
|
//プロパティ設定のデータ型は必要な情報が含めますか
|
||||||
|
if (!('field' in actionNode.ActionValue) && !('show' in actionNode.ActionValue)) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
//既定のプロパティのインターフェースへ変換する
|
||||||
|
this.props = actionNode.ActionValue as IShownProps;
|
||||||
|
```
|
||||||
|
|
||||||
|
- 2. 条件式の評価
|
||||||
|
getConditionResult関数を呼び出して条件式を評価します。この関数は、現在のコンテキストに基づいて条件式が真か偽かを返します。
|
||||||
|
```ts
|
||||||
|
//条件式の計算結果を取得
|
||||||
|
const conditionResult = this.getConditionResult(context);
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 3. Kintone APIを使用して、フィールドの表示/非表示の制御
|
||||||
|
```ts
|
||||||
|
//条件式の計算結果を取得
|
||||||
|
const conditionResult = this.getConditionResult(context);
|
||||||
|
if(conditionResult){
|
||||||
|
if(this.props.show==='表示'){
|
||||||
|
kintone.app.record.setFieldShown(this.props.field.code,true);
|
||||||
|
}else if (this.props.show==='非表示'){
|
||||||
|
kintone.app.record.setFieldShown(this.props.field.code,false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
5. **登録関数の実装** (`register`):
|
||||||
|
- アクションをアドインシステムに登録するための`register`メソッドを実装します。
|
||||||
|
|
||||||
|
6. **アクションプロセス`ActionProcess`に参照追加**
|
||||||
|
```ts
|
||||||
|
import { actionAddins } from "../actions";
|
||||||
|
import '../actions/must-input';
|
||||||
|
import '../actions/auto-numbering';
|
||||||
|
import '../actions/field-shown';
|
||||||
|
import '../actions/your-action'; //ここに新規のアクションの参照を追加する
|
||||||
|
...
|
||||||
|
```
|
||||||
|
### 3. デプロイ
|
||||||
|
1. **プロジェクトをビルドする**
|
||||||
|
- 本番環境にデプロイする場合
|
||||||
|
```bash
|
||||||
|
cd plug\kintone-addins\
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
- 開発環境にデプロイする場合(ソースマップ出力ます)
|
||||||
|
```bash
|
||||||
|
cd plug\kintone-addins\
|
||||||
|
npm install
|
||||||
|
npm run build:dev
|
||||||
|
```
|
||||||
|
2. **Azureにデプロイする**
|
||||||
|
- Azure 拡張機能のインストール:
|
||||||
|
VSCode の拡張機能ペインで`Azure Tools`を検索し、インストールします。
|
||||||
|
|
||||||
|
- Azure にログイン:
|
||||||
|
- Azure Account 拡張機能を使用して Azure にログインします。
|
||||||
|
|
||||||
|
- Azure へのデプロイ:
|
||||||
|
- 「Deploy to Web App」オプションを使用し、デプロイするファイルやフォルダを指定します。
|
||||||
|
|
||||||
|
- デプロイの確認:
|
||||||
|
- Azure App Service 拡張機能でデプロイが完了したことを確認します。
|
||||||
|
- ka-addin の URL にアクセスしてアプリケーションが正常に動作しているか確認します。
|
||||||
97
plugin/kintone-addins/src/actions/error-show.ts
Normal file
97
plugin/kintone-addins/src/actions/error-show.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
|
||||||
|
import { actionAddins } from ".";
|
||||||
|
import { IAction, IActionProperty, IActionNode, IContext, IActionResult } from "../types/ActionTypes";
|
||||||
|
import { ConditionTree } from '../types/Conditions';
|
||||||
|
|
||||||
|
interface IErrorShowProps {
|
||||||
|
message: string;
|
||||||
|
condition: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ErrorShowAction implements IAction {
|
||||||
|
name: string;
|
||||||
|
actionProps: IActionProperty[];
|
||||||
|
props: IErrorShowProps;
|
||||||
|
constructor() {
|
||||||
|
this.name = "エラー表示";
|
||||||
|
this.actionProps = [];
|
||||||
|
this.props = {
|
||||||
|
message: '',
|
||||||
|
condition: ''
|
||||||
|
}
|
||||||
|
this.register();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* アクションの実行を呼び出す
|
||||||
|
* @param actionNode
|
||||||
|
* @param event
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async process(actionNode: IActionNode, event: any, context: IContext): Promise<IActionResult> {
|
||||||
|
let result = {
|
||||||
|
canNext: true,
|
||||||
|
result: false
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
this.actionProps = actionNode.actionProps;
|
||||||
|
if (!('message' in actionNode.ActionValue) && !('condition' in actionNode.ActionValue)) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
this.props = actionNode.ActionValue as IErrorShowProps;
|
||||||
|
const conditionResult = this.getConditionResult(context);
|
||||||
|
if (conditionResult) {
|
||||||
|
event.error = this.props.message
|
||||||
|
} else {
|
||||||
|
result = {
|
||||||
|
canNext: false,
|
||||||
|
result: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
event.error = error;
|
||||||
|
console.error(error);
|
||||||
|
result.canNext = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param context 条件式を実行する
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getConditionResult(context: any): boolean {
|
||||||
|
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 ErrorShowAction();
|
||||||
Reference in New Issue
Block a user