diff --git a/.idea/DynamicFormToolsProject.xml b/.idea/DynamicFormToolsProject.xml new file mode 100644 index 0000000..77f7241 --- /dev/null +++ b/.idea/DynamicFormToolsProject.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 435aae6..ffe66dc 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -56,6 +56,12 @@ + + + + + + diff --git a/.intellijPlatform/coroutines-javaagent-legacy.jar b/.intellijPlatform/coroutines-javaagent-legacy.jar index 7bde603..914ff94 100644 Binary files a/.intellijPlatform/coroutines-javaagent-legacy.jar and b/.intellijPlatform/coroutines-javaagent-legacy.jar differ diff --git a/.intellijPlatform/self-update.lock b/.intellijPlatform/self-update.lock index e5d60f1..33766b8 100644 --- a/.intellijPlatform/self-update.lock +++ b/.intellijPlatform/self-update.lock @@ -1 +1 @@ -2026-04-22 \ No newline at end of file +2026-05-14 \ No newline at end of file diff --git a/DevResources/full-examples/bdgt04/exec/bdgt04-actions.jsp b/DevResources/full-examples/bdgt04/exec/bdgt04-actions.jsp index 7c944b0..afc9393 100755 --- a/DevResources/full-examples/bdgt04/exec/bdgt04-actions.jsp +++ b/DevResources/full-examples/bdgt04/exec/bdgt04-actions.jsp @@ -11,7 +11,7 @@ SDKLogger logger = new SDKLogger("Operate-actions"); - public boolean bgt0102010_add(SystemFactory factory, JSONObject jsData) { + public boolean flowAdd(SystemFactory factory, JSONObject jsData) { if (!jsData.getString("-- some important field --").isBlank()) { DBConnector dbConn = factory.appDatabase.getXConnector(); try { @@ -45,6 +45,14 @@ return true; } + public boolean flowUpdate(SystemFactory factory, JSONObject jsData) { + return true; + } + + public boolean flowDelete(SystemFactory factory, JSONObject jsData) { + return true; + } + public boolean _action_skeleton(SystemFactory factory, JSONObject jsData) { if (!jsData.getString("-- some important field --").isBlank()) { DBConnector dbConn = factory.appDatabase.getXConnector(); @@ -62,7 +70,7 @@ return true; } - public boolean execute(String action, SystemFactory factory) { + private boolean execute(String action, SystemFactory factory) { try { Class> thisClass = getClass(); Method mtAction = thisClass.getMethod(action, SystemFactory.class, JSONObject.class); diff --git a/DevResources/full-examples/bdgt04/view/frm/bdgt-0401010.frml b/DevResources/full-examples/bdgt04/view/frm/bdgt-0401010.frml index 1ff2a45..13b52f1 100644 --- a/DevResources/full-examples/bdgt04/view/frm/bdgt-0401010.frml +++ b/DevResources/full-examples/bdgt04/view/frm/bdgt-0401010.frml @@ -1,5 +1,5 @@ - + @@ -94,6 +94,7 @@ + 0 diff --git a/DevResources/full-examples/bdgt04/view/frm/grids/grid-stratigy.frml b/DevResources/full-examples/bdgt04/view/frm/grids/grid-stratigy.frml index 7fcc58e..6c88913 100644 --- a/DevResources/full-examples/bdgt04/view/frm/grids/grid-stratigy.frml +++ b/DevResources/full-examples/bdgt04/view/frm/grids/grid-stratigy.frml @@ -1,5 +1,5 @@ - + APP diff --git a/DevResources/full-examples/bdgt05X/defs/ajax.xml b/DevResources/full-examples/bdgt05X/defs/ajax.xml new file mode 100644 index 0000000..25237df --- /dev/null +++ b/DevResources/full-examples/bdgt05X/defs/ajax.xml @@ -0,0 +1,201 @@ + + + + BUD + Main Activity + 100 + + SELECT PJM.PJM_CODE + , PJM.PJM_NAME + , PJM.PJM_JOB_TYPE + , PJM.PJM_WORK_PLAN + , PJM.PJM_PRODUCT + , PJM.PJM_GROUP_PLAN + , PJM.PJM_JOB_PROJECT + , PJM.PJM_STRATEGY + , PJM.PJM_ACTIVE + + FROM PROJECT_M PJM + + WHERE (PJM.PJM_CODE LIKE '%'||:SEARCH||'%' OR PJM.PJM_NAME LIKE '%'||:SEARCH||'%') + + + + + + + + BUD + Main Activity + 100 + + + SELECT ACM_CODE + , ACM_NAME + , NODE_LEVEL + , MAIN_NODE + , ACM_UNIT + , ACM_START_YEAR + , ACM_END_YEAR + , STM_CODE + , JBT_CODE + , JBP_CODE + , EXP_CODE + , PJM_CODE + , STG_CODE + , NODE_TYPE + , DECODE(NODE_TYPE,'C','true','') disabled + + FROM BGT.V_ACTIVITY_TREE ACM + + WHERE 0=0 + AND (:DV_YEAR BETWEEN ACM_START_YEAR AND ACM_END_YEAR) + -- AND (ACM.STM_CODE = '%' OR ACM.STM_CODE = :STM_CODE) + AND (ACM.ACM_CODE LIKE '%'||:SEARCH||'%' OR ACM.ACM_NAME LIKE '%'||:SEARCH||'%') + + + + + + + + BUD + Main Activity + 100 + + + SELECT BGD.BGM_CODE + , BGD.BGM_NAME + , BGD.BGM_EXPENSE_TYPE + , BGD.BGM_UNIT + , BGD.BGM_UNIT_RATE + , BGD.BGM_ACTIVE + , BGD.BGM_PREPARE + + FROM BUDGET_M BGD + + WHERE (BGD.BGM_CODE LIKE '%'||:SEARCH||'%' OR BGD.BGM_NAME LIKE '%'||:SEARCH||'%') + + ORDER BY BGM_CODE + + + + + APP + + SELECT RFG_GRP + , RFC_CODE + , RFC_DESC + , RFC_FLAG + , RFC_ORDER + + FROM REFER_CODE + WHERE (RFG_GRP = 'STG-ITEMS') + AND (RFC_CODE LIKE '%'||:SEARCH||'%' OR RFC_DESC LIKE '%'||:SEARCH||'%') + ORDER BY RFC_CODE + + + + + APP + + SELECT RFG_GRP + , RFC_CODE + , RFC_DESC + , RFC_FLAG + , RFC_ORDER + + FROM REFER_CODE + WHERE (RFG_GRP = :RFG_GRP) + AND (RFC_CODE LIKE '%'||:SEARCH||'%' OR RFC_DESC LIKE '%'||:SEARCH||'%') + ORDER BY RFC_CODE + + + + + + BUD + Main Activity + 100 + + + SELECT ACM_CODE + , ACM_NAME + , NODE_LEVEL + , MAIN_NODE + , ACM_UNIT + , ACM_START_YEAR + , ACM_END_YEAR + , STM_CODE + , JBT_CODE + , JBP_CODE + , EXP_CODE + , PJM_CODE + , STG_CODE + , NODE_TYPE + , DECODE(NODE_TYPE,'C','true','') disabled + + FROM BGT.V_ACTIVITY_TREE ACM + + WHERE 0=0 + AND (:DV_YEAR BETWEEN ACM_START_YEAR AND ACM_END_YEAR) + -- AND (ACM.STM_CODE = '%' OR ACM.STM_CODE = :STM_CODE) + AND (ACM.ACM_CODE LIKE '%'||:SEARCH||'%' OR ACM.ACM_NAME LIKE '%'||:SEARCH||'%') + + + + + + + + APP + Main Activity + 100 + + + SELECT BGM.BGM_CODE + , BGM.BGM_NAME + , BGM.EXP_TYPE + , BGM.BGM_UNIT + , BGM.BGM_UNIT_RATE + , BGM.BGM_SEQ + , BGM.NODE_LEVEL + , BGM.MAIN_NODE + , DECODE(NODE_TYPE,'C','true','') disabled + + FROM V_BUDGET_TREE BGM + + WHERE (BGM.BGM_CODE LIKE '%'||:SEARCH||'%' OR BGM.BGM_NAME LIKE '%'||:SEARCH||'%') + + ORDER BY BGM_SEQ + + + + + + + APP + Main Activity + 100 + + + SELECT BGM.BGM_CODE + , BGM.BGM_NAME + , BGM.EXP_TYPE + , BGM.BGM_UNIT + , BGM.BGM_UNIT_RATE + , BGM.BGM_SEQ + , BGM.NODE_LEVEL + , BGM.MAIN_NODE + , DECODE(NODE_TYPE,'C','true','') disabled + + FROM V_BUDGET_TREE BGM + + WHERE (BGM_CODE LIKE '3.1%') + AND (BGM.BGM_CODE LIKE '%'||:SEARCH||'%' OR BGM.BGM_NAME LIKE '%'||:SEARCH||'%') + + ORDER BY BGM_SEQ + + + + \ No newline at end of file diff --git a/DevResources/full-examples/bdgt05X/view/bgt0501010-general.jsp b/DevResources/full-examples/bdgt05X/view/bgt0501010-general.jsp new file mode 100644 index 0000000..653d52d --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/bgt0501010-general.jsp @@ -0,0 +1,50 @@ +<%@ page import="org.apache.commons.codec.*" %> +<%@ page contentType="text/html; charset=UTF-8" language="java" %> +<%@ include file="/WEB-INF/app/system/dynf/dynfSysInfo.jsp" %> + +<%=pageItemInfo.getProgTitle()%> +<% + String _formId = "bdgt-0501010/general"; + try { + String data = factory.rqsCtx.getParameter("data",""); + boolean detailMode = false; + if (!data.isBlank()) { + data = JUtils.decBase64(data); + data = URLDecoder.decode(data, CharEncoding.UTF_8); + + JSONObject jsData = new JSONObject(data); + String type = jsData.getString("type").toLowerCase(); + String acmCode = jsData.getString("acm_code"); + String projId = jsData.getString("proj_id"); + + if (type.equals("rutn")) { + _formId = "bdgt-0501010/routine"; + } else if (type.equals("mngt")) { + _formId = "bdgt-0501010/manage"; + } else if (type.equals("eqpt")) { + _formId = "bdgt-0501010/equipt"; + } else if (type.equals("cnst")) { + _formId = "bdgt-0501010/construct"; + } + + detailMode = true; + dynForm = new DynForm(application, request, response, "bdgt05", _formId); + dynForm.setFormOwner(request.getRequestURL().toString()); + dynForm.setFormvar("ACM_CODE",acmCode); + dynForm.setFormvar("PROJ_ID",projId); + } else { + dynForm = new DynForm(application, request, response, "bdgt05", _formId); + } + + if (detailMode) { + factory.pageCtx.addWidget(DynConstants.PageWidget.FileBox); + %><%@ include file="/WEB-INF/app/system/dynf/dynfDataEdit.jsp" %><% + } else { + %><%@ include file="/WEB-INF/app/system/dynf/dynfDataList.jsp" %><% + } + } catch (Exception ex) { + factory.setRestCode("ERROR"); + factory.setRestMsg(ex.getMessage() + "\n" + JUtils.stackToString(ex,16)); + logger.error(ex.getMessage(),ex); + } +%> diff --git a/DevResources/full-examples/bdgt05X/view/bgt0501010.jsp b/DevResources/full-examples/bdgt05X/view/bgt0501010.jsp new file mode 100644 index 0000000..e91888d --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/bgt0501010.jsp @@ -0,0 +1,20 @@ +<%@ page contentType="text/html; charset=UTF-8" language="java" %> +<%@ include file="/WEB-INF/app/system/dynf/dynfSysInfo.jsp" %> + +<%=pageItemInfo.getProgTitle()%> +<% + try { + dynForm = new DynForm(application, request, response, "bdgt05", "bdgt-0501010"); + dynForm.setFormOwner(request.getRequestURL().toString()); + dynForm.setFormvar("USER_CODE",factory.user.getUSER_CODE()); + if (dynForm.workIn(WorkMode.Editing)) { + %><%@ include file="/WEB-INF/app/system/dynf/dynfDataEdit.jsp" %><% + } else { + %><%@ include file="/WEB-INF/app/system/dynf/dynfDataList.jsp" %><% + } + } catch (Exception ex) { + factory.setRestCode("ERROR"); + factory.setRestMsg(ex.getMessage() + "\n" + JUtils.stackToString(ex,16)); + logger.error(ex.getMessage(),ex); + } +%> diff --git a/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010.frml b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010.frml new file mode 100644 index 0000000..3aec1a2 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010.frml @@ -0,0 +1,99 @@ + + + + + APP + PROJECTS + PROJ_ID + + + SELECT DISTINCT PRPS.PROP_ID + , PRPS.PROP_YEAR + , PRPS.PROP_YEAR FLOW_YEAR + , PRPS.STM_CODE STM_CODE + , BUD.GET_SECTION(PRPS.STM_CODE) STM_NAME + , PRPS.BTM_CODE BTM_CODE + , PRPS.FLOW_TYPE FLOW_TYPE + , PRPS.FLOW_STATE FLOW_STATE + , NVL(BUD.GET_PROJECT(PROJ.PJM_CODE), VPSTT.FLOW_TYPE_DESC) PROJ_NAME + , BUD.GET_ACTIVITY(PROJ.ACM_CODE) ACM_NAME + , VPSTT.FLOW_TYPE_DESC + , VPSTT.FLOW_LEVEL + , VPSTT.FLOW_STATE + , VPSTT.FLOW_STEP_DESC + , VPSTT.FLOW_STATE_DESC + , VPSTT.NEXT_STEP + , VPSTT.NEXT_STEP_DESC + , VPSTT.NEXT_STATE_DESC + , PRPS.PROP_VERSION VERSION + , PRPS.PROP_DOCNO DOCNO + , PRPS.PROP_PROVED_DATE PROVED_DATE + , JDTOT(PRPS.FLOW_DATE) STATE_DATE + + + FROM PROPOSALS PRPS + INNER JOIN V_PROJECT_STATE VPSTT on PRPS.PROP_ID = VPSTT.PROP_ID + LEFT OUTER JOIN PROPS_PROJECTS PROJ ON PRPS.PROP_ID = PROJ.PROJ_ID + INNER JOIN V_PROPS_APPROVERS PAPVS ON PAPVS.PROP_ID=PRPS.PROP_ID + + WHERE PRPS.FLOW_TYPE = 'PROP-GNL-01' AND PAPVS.FAPV_APPROVER = :USER_CODE + ORDER BY PRPS.PROP_ID + + + + + + + 0 + + + + + {return $(`${value}`);}]]> + + + + + {return $(`${value}`);}]]> + + + + + {return $(`${value}`);}]]> + + + + + + { + console.log(row); + $PageCtx.saveSessionData("general", row); + $PageCtx.saveStorageData("general#search", {prop_id: row.prop_id}); + $WebNavi.goto("/bdgt05/bgt0501010-general"); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/construct.frml b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/construct.frml new file mode 100644 index 0000000..11fafdf --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/construct.frml @@ -0,0 +1,322 @@ + + + + + + + + + APP + PROJECTS + PROJ_ID + + SELECT PROJ_ID + , PROJ_TYPE + , PROJ_YEAR + , PJM_CODE + , STM_CODE + , BTM_CODE + , ACM_CODE + , BUD.GET_ACTIVITY(ACM_CODE) ACM_NAME + , PLAN_TYPE + , PROJ_DURATION + , PROJ_INST_COUNT + , PROJ_START_YEAR + , PROJ_END_YEAR + , PROJ_FOR_STAFF + , PROJ_FROM_YEAR + + FROM PROJECTS + WHERE PROJ_ID = :PROJ_ID + ORDER BY PROJ_ID + + + + + + + + + + + + + + + + + + + + + APP + PROJECT_CONST_INFO + PROJ_ID,ACM_CODE + + + SELECT PROJ_ID + , ACM_CODE + , PCTI_ADDRESS + , PCTI_TUMBON + , PCTI_AMPUR + , PCTI_PROVINCE + , PCTI_ADDR_NOTE + , PCTI_AREA + , PCTI_EMP_COUNT + , PCTI_CHK_LOCATION + , PCTI_CHK_DOCUMENT + , PCTI_REMARK + + FROM PROJECT_CONST_INFO + ORDER BY PROJ_ID, ACM_CODE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @M{project.year_budgets}]]> + + + + + + + + + + + + + @M{pcti.location_info} + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + @M{project.budget}]]> + + + + + + + + + @M{project.constructor_attatchment}]]> + + + + + + + + + + + + + + + + + การส่งกลับแก้ไข]]> + + + + รายการแก้ไข + + + + + + + ประวัติการแก้ไข + + แก้ครั้งที่ 1 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 2 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 3 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 4 วันที่ xx/xx/xxxx : ................................... + + + ]]> + + + + + + เอกสารประกอบการพิจารณา]]> + + + + + + + + + + + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/equipt.frml b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/equipt.frml new file mode 100644 index 0000000..6b6e46b --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/equipt.frml @@ -0,0 +1,348 @@ + + + + + APP + PROJECT_BUDGETS + PROJ_ID,ACM_CODE + + SELECT PBDG.PROJ_ID + , PBDG.PBDG_ID + , PBDG.ACM_CODE + , BUD.GET_ACTIVITY(PBDG.ACM_CODE) ACM_NAME + , PBDG.PBDG_TOTAL + , PBDG.PBDG_COUNT + , PBDG.PBDG_ASSET_TYPE + , PBDG.PBDG_REASON + , PMAS.PROJ_YEAR + , PMAS.STM_CODE + , PMAS.PROJ_YEAR||'/'||PMAS.STM_CODE||'/'||PBDG.PROJ_ID FILEPATH + + FROM PROJECT_BUDGETS PBDG + INNER JOIN PROJECTS PMAS ON PMAS.PROJ_ID = PBDG.PROJ_ID + + ORDER BY PROJ_ID,PBDG_ID + + + + + + + + + + + + + + APP + PROJECT_TARGETS + PROJ_ID,ACM_CODE + + SELECT PROJ_ID + , ACM_CODE + , PWMT_UNIT + , PTGT_QT_0101 + , PTGT_QT_0102 + , PTGT_QT_0201 + , PTGT_QT_0202 + , PTGT_QT_0301 + , PTGT_QT_0302 + , PTGT_QT_0401 + , PTGT_QT_0402 + + FROM PROJECT_TARGETS + ORDER BY PROJ_ID,ACM_CODE + + + + + + + + + + + + + + + + + + + APP + PROJECT_BUDGET_ITEMS + PROJ_ID,BGM_CODE,PBDG_ID + + SELECT PROJ_ID + , PBDG_ID + , BGM_CODE + , PBGI_QT_01 + , PBGI_QT_02 + , PBGI_QT_03 + , PBGI_QT_04 + , PBGI_QTY + , PBGI_COST + , PBGI_FREQ + , PBGI_ASSET_TYPE + , PBGI_REPLACE + , PBGI_ADDITION + , PBGI_ACQUIRE + , PBGI_TOTAL + , PBGI_REASON + + FROM PROJECT_BUDGET_ITEMS + ORDER BY PROJ_ID,BGM_CODE,PBDG_ID + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + console.log("call formater with ", data); + return $(`[${data.bgm_code}] : ${data.bgm_name}`); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @M{project.target}]]> + + + + + + + + + + + + + + @M{pbge.quotaion_file}]]> + + + + + + + + + + + + + + + การส่งกลับแก้ไข]]> + + + + รายการแก้ไข + + + + + + + ประวัติการแก้ไข + + แก้ครั้งที่ 1 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 2 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 3 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 4 วันที่ xx/xx/xxxx : ................................... + + + ]]> + + + + + + เอกสารประกอบการพิจารณา]]> + + + + + + + + + + + + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/general.frml b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/general.frml new file mode 100644 index 0000000..6c29005 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/general.frml @@ -0,0 +1,259 @@ + + + + + APP + PROPS_PROJECTS + PROP_ID + + SELECT PPROJ.PROP_ID + , PROJ.PROJ_ID + , PROJ.PROJ_TYPE + , PROJ.PROJ_YEAR + , PROJ.PJM_CODE + , PROJ.STM_CODE + , PROJ.BTM_CODE + , PROJ.ACM_CODE + , BUD.GET_ACTIVITY(PROJ.ACM_CODE) ACM_NAME + , PROJ.PLAN_TYPE + , PROJ.PROJ_DURATION + , PROJ.PROJ_START_YEAR + , PROJ.PROJ_END_YEAR + , PROJ.PROJ_FOR_STAFF + , PROJ.PROJ_FROM_YEAR + , VPSTT.PROJ_ORDER + , VPSTT.FLOW_LEVEL + , 0 BDGT_TOTAL + + FROM PROPS_PROJECTS PROJ + INNER JOIN PROPOSALS_PROJECTS PPROJ ON PROJ.PROJ_ID=PPROJ.PROJ_ID + INNER JOIN V_PROJECT_STATE VPSTT ON VPSTT.PROJ_ID=PPROJ.PROJ_ID + + WHERE PPROJ.PROP_ID = :PROP_ID + ORDER BY VPSTT.PROJ_ORDER + + + + + + + + + + + + + + + + + + + + + + + APP + V_ACTIVITY_TREE + + + SELECT PACT.PROJ_ID + , PACT.PROJ_YEAR + , PACT.PROJ_TYPE + , PACT.PROJ_GROUP + , ACTT.ACM_CODE + , ACTT.ACM_NAME + , ACTT.ACM_SEQ + , ACTT.NODE_LEVEL+1 NODE_LEVEL + , ACTT.MAIN_NODE + , ACTT.ACM_UNIT + , ACTT.ACM_START_YEAR + , ACTT.ACM_END_YEAR + , ACTT.NODE_TYPE + , PACT.BDGT_COUNT + , PACT.BDGT_TOTAL + + + FROM BGT.V_ACTIVITY_TREE ACTT + INNER JOIN (SELECT DISTINCT ACM.ACM_CODE + FROM BUD.ACTIVITY_M ACM + INNER JOIN BUD.ACTIVITY_CTRL_H ACH ON ACM.ACM_CODE = ACH.ACH_CODE + START WITH ACM.ACM_CODE IN (SELECT ACM_CODE FROM V_PROJECT_ACTIVITY PACT WHERE PACT.PROJ_ID = :PROJ_ID) + CONNECT BY PRIOR ACH.ACH_CTRL_CODE = ACH.ACH_CODE) FLTR ON FLTR.ACM_CODE = ACTT.ACM_CODE + LEFT OUTER JOIN V_PROJECT_ACTIVITY PACT ON PACT.ACM_CODE = ACTT.ACM_CODE AND PACT.PROJ_ID = :PROJ_ID + + + ORDER BY ACTT.ACM_SEQ + + + + + + + + + 0 + + + + + {return $(`${value}`);}]]> + + + + + {return $(`${value}`);}]]> + + + + + { + if (row["node_type"] === "D") { + return `${formatNumber(value,"#,##0")} ${row["acm_unit"]}`; + } else { + return "" + } + }]]> + + + + + + + {return ["D"].includes(row["node_type"]);}]]> + { + console.log(row); + $PageCtx.saveSessionData("general", row); + $PageCtx.saveStorageData("general#search", {PROP_ID: row.prop_id}); + let $data = {proj_id : row.proj_id,type : row.proj_type,acm_code : row.acm_code,} + $WebNavi.goto("/bdgt05/bgt0501010-general?data="+SdkXUtils.jsonBase64($data)); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/manage.frml b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/manage.frml new file mode 100644 index 0000000..71fd9a7 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/manage.frml @@ -0,0 +1,264 @@ + + + + + + + + APP + PROJECT_BUDGETS + PROJ_ID,ACM_CODE + + SELECT PBDG.PROJ_ID + , PROJ.PROJ_YEAR + , PBDG.PBDG_ID + , PBDG.ACM_CODE + , BUD.GET_ACTIVITY(PBDG.ACM_CODE) ACM_NAME + , PBDG.PBDG_TOTAL + , PBDG.PBDG_COUNT + + FROM PROJECT_BUDGETS PBDG + INNER JOIN PROJECTS PROJ ON PROJ.PROJ_ID=PBDG.PROJ_ID + + ORDER BY PBDG.PROJ_ID,PBDG.PBDG_ID + + + + + + + + + + + APP + PROJECT_TARGETS + PROJ_ID,ACM_CODE + + SELECT PROJ_ID + , ACM_CODE + , PWMT_UNIT + , PTGT_QT_0101 + , PTGT_QT_0102 + , PTGT_QT_0201 + , PTGT_QT_0202 + , PTGT_QT_0301 + , PTGT_QT_0302 + , PTGT_QT_0401 + , PTGT_QT_0402 + + FROM PROJECT_TARGETS + ORDER BY PROJ_ID,ACM_CODE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @M{project.budget} + ]]> + + + + + + + @M{project.constructor_attatchment}]]> + + + + + + + + + + + + + + + การส่งกลับแก้ไข]]> + + + + รายการแก้ไข + + + + + + + ประวัติการแก้ไข + + แก้ครั้งที่ 1 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 2 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 3 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 4 วันที่ xx/xx/xxxx : ................................... + + + ]]> + + + + + + เอกสารประกอบการพิจารณา]]> + + + + + + + + + + + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/pt-routine.frml b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/pt-routine.frml new file mode 100644 index 0000000..1654ced --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/pt-routine.frml @@ -0,0 +1,267 @@ + + + + + + + + APP + PROJECT_BUDGETS + PROJ_ID,ACM_CODE + + SELECT PBDG.PROJ_ID + , PROJ.PROJ_YEAR + , PBDG.PBDG_ID + , PBDG.ACM_CODE + , BUD.GET_ACTIVITY(PBDG.ACM_CODE) ACM_NAME + , PBDG.PBDG_TOTAL + , PBDG.PBDG_COUNT + + FROM PROJECT_BUDGETS PBDG + INNER JOIN PROJECTS PROJ ON PROJ.PROJ_ID=PBDG.PROJ_ID + + ORDER BY PBDG.PROJ_ID,PBDG.PBDG_ID + + + + + + + + + + + + APP + PROJECT_TARGETS + PROJ_ID,ACM_CODE + + SELECT PROJ_ID + , ACM_CODE + , PWMT_UNIT + , PTGT_QT_0101 + , PTGT_QT_0102 + , PTGT_QT_0201 + , PTGT_QT_0202 + , PTGT_QT_0301 + , PTGT_QT_0302 + , PTGT_QT_0401 + , PTGT_QT_0402 + + FROM PROJECT_TARGETS + ORDER BY PROJ_ID,ACM_CODE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @M{project.budget} + ]]> + + + + + + + + @M{project.constructor_attatchment}]]> + + + + + + + + + + + + + + + การส่งกลับแก้ไข]]> + + + + รายการแก้ไข + + + + + + + ประวัติการแก้ไข + + แก้ครั้งที่ 1 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 2 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 3 วันที่ xx/xx/xxxx : ................................... + แก้ครั้งที่ 4 วันที่ xx/xx/xxxx : ................................... + + + ]]> + + + + + + เอกสารประกอบการพิจารณา]]> + + + + + + + + + + + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/routine.frml b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/routine.frml new file mode 100644 index 0000000..278c641 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/bdgt-0501010/routine.frml @@ -0,0 +1,232 @@ + + + + + + + + + + APP + PROJECT_BUDGETS + PROJ_ID,ACM_CODE + + SELECT PROPS.PROP_ID, PROPS.PROP_YEAR,PROPS.STM_CODE,PROPS.PROP_VERSION + , PBDG.PROJ_ID + , PROJ.PROJ_YEAR + , PBDG.PBDG_ID + , PBDG.ACM_CODE + , BUD.GET_ACTIVITY(PBDG.ACM_CODE) ACM_NAME + , PBDG.PBDG_TOTAL + , PBDG.PBDG_COUNT + , 'budget' FILE_TYPE + , 'budget-apvs' APPROVE_TYPE + + FROM PROPS_BUDGETS PBDG + INNER JOIN PROPS_PROJECTS PROJ ON PROJ.PROJ_ID=PBDG.PROJ_ID + INNER JOIN PROPOSALS_PROJECTS PPROJ ON PPROJ.PROJ_ID=PROJ.PROJ_ID + INNER JOIN PROPOSALS PROPS ON PROPS.PROP_ID=PPROJ.PROP_ID + + ORDER BY PBDG.PROJ_ID,PBDG.PBDG_ID + + + + + + + + + + + + APP + PROJECT_TARGETS + PROJ_ID,ACM_CODE + + SELECT PROJ_ID + , ACM_CODE + , PWMT_UNIT + , PTGT_QT_0101 + , PTGT_QT_0102 + , PTGT_QT_0201 + , PTGT_QT_0202 + , PTGT_QT_0301 + , PTGT_QT_0302 + , PTGT_QT_0401 + , PTGT_QT_0402 + + FROM PROJECT_TARGETS + ORDER BY PROJ_ID,ACM_CODE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @M{project.file_attatchment}]]> + + + + + + + + + @M{revise.title}]]> + + + + + + + + + + + + @M{approve.file_attatchment}]]> + + + + + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/datasets/attatch-files.frml b/DevResources/full-examples/bdgt05X/view/frm/datasets/attatch-files.frml new file mode 100644 index 0000000..6761d8b --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/datasets/attatch-files.frml @@ -0,0 +1,32 @@ + + + + + APP + ATTACH_FILES + ATFI_SEQ + + SELECT ATFI_SEQ + , PROJ_ID + , ACM_CODE + , ATFI_TYPE + , ATFI_FILE + , ATFI_MIME + , ATFI_DESC + + FROM ATTACH_FILES + ORDER BY PROJ_ID,ATFI_SEQ,ACM_CODE + + + + + + + + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/datasets/comments.frml b/DevResources/full-examples/bdgt05X/view/frm/datasets/comments.frml new file mode 100644 index 0000000..93a19a8 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/datasets/comments.frml @@ -0,0 +1,73 @@ + + + + + APP + PROPOSALS_COMMENTS + PPSC_SEQ + + SELECT PROP_ID + , PROP_VERSION + , PROJ_ID + , ACM_CODE + , BGM_CODE + , PPSC_LEVEL + , PPSC_SEQ + , PPSC_COMMENT + , PPSC_COMMENT_BY + , PPSC_COMMENT_TIME + + FROM PROPOSALS_COMMENTS + + + + + + + + + + + + + + + + + + APP + PROPOSALS_COMMENTS + PROJ_ID,PROP_ID,PPSC_SEQ + + SELECT PROP_ID + , PROP_VERSION + , PROJ_ID + , ACM_CODE + , BGM_CODE + , PPSC_LEVEL + , PPSC_SEQ + , PPSC_COMMENT + , PPSC_COMMENT_BY + , PPSC_COMMENT_TIME + + FROM PROPOSALS_COMMENTS + + ORDER BY PROJ_ID,PROP_ID,PPSC_SEQ + + + + + + + + + + + + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/datasets/policy-data.frml b/DevResources/full-examples/bdgt05X/view/frm/datasets/policy-data.frml new file mode 100644 index 0000000..2b5cbe0 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/datasets/policy-data.frml @@ -0,0 +1,80 @@ + + + + APP + PROJECT_POLICY_INFO + PROJ_ID + + SELECT PROJ_ID + , PROJ_PRINCIPLES + , PROJ_OBJECTIVE + , PROJ_BENEFIT + , PROJ_GOLD + , PROJ_TAGET_GROUP + , PROJ_LOCATION + , PROJ_ACTIVITY + , PROJ_RESPONDER + , PROJ_INVOLVERS + , PROJ_INVOLVISSE + , PROJ_PHASE + , PROJ_MONITORING + , CREATE_BY + , CREATE_AT + , UPDATE_BY + , UPDATE_AT + + FROM PROJECT_POLICY_INFO + ORDER BY PROJ_ID + + + + + + + + + + + + + + + + + + + + + APP + PROJECT_EXPENSES + PROJ_ID + + SELECT PROJ_ID + , PEXP_SEQ + , PEXP_DETAIL + , PEXP_QT_01 + , PEXP_QT_02 + , PEXP_QT_03 + , PEXP_QT_04 + , PEXP_TOTAL + , CREATE_BY + , CREATE_AT + , UPDATE_BY + , UPDATE_AT + + FROM PROJECT_EXPENSES + ORDER BY PROJ_ID,PEXP_SEQ + + + + + + + + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-construct-budget.frml b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-construct-budget.frml new file mode 100644 index 0000000..23ed717 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-construct-budget.frml @@ -0,0 +1,260 @@ + + + + + APP + PROJECT_BUDGET_ITEMS + PROJ_ID,BGM_CODE,PBDG_ID + + + SELECT :PROJ_ID PROJ_ID + , ACTT.ACM_CODE + , ACTT.ACM_NAME + , ACTT.ACM_SEQ + , ACTT.NODE_LEVEL + , ACTT.MAIN_NODE + , ACTT.ACM_UNIT + , ACTT.ACM_START_YEAR + , ACTT.ACM_END_YEAR + , ACTT.NODE_TYPE + , PBGI.PBDG_ID + , PBGI.BGM_CODE + , PBGI.PBGI_QTY + , PBGI.PBGI_COST + , PBGI.PBGI_FREQ + , PBGI.PBGI_ASSET_TYPE + , PBGI.PBGI_REPLACE + , PBGI.PBGI_ADDITION + , PBGI.PBGI_ACQUIRE + , PBGI.PBGI_TOTAL + , PBGI.PBGI_REASON + + + FROM BGT.V_ACTIVITY_TREE ACTT + INNER JOIN ( SELECT DISTINCT ACM.ACM_CODE + FROM BUD.ACTIVITY_M ACM + INNER JOIN BUD.ACTIVITY_CTRL_H ACH ON ACM.ACM_CODE = ACH.ACH_CODE + START WITH ACM.ACM_CODE IN (SELECT PBGI.PBDG_ID ACM_CODE FROM PROJECT_BUDGET_ITEMS PBGI WHERE PROJ_ID = :PROJ_ID) + CONNECT BY PRIOR ACH.ACH_CTRL_CODE = ACH.ACH_CODE) FLTR ON FLTR.ACM_CODE = ACTT.ACM_CODE + LEFT OUTER JOIN BGT.PROJECT_BUDGET_ITEMS PBGI ON PBGI.PBDG_ID = ACTT.ACM_CODE + + ORDER BY ACTT.ACM_SEQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + PROJ_ID,BGM_CODE + + + + + + + + + + + + + + + + + {return $(`${value}`);}]]> + + + + + {return $(`${value}`);}]]> + + + + + + + + {return row["node_type"] !== "C"}]]> + {return row["node_type"] !== "C"}]]> + {return row["node_type"] !== "C"}]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + { + console.log("call formater with ", data); + return $(`[${data.acm_code}] : ${data.acm_name}`); + } + ]]> + + + + + + + + + + + + + + { + console.log("call formater with ", data); + return $(`[${data.bgm_code}] : ${data.bgm_name}`); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-equipt-budget.frml b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-equipt-budget.frml new file mode 100644 index 0000000..3f1acd1 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-equipt-budget.frml @@ -0,0 +1,231 @@ + + + + + APP + PROJECT_BUDGET_ITEMS + PROJ_ID,BGM_CODE,PBDG_ID + + SELECT BGTT.BGM_CODE + , BGTT.BGM_NAME + , BGTT.BGM_UNIT + , BGTT.BGM_UNIT_RATE + , BGTT.NODE_LEVEL + , BGTT.BGM_SEQ + , BGTT.EXP_TYPE + , BGTT.MAIN_NODE + , BGTT.NODE_TYPE + , PBGI.PBGI_QTY + , PBGI.PBGI_COST + , PBGI.PBGI_FREQ + , PBGI.PBGI_ASSET_TYPE + , PBGI.PBGI_REPLACE + , PBGI.PBGI_ADDITION + , PBGI.PBGI_ACQUIRE + , PBGI.PBGI_TOTAL + , PBGI.PBGI_REASON + + + FROM BGT.V_BUDGET_TREE BGTT + INNER JOIN (SELECT DISTINCT BGDM.BGM_CODE + FROM BUD.BUDGET_M BGDM + INNER JOIN BUD.BUDGET_M BGDH ON BGDM.BGM_CODE = BGDH.BGM_CODE + START WITH BGDM.BGM_CODE IN (SELECT PBGI.BGM_CODE FROM BGT.PROJECT_BUDGET_ITEMS PBGI WHERE PBGI.PROJ_ID = :PROJ_ID AND PBGI.PBDG_ID = :PBDG_ID) + CONNECT BY PRIOR BGDH.BGM_MAIN_NODE = BGDH.BGM_CODE) FLTR ON FLTR.BGM_CODE = BGTT.BGM_CODE + LEFT OUTER JOIN BGT.PROJECT_BUDGET_ITEMS PBGI ON PBGI.BGM_CODE = BGTT.BGM_CODE + + ORDER BY BGTT.BGM_SEQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + PROJ_ID,BGM_CODE + + + + + + + + + + + + + + + + + + + {return $(`${value}`);}]]> + + + + + {return $(`${value}`);}]]> + + + + + + + + + + {return row["node_type"] !== "C"}]]> + {return row["node_type"] !== "C"}]]> + {return row["node_type"] !== "C"}]]> + + + + + + + + + + + + + + + + + + + + + + { + console.log("call formater with ", data); + return $(`[${data.bgm_code}] : ${data.bgm_name}`); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-manage-budget.frml b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-manage-budget.frml new file mode 100644 index 0000000..f08888e --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-manage-budget.frml @@ -0,0 +1,222 @@ + + + + + APP + PROJECT_BUDGET_ITEMS + PROJ_ID,BGM_CODE,PBDG_ID + + SELECT BGTT.BGM_CODE + , BGTT.BGM_NAME + , BGTT.BGM_UNIT + , BGTT.BGM_UNIT_RATE + , BGTT.NODE_LEVEL + , BGTT.BGM_SEQ + , BGTT.EXP_TYPE + , BGTT.MAIN_NODE + , BGTT.NODE_TYPE + , PBGI.PBGI_QTY + , PBGI.PBGI_FREQ + , PBGI.PBGI_COST + , PBGI.PBGI_ACQUIRE + , PBGI.PBGI_TOTAL + + + FROM BGT.V_BUDGET_TREE BGTT + INNER JOIN (SELECT DISTINCT BGDM.BGM_CODE + FROM BUD.BUDGET_M BGDM + INNER JOIN BUD.BUDGET_M BGDH ON BGDM.BGM_CODE = BGDH.BGM_CODE + START WITH BGDM.BGM_CODE IN (SELECT PBGI.BGM_CODE FROM BGT.PROJECT_BUDGET_ITEMS PBGI WHERE PBGI.PROJ_ID = :PROJ_ID AND PBGI.PBDG_ID = :PBDG_ID) + CONNECT BY PRIOR BGDH.BGM_MAIN_NODE = BGDH.BGM_CODE) FLTR ON FLTR.BGM_CODE = BGTT.BGM_CODE + LEFT OUTER JOIN BGT.PROJECT_BUDGET_ITEMS PBGI ON PBGI.BGM_CODE = BGTT.BGM_CODE AND PBGI.PROJ_ID = :PROJ_ID AND PBGI.PBDG_ID = :PBDG_ID + + ORDER BY BGTT.BGM_SEQ + + + + + + + + + + + + + + + + + + + + + + + PROJ_ID,BGM_CODE + + + + + + + + + + + + + + + + + + + {return $(`${value}`);}]]> + + + + + {return $(`${value}`);}]]> + + + + + + + + + + {return row["node_type"] !== "C"}]]> + {return row["node_type"] !== "C"}]]> + {return row["node_type"] !== "C"}]]> + + + + + + + + + + + + + + + + + + + + + + { + console.log("call formater with ", data); + return $(`[${data.bgm_code}] : ${data.bgm_name}`); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-proj-budget.frml b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-proj-budget.frml new file mode 100644 index 0000000..f6cc2b0 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-proj-budget.frml @@ -0,0 +1,213 @@ + + + + + APP + PROJECT_BUDGET_ITEMS + PROJ_ID,BGM_CODE,PBDG_ID + + SELECT BGTT.BGM_CODE + , BGTT.BGM_NAME + , BGTT.BGM_UNIT + , BGTT.BGM_UNIT_RATE + , BGTT.NODE_LEVEL + , BGTT.BGM_SEQ + , BGTT.EXP_TYPE + , BGTT.MAIN_NODE + , BGTT.NODE_TYPE + , PBGI.PBGI_QTY + , PBGI.PBGI_FREQ + , PBGI.PBGI_COST + , PBGI.PBGI_TOTAL + + + FROM BGT.V_BUDGET_TREE BGTT + INNER JOIN (SELECT DISTINCT BGDM.BGM_CODE + FROM BUD.BUDGET_M BGDM + INNER JOIN BUD.BUDGET_M BGDH ON BGDM.BGM_CODE = BGDH.BGM_CODE + START WITH BGDM.BGM_CODE IN (SELECT PBGI.BGM_CODE FROM BGT.PROJECT_BUDGET_ITEMS PBGI WHERE PBGI.PROJ_ID = :PROJ_ID AND PBGI.PBDG_ID = :PBDG_ID) + CONNECT BY PRIOR BGDH.BGM_MAIN_NODE = BGDH.BGM_CODE) FLTR ON FLTR.BGM_CODE = BGTT.BGM_CODE + LEFT OUTER JOIN BGT.PROJECT_BUDGET_ITEMS PBGI ON PBGI.BGM_CODE = BGTT.BGM_CODE AND PBGI.PROJ_ID = :PROJ_ID AND PBGI.PBDG_ID = :PBDG_ID + + ORDER BY BGTT.BGM_SEQ + + + + + + + + + + + + + + + + + + + + + + PROJ_ID,BGM_CODE + + + + + + + + + + + + + + + + + + {return $(`${value}`);}]]> + + + + + {return $(`${value}`);}]]> + + + + + + + + + {return row["node_type"] !== "C"}]]> + {return row["node_type"] !== "C"}]]> + {return row["node_type"] !== "C"}]]> + + + + + + + + + + + + + + + + + + + + + + + { + return $(`[${data.bgm_code}] : ${data.bgm_name}`); + } + ]]> + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-routine-budget.frml b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-routine-budget.frml new file mode 100644 index 0000000..ef8085d --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-routine-budget.frml @@ -0,0 +1,222 @@ + + + + + APP + PROJECT_BUDGET_ITEMS + PROJ_ID,BGM_CODE,PBDG_ID + + SELECT BGTT.BGM_CODE + , BGTT.BGM_NAME + , BGTT.BGM_UNIT + , BGTT.BGM_UNIT_RATE + , BGTT.NODE_LEVEL + , BGTT.BGM_SEQ + , BGTT.EXP_TYPE + , BGTT.MAIN_NODE + , BGTT.NODE_TYPE + , PBGI.PBGI_QTY + , PBGI.PBGI_FREQ + , PBGI.PBGI_COST + , PBGI.PBGI_ACQUIRE + , PBGI.PBGI_TOTAL + + + FROM BGT.V_BUDGET_TREE BGTT + INNER JOIN (SELECT DISTINCT BGDM.BGM_CODE + FROM BUD.BUDGET_M BGDM + INNER JOIN BUD.BUDGET_M BGDH ON BGDM.BGM_CODE = BGDH.BGM_CODE + START WITH BGDM.BGM_CODE IN (SELECT PBGI.BGM_CODE FROM BGT.PROJECT_BUDGET_ITEMS PBGI WHERE PBGI.PROJ_ID = :PROJ_ID AND PBGI.PBDG_ID = :PBDG_ID) + CONNECT BY PRIOR BGDH.BGM_MAIN_NODE = BGDH.BGM_CODE) FLTR ON FLTR.BGM_CODE = BGTT.BGM_CODE + LEFT OUTER JOIN BGT.PROJECT_BUDGET_ITEMS PBGI ON PBGI.BGM_CODE = BGTT.BGM_CODE AND PBGI.PROJ_ID = :PROJ_ID AND PBGI.PBDG_ID = :PBDG_ID + + ORDER BY BGTT.BGM_SEQ + + + + + + + + + + + + + + + + + + + + + + + PROJ_ID,BGM_CODE + + + + + + + + + + + + + + + + + + + {return $(`${value}`);}]]> + + + + + {return $(`${value}`);}]]> + + + + + + + + + + {return row["node_type"] !== "C"}]]> + {return row["node_type"] !== "C"}]]> + {return row["node_type"] !== "C"}]]> + + + + + + + + + + + + + + + + + + + + + + { + console.log("call formater with ", data); + return $(`[${data.bgm_code}] : ${data.bgm_name}`); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-stratigy-item.frml b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-stratigy-item.frml new file mode 100644 index 0000000..8317aa4 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-stratigy-item.frml @@ -0,0 +1,82 @@ + + + + + APP + PROJECT_STTGYS_ITEMS + PROJ_ID,PSTG_CODE,PSTI_GROUP + + + SELECT PSTI.PROJ_ID + , PSTI.PSTG_CODE + , PSTI.PSTI_GROUP + , RFGP.RFC_DESC GROUP_DESC + , PSTI.PSTI_CODE + , RFCI.RFC_DESC ITEM_DESC + , PSTI_DETAIL + + FROM PROJECT_STTGYS_ITEMS PSTI + INNER JOIN BGT.REFER_CODE RFGP ON RFGP.RFG_GRP='STG-ITEMS' AND RFC.RFC_CODE=STTG.PSTI_CODE + INNER JOIN BGT.REFER_CODE RFCI ON RFC.RFG_GRP=PSTI.PSTI_GROUP AND RFC.RFC_CODE=STTG.PSTI_CODE + + ORDER BY PSTI_GROUP, PSTI_CODE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-stratigy.frml b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-stratigy.frml new file mode 100644 index 0000000..0116838 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-stratigy.frml @@ -0,0 +1,71 @@ + + + + + APP + PROJECT_STTGYS + PROJ_ID,PSTG_CODE + + + SELECT STTG.PROJ_ID + , STTG.PSTG_CODE + , RFC.RFC_DESC PSTG_DESC + , STTG.PSTG_CONCEPT + + + FROM PROJECT_STTGYS STTG + INNER JOIN BGT.REFER_CODE RFC ON RFC.RFG_GRP='STG-MAIN' AND RFC.RFC_CODE=STTG.PSTG_CODE + + ORDER BY PSTG_CODE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @M{pstg.header}]]> + + + + + + + + + + ]]> + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-work-budget.frml b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-work-budget.frml new file mode 100644 index 0000000..3347e5c --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/grids-bk/grid-work-budget.frml @@ -0,0 +1,174 @@ + + + + + APP + PROJECT_BUDGET_ITEMS + PROJ_ID,BGM_CODE,PBDG_ID + + SELECT PROJ_ID + , PBDG_ID + , BGM_CODE + , BUD.GET_BUDGET (BGM_CODE) BGM_NAME + , PBGI_QT_01 + , PBGI_QT_02 + , PBGI_QT_03 + , PBGI_QT_04 + , PBGI_QTY + , PBGI_COST + , PBGI_FREQ + , PBGI_ASSET_TYPE + , PBGI_REPLACE + , PBGI_ADDITION + , PBGI_ACQUIRE + , PBGI_TOTAL + , PBGI_REASON + + FROM PROJECT_BUDGET_ITEMS + ORDER BY PROJ_ID,BGM_CODE + + + + + + + + + + + + + + + + + + + + + + + + + + + + PROJ_ID,BGM_CODE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + console.log("call formater with ", data); + return $(`[${data.bgm_code}] : ${data.bgm_name}`); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/grids/grid-routine-budget.frml b/DevResources/full-examples/bdgt05X/view/frm/grids/grid-routine-budget.frml new file mode 100644 index 0000000..3d5b315 --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/grids/grid-routine-budget.frml @@ -0,0 +1,174 @@ + + + + + APP + PROJECT_BUDGET_ITEMS + PROJ_ID,BGM_CODE,PBDG_ID + + SELECT PROJ_ID + , PBDG_ID + , BGM_CODE + , BUD.GET_BUDGET (BGM_CODE) BGM_NAME + , PBGI_QT_01 + , PBGI_QT_02 + , PBGI_QT_03 + , PBGI_QT_04 + , PBGI_QTY + , PBGI_COST + , PBGI_FREQ + , PBGI_ASSET_TYPE + , PBGI_REPLACE + , PBGI_ADDITION + , PBGI_ACQUIRE + , PBGI_TOTAL + , PBGI_REASON + + FROM PROJECT_BUDGET_ITEMS + ORDER BY PROJ_ID,BGM_CODE + + + + + + + + + + + + + + + + + + + + + + + + + + + + PROJ_ID,BGM_CODE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + console.log("call formater with ", data); + return $(`[${data.bgm_code}] : ${data.bgm_name}`); + } + ]]> + + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + diff --git a/DevResources/full-examples/bdgt05X/view/frm/sections/sect-approve.frml b/DevResources/full-examples/bdgt05X/view/frm/sections/sect-approve.frml new file mode 100644 index 0000000..f506ffb --- /dev/null +++ b/DevResources/full-examples/bdgt05X/view/frm/sections/sect-approve.frml @@ -0,0 +1,120 @@ + + + + + + APP + ATTACH_FILES + ATFI_SEQ + + SELECT ATFI_SEQ + , PROJ_ID + , ACM_CODE + , ATFI_TYPE + , ATFI_FILE + , ATFI_MIME + , ATFI_DESC + + FROM ATTACH_FILES + ORDER BY PROJ_ID,ATFI_SEQ,ACM_CODE + + + + + + + + + + + + + + + APP + PROPOSALS_COMMENTS + PPSC_SEQ + + SELECT PROP_ID + , PROP_VERSION + , PROJ_ID + , ACM_CODE + , BGM_CODE + , PPSC_LEVEL + , PPSC_SEQ + , PPSC_COMMENT + , PPSC_COMMENT_BY + , PPSC_COMMENT_TIME + + FROM PROPOSALS_COMMENTS + + + + + + + + + + + + + + + + + + APP + PROPOSALS_COMMENTS + PROJ_ID,PROP_ID,PPSC_SEQ + + SELECT PROP_ID + , PROP_VERSION + , PROJ_ID + , ACM_CODE + , BGM_CODE + , PPSC_LEVEL + , PPSC_SEQ + , PPSC_COMMENT + , PPSC_COMMENT_BY + , PPSC_COMMENT_TIME + + FROM PROPOSALS_COMMENTS + + ORDER BY PROJ_ID,PROP_ID,PPSC_SEQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build.gradle.kts b/build.gradle.kts index d783f2b..da02ab1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { id("org.jetbrains.intellij.platform") version "2.7.0" } group = "com.sdk.dynform.tools" -version = "3.2.9" +version = "3.3.0" repositories { mavenCentral() @@ -38,6 +38,12 @@ intellijPlatform { } changeNotes = """ + [3.3.0] + + Cross-file Support: Implemented comprehensive cross-file completion, reference (Go to Definition), and validation for fields and sections across included .frml files. + Performance Optimization: Optimized resource discovery logic by leveraging IntelliJ's indexing (ReferencesSearch), resolving IDE freeze issues in large projects. + Refined Validation: Excluded <ROW> tags from field definition requirements, as they are structural elements. + [3.2.9] Extended I18n Support: Added Inlay Hints, Autocomplete, and Reference support for the MESSAGE attribute in <UNIQ-CHECK> and other tags. diff --git a/src/main/java/com/sdk/dynform/tools/dynform/DynFormAnnotator.java b/src/main/java/com/sdk/dynform/tools/dynform/DynFormAnnotator.java index 4cbe8d6..2fc47aa 100644 --- a/src/main/java/com/sdk/dynform/tools/dynform/DynFormAnnotator.java +++ b/src/main/java/com/sdk/dynform/tools/dynform/DynFormAnnotator.java @@ -9,71 +9,47 @@ import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlTag; import org.jetbrains.annotations.NotNull; +import com.intellij.psi.PsiFile; +import java.util.HashSet; + public class DynFormAnnotator implements Annotator { @Override public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { if (!(element instanceof XmlAttributeValue attrValue)) return; - + PsiElement parent = attrValue.getParent(); if (!(parent instanceof XmlAttribute attr)) return; - - if (!"NAME".equals(attr.getName())) return; - - PsiElement tagElem = attr.getParent(); - if (!(tagElem instanceof XmlTag tag) || !"FIELD".equals(tag.getName())) return; - // Check if this FIELD is inside a LAYOUT tag + String attrName = attr.getName(); + PsiElement tagElem = attr.getParent(); + if (!(tagElem instanceof XmlTag tag)) return; + + String tagName = tag.getName(); + boolean isField = "FIELD".equals(tagName) && "NAME".equals(attrName); + boolean isContainer = ("SECTION".equals(tagName) || "SECTIONS".equals(tagName)) && "ID".equals(attrName); + + if (!isField && !isContainer) return; + + // Check if this tag is inside a LAYOUT tag XmlTag layoutTag = tag.getParentTag(); while (layoutTag != null && !"LAYOUT".equals(layoutTag.getName())) { layoutTag = layoutTag.getParentTag(); } - + if (layoutTag == null) return; - // The "Owner" of the LAYOUT (e.g., FORM_ENTRY, FILTERS, GRID-EDITOR) + // The "Owner" of the LAYOUT (e.g., FORM_ENTRY, FILTERS, GRID-EDITOR, FORM_BROWSE, GRID-LIST) XmlTag ownerTag = layoutTag.getParentTag(); if (ownerTag == null) return; - + String fieldName = attrValue.getValue(); if (fieldName == null || fieldName.isEmpty()) return; - // Look for the FIELDS tag that is a sibling of the current LAYOUT - XmlTag fieldsTag = ownerTag.findFirstSubTag("FIELDS"); - if (fieldsTag == null) { - holder.newAnnotation(HighlightSeverity.ERROR, "No definition found in <" + ownerTag.getName() + ">") - .range(attrValue.getTextRange()) - .create(); - return; - } - - if (!isFieldDefined(fieldsTag, fieldName)) { - holder.newAnnotation(HighlightSeverity.ERROR, "Field '" + fieldName + "' is not defined in of <" + ownerTag.getName() + ">") + PsiFile currentFile = tag.getContainingFile(); + if (DynFormPathUtils.findFieldInFormContext(currentFile, ownerTag.getName(), fieldName, new HashSet<>()) == null) { + holder.newAnnotation(HighlightSeverity.ERROR, (isField ? "Field '" : "Section/Row '") + fieldName + "' is not defined in of <" + ownerTag.getName() + ">") .range(attrValue.getTextRange()) .create(); } } - - private boolean isFieldDefined(XmlTag fieldsTag, String name) { - for (XmlTag field : fieldsTag.findSubTags("FIELD")) { - if (name.equals(field.getAttributeValue("NAME"))) { - return true; - } - } - // Also check inside SECTIONS or ROWS if any - for (XmlTag sub : fieldsTag.getSubTags()) { - if ("SECTION".equals(sub.getName()) || "ROW".equals(sub.getName())) { - if (isFieldDefined(sub, name)) return true; - } - } - return false; - } - - private static boolean hasAncestorWithName(XmlTag tag, String name) { - XmlTag current = tag.getParentTag(); - while (current != null) { - if (name.equals(current.getName())) return true; - current = current.getParentTag(); - } - return false; - } } diff --git a/src/main/java/com/sdk/dynform/tools/dynform/DynFormCompletionContributor.java b/src/main/java/com/sdk/dynform/tools/dynform/DynFormCompletionContributor.java index a873479..a4d8cf5 100644 --- a/src/main/java/com/sdk/dynform/tools/dynform/DynFormCompletionContributor.java +++ b/src/main/java/com/sdk/dynform/tools/dynform/DynFormCompletionContributor.java @@ -85,10 +85,34 @@ public class DynFormCompletionContributor extends CompletionContributor { } if (formContainer != null) { - addFieldsInTagRecursive(formContainer, resultSet); + DynFormPathUtils.addFieldsInTagRecursive(formContainer, resultSet); + } + + // เพิ่มเติม: เสนอฟิลด์จากไฟล์ที่ include ไฟล์นี้ (Includers) + List includers = DynFormPathUtils.findIncluders(parameters.getOriginalFile()); + for (PsiFile includer : includers) { + if (includer instanceof XmlFile xmlFile) { + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag != null) { + for (XmlTag formTag : rootTag.findSubTags("FORM")) { + for (XmlTag entryTag : formTag.findSubTags("FORM_ENTRY")) { + XmlTag fieldsTag = entryTag.findFirstSubTag("FIELDS"); + if (fieldsTag != null) { + DynFormPathUtils.addFieldsInTagRecursive(fieldsTag, resultSet); + } + } + for (XmlTag browseTag : formTag.findSubTags("FORM_BROWSE")) { + XmlTag fieldsTag = browseTag.findFirstSubTag("FIELDS"); + if (fieldsTag != null) { + DynFormPathUtils.addFieldsInTagRecursive(fieldsTag, resultSet); + } + } + } + } + } } } else { - // เสนอฟิลด์ภายใต้ LAYOUT container เดียวกัน + // เสนอฟิลด์ภายใต้ LAYOUT container เดียวกัน (ข้ามไฟล์ได้) XmlTag layoutTag = PsiTreeUtil.getParentOfType(position, XmlTag.class); while (layoutTag != null && !"LAYOUT".equals(layoutTag.getName())) { layoutTag = layoutTag.getParentTag(); @@ -96,10 +120,8 @@ public class DynFormCompletionContributor extends CompletionContributor { if (layoutTag == null) return; XmlTag containerTag = layoutTag.getParentTag(); if (containerTag == null) return; - XmlTag fieldsTag = containerTag.findFirstSubTag("FIELDS"); - if (fieldsTag != null) { - addFieldsInTagRecursive(fieldsTag, resultSet); - } + + DynFormPathUtils.addFieldsInFormContext(parameters.getOriginalFile(), containerTag.getName(), resultSet, new HashSet<>()); } } }); @@ -120,10 +142,10 @@ public class DynFormCompletionContributor extends CompletionContributor { if (isAjaxOption) { PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(parameters.getOriginalFile()); if (ajaxFile != null) { - addDatasetsInFile(ajaxFile, resultSet); + DynFormPathUtils.addDatasetsInFile(ajaxFile, resultSet); } } else { - addDatasetsInFileRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>(), false); + DynFormPathUtils.addDatasetsInFileRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>(), false); } } }); @@ -164,7 +186,7 @@ public class DynFormCompletionContributor extends CompletionContributor { if (parentDataset != null && "DATASET".equals(parentDataset.getName())) { XmlTag fieldsTag = parentDataset.findFirstSubTag("FIELDS"); if (fieldsTag != null) { - addFieldsInTagRecursive(fieldsTag, resultSet); + DynFormPathUtils.addFieldsInTagRecursive(fieldsTag, resultSet); } } } @@ -205,7 +227,7 @@ public class DynFormCompletionContributor extends CompletionContributor { if (datasetId.equals(datasetTag.getAttributeValue("ID"))) { XmlTag fieldsTag = datasetTag.findFirstSubTag("FIELDS"); if (fieldsTag != null) { - addFieldsInTagRecursive(fieldsTag, resultSet); + DynFormPathUtils.addFieldsInTagRecursive(fieldsTag, resultSet); } } } @@ -223,7 +245,7 @@ public class DynFormCompletionContributor extends CompletionContributor { protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet resultSet) { - addGridsInFileRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>()); + DynFormPathUtils.addGridsInFileRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>()); } }); @@ -239,7 +261,7 @@ public class DynFormCompletionContributor extends CompletionContributor { protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet resultSet) { - addFormFieldsForNameRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>()); + DynFormPathUtils.addFormFieldsForNameRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>()); } }); @@ -284,171 +306,16 @@ public class DynFormCompletionContributor extends CompletionContributor { }); } - private void addFieldsInTagRecursive(XmlTag container, @NotNull CompletionResultSet resultSet) { - for (XmlTag subTag : container.getSubTags()) { - if ("FIELD".equals(subTag.getName())) { - String name = subTag.getAttributeValue("NAME"); - if (name != null && !name.isEmpty()) { - resultSet.addElement(LookupElementBuilder.create(name) - .withIcon(com.intellij.icons.AllIcons.Nodes.Field) - .withTypeText(subTag.getAttributeValue("TYPE"))); - } - } else { - if (("SECTION".equals(subTag.getName()) || "FIELDS".equals(subTag.getName())) && subTag.getAttributeValue("ID") != null) { - String id = subTag.getAttributeValue("ID"); - resultSet.addElement(LookupElementBuilder.create(id) - .withIcon(com.intellij.icons.AllIcons.Nodes.Package) - .withItemTextForeground(java.awt.Color.BLUE)); - } - addFieldsInTagRecursive(subTag, resultSet); - } - } - } - - private void addDatasetsInFileRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set visited, boolean includeAjax) { - if (file == null || !visited.add(file)) return; - - if (includeAjax) { - // สำหรับ AJAX-OPTION ให้หาใน ajax.xml เท่านั้น - PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(file); - if (ajaxFile != null) { - addDatasetsInFile(ajaxFile, resultSet); - } - return; - } - - // 1. Add datasets from current file - addDatasetsInFile(file, resultSet); - - // 2. Add datasets from files that INCLUDE this file (Parent/Main Files) - if (visited.size() == 1) { - List includers = DynFormPathUtils.findIncluders(file); - for (PsiFile includer : includers) { - if (visited.add(includer)) { - addDatasetsInFile(includer, resultSet); - } - } - } - - // 3. Add datasets from included files (Downward) - if (file instanceof XmlFile xmlFile) { - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag != null) { - XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); - if (includesTag != null) { - for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { - String path = includeTag.getAttributeValue("FILE"); - if (path != null) { - PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path); - if (includedFile != null && !visited.contains(includedFile)) { - addDatasetsInFileRecursive(includedFile, resultSet, visited, includeAjax); - } - } - } - } - } - } - - // 4. Add datasets from all .frml files in module (fallback) - if (visited.size() <= 2) { - for (PsiFile frmlFile : DynFormPathUtils.getAllFrmlFiles(file)) { - if (visited.add(frmlFile)) { - addDatasetsInFile(frmlFile, resultSet); - } - } - } - } - - private void addDatasetsInFile(PsiFile file, @NotNull CompletionResultSet resultSet) { - if (!(file instanceof XmlFile xmlFile)) return; - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag == null) return; - - XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS"); - if (datasetsContainer == null) datasetsContainer = rootTag; - - for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) { - String id = datasetTag.getAttributeValue("ID"); - if (id != null && !id.isEmpty()) { - resultSet.addElement(LookupElementBuilder.create(id) - .withIcon(com.intellij.icons.AllIcons.Nodes.DataTables) - .withTypeText(datasetTag.getAttributeValue("TABLENAME"))); - } - } - } - - private void addGridsInFileRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set visited) { - if (file == null || !visited.add(file)) return; - - // 1. Add grids from current file - addGridsInFile(file, resultSet); - - // 2. Add grids from files that INCLUDE this file (Parent/Main Files) - if (visited.size() == 1) { - List includers = DynFormPathUtils.findIncluders(file); - for (PsiFile includer : includers) { - if (visited.add(includer)) { - addGridsInFile(includer, resultSet); - } - } - } - - // 3. Add grids from included files (Downward) - if (file instanceof XmlFile xmlFile) { - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag != null) { - XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); - if (includesTag != null) { - for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { - String path = includeTag.getAttributeValue("FILE"); - if (path != null) { - PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path); - if (includedFile != null && !visited.contains(includedFile)) { - addGridsInFileRecursive(includedFile, resultSet, visited); - } - } - } - } - } - } - - // 4. Add grids from all .frml files in module (fallback) - if (visited.size() <= 2) { - for (PsiFile frmlFile : DynFormPathUtils.getAllFrmlFiles(file)) { - if (visited.add(frmlFile)) { - addGridsInFile(frmlFile, resultSet); - } - } - } - } - - private void addGridsInFile(PsiFile file, @NotNull CompletionResultSet resultSet) { - if (!(file instanceof XmlFile xmlFile)) return; - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag == null) return; - - XmlTag dataGridsTag = rootTag.findFirstSubTag("DATA-GRIDS"); - if (dataGridsTag != null) { - for (XmlTag gridTag : dataGridsTag.findSubTags("DATA-GRID")) { - String id = gridTag.getAttributeValue("ID"); - if (id != null && !id.isEmpty()) { - resultSet.addElement(LookupElementBuilder.create(id) - .withIcon(com.intellij.icons.AllIcons.Nodes.DataTables)); - } - } - } - } - private void addFieldsFromDatasetId(XmlTag tag, @NotNull CompletionResultSet resultSet) { String datasetId = tag.getAttributeValue("DATASET-ID"); if (datasetId == null || datasetId.isEmpty()) return; PsiFile file = tag.getContainingFile(); - PsiElement datasetElement = findDatasetElement(file, datasetId); + PsiElement datasetElement = DynFormPathUtils.findDatasetElement(file, datasetId); if (datasetElement instanceof XmlTag datasetTag) { XmlTag fieldsTag = datasetTag.findFirstSubTag("FIELDS"); if (fieldsTag != null) { - addFieldsInTagRecursive(fieldsTag, resultSet); + DynFormPathUtils.addFieldsInTagRecursive(fieldsTag, resultSet); } } } @@ -469,11 +336,11 @@ public class DynFormCompletionContributor extends CompletionContributor { // 2. Add fields from that DATASET if (dataId != null && !dataId.isEmpty()) { - PsiElement datasetElement = findDatasetElement(gridTag.getContainingFile(), dataId); + PsiElement datasetElement = DynFormPathUtils.findDatasetElement(gridTag.getContainingFile(), dataId); if (datasetElement instanceof XmlTag datasetTag) { XmlTag fieldsTag = datasetTag.findFirstSubTag("FIELDS"); if (fieldsTag != null) { - addFieldsInTagRecursive(fieldsTag, resultSet); + DynFormPathUtils.addFieldsInTagRecursive(fieldsTag, resultSet); } } } @@ -482,98 +349,18 @@ public class DynFormCompletionContributor extends CompletionContributor { if (listTag != null) { XmlTag fieldsTag = listTag.findFirstSubTag("FIELDS"); if (fieldsTag != null) { - addFieldsInTagRecursive(fieldsTag, resultSet); + DynFormPathUtils.addFieldsInTagRecursive(fieldsTag, resultSet); } } XmlTag editorTag = gridTag.findFirstSubTag("GRID-EDITOR"); if (editorTag != null) { XmlTag fieldsTag = editorTag.findFirstSubTag("FIELDS"); if (fieldsTag != null) { - addFieldsInTagRecursive(fieldsTag, resultSet); + DynFormPathUtils.addFieldsInTagRecursive(fieldsTag, resultSet); } } } - private PsiElement findDatasetElement(PsiFile file, String id) { - Set visited = new HashSet<>(); - - // 1. Local - PsiElement found = findDatasetInFile(file, id); - if (found != null) return found; - visited.add(file); - - // 2. Includers - List includers = DynFormPathUtils.findIncluders(file); - for (PsiFile includer : includers) { - found = findDatasetInFile(includer, id); - if (found != null) return found; - visited.add(includer); - } - - // 3. Ajax - PsiFile ajaxXml = DynFormPathUtils.findAjaxXml(file); - if (ajaxXml != null && visited.add(ajaxXml)) { - found = findDatasetInFile(ajaxXml, id); - if (found != null) return found; - } - - // 4. Recursive search - return findDatasetInFileRecursiveForCompletion(file, id, visited); - } - - private PsiElement findDatasetInFile(PsiFile file, String id) { - if (!(file instanceof XmlFile xmlFile)) return null; - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag == null) return null; - - XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS"); - if (datasetsContainer == null) datasetsContainer = rootTag; - - for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) { - if (id.equals(datasetTag.getAttributeValue("ID"))) { - return datasetTag; - } - } - return null; - } - - private PsiElement findDatasetInFileRecursiveForCompletion(PsiFile file, String id, Set visited) { - if (file == null || !visited.add(file)) return null; - if (!(file instanceof XmlFile xmlFile)) return null; - - // Try local (should already be checked by findDatasetElement but for recursion consistency) - PsiElement found = findDatasetInFile(xmlFile, id); - if (found != null) return found; - - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag == null) return null; - - // Downward search - XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); - if (includesTag != null) { - for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { - String path = includeTag.getAttributeValue("FILE"); - if (path != null) { - PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path); - found = findDatasetInFileRecursiveForCompletion(includedFile, id, visited); - if (found != null) return found; - } - } - } - - // Final fallback: Module-wide - if (visited.size() <= 2) { - for (PsiFile frmlFile : DynFormPathUtils.getAllFrmlFiles(file)) { - if (!visited.contains(frmlFile)) { - found = findDatasetInFile(frmlFile, id); - if (found != null) return found; - } - } - } - - return null; - } - private static boolean hasAncestorWithName(XmlTag tag, String name) { XmlTag current = tag.getParentTag(); while (current != null) { @@ -676,60 +463,4 @@ public class DynFormCompletionContributor extends CompletionContributor { } } } - - private void addFormFieldsForNameRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set visited) { - if (file == null || !visited.add(file)) return; - if (!(file instanceof XmlFile xmlFile)) return; - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag == null) return; - - // 1. Search in current file FORM_ENTRY tags - for (XmlTag formTag : rootTag.findSubTags("FORM")) { - for (XmlTag entryTag : formTag.findSubTags("FORM_ENTRY")) { - // Collect hidden fields from FIELDS - XmlTag fieldsTag = entryTag.findFirstSubTag("FIELDS"); - if (fieldsTag != null) { - for (XmlTag fieldTag : fieldsTag.findSubTags("FIELD")) { - if ("HIDDEN".equals(fieldTag.getAttributeValue("INPUTTYPE"))) { - String name = fieldTag.getAttributeValue("NAME"); - if (name != null && !name.isEmpty()) { - resultSet.addElement(LookupElementBuilder.create(name) - .withIcon(com.intellij.icons.AllIcons.Nodes.Field) - .withTypeText("Hidden Field")); - } - } - } - } - // Collect any field/tag with NAME from LAYOUT - XmlTag layoutTag = entryTag.findFirstSubTag("LAYOUT"); - if (layoutTag != null) { - addFieldsInLayoutRecursive(layoutTag, resultSet); - } - } - } - - // 2. Search in included files - XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); - if (includesTag != null) { - for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { - String path = includeTag.getAttributeValue("FILE"); - if (path != null) { - PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path); - addFormFieldsForNameRecursive(includedFile, resultSet, visited); - } - } - } - } - - private void addFieldsInLayoutRecursive(XmlTag container, @NotNull CompletionResultSet resultSet) { - for (XmlTag subTag : container.getSubTags()) { - String name = subTag.getAttributeValue("NAME"); - if (name != null && !name.isEmpty()) { - resultSet.addElement(LookupElementBuilder.create(name) - .withIcon(com.intellij.icons.AllIcons.Nodes.Field) - .withTypeText("Layout: " + subTag.getName())); - } - addFieldsInLayoutRecursive(subTag, resultSet); - } - } } diff --git a/src/main/java/com/sdk/dynform/tools/dynform/DynFormPathUtils.java b/src/main/java/com/sdk/dynform/tools/dynform/DynFormPathUtils.java index ff9da00..70faac7 100644 --- a/src/main/java/com/sdk/dynform/tools/dynform/DynFormPathUtils.java +++ b/src/main/java/com/sdk/dynform/tools/dynform/DynFormPathUtils.java @@ -5,18 +5,203 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; + +import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.icons.AllIcons; +import java.awt.Color; public class DynFormPathUtils { public static final String MODULE_BASE_PATH = "src/main/webapp/WEB-INF/app/module"; + public static void addFieldsInTagRecursive(XmlTag container, @NotNull CompletionResultSet resultSet) { + for (XmlTag subTag : container.getSubTags()) { + if ("FIELD".equals(subTag.getName())) { + String name = subTag.getAttributeValue("NAME"); + if (name != null && !name.isEmpty()) { + resultSet.addElement(LookupElementBuilder.create(name) + .withIcon(AllIcons.Nodes.Field) + .withTypeText(subTag.getAttributeValue("TYPE"))); + } + } else { + if (("SECTION".equals(subTag.getName()) || "FIELDS".equals(subTag.getName())) && subTag.getAttributeValue("ID") != null) { + String id = subTag.getAttributeValue("ID"); + resultSet.addElement(LookupElementBuilder.create(id) + .withIcon(AllIcons.Nodes.Package) + .withItemTextForeground(Color.BLUE)); + } + addFieldsInTagRecursive(subTag, resultSet); + } + } + } + + public static void addFieldsInFormContext(PsiFile file, String containerName, @NotNull CompletionResultSet resultSet, Set visited) { + if (file == null || !visited.add(file)) return; + if (!(file instanceof XmlFile xmlFile)) return; + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) return; + + // 1. Search for (containerName) > FIELDS in this file + List containers = new ArrayList<>(); + collectContainers(rootTag, containerName, containers); + for (XmlTag container : containers) { + XmlTag fieldsTag = container.findFirstSubTag("FIELDS"); + if (fieldsTag != null) { + addFieldsInTagRecursive(fieldsTag, resultSet); + } + } + + // 2. Search in INCLUDES (Downward) + XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); + if (includesTag != null) { + for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { + String path = includeTag.getAttributeValue("FILE"); + if (path != null) { + PsiFile includedFile = findIncludedFile(file, path); + addFieldsInFormContext(includedFile, containerName, resultSet, visited); + } + } + } + + // 3. Search in Includers (Upward) + if (visited.size() == 1) { + List includers = findIncluders(file); + for (PsiFile includer : includers) { + addFieldsInFormContext(includer, containerName, resultSet, visited); + } + } + } + + @Nullable + public static PsiElement findFieldInTag(XmlTag container, String name) { + for (XmlTag subTag : container.getSubTags()) { + if ("FIELD".equals(subTag.getName()) && name.equals(subTag.getAttributeValue("NAME"))) { + XmlAttribute nameAttr = subTag.getAttribute("NAME"); + return nameAttr != null ? nameAttr.getValueElement() : subTag; + } + if (("SECTION".equals(subTag.getName()) || "SECTIONS".equals(subTag.getName())) && name.equals(subTag.getAttributeValue("ID"))) { + XmlAttribute idAttr = subTag.getAttribute("ID"); + return idAttr != null ? idAttr.getValueElement() : subTag; + } + PsiElement found = findFieldInTag(subTag, name); + if (found != null) return found; + } + return null; + } + + public static void collectContainers(XmlTag parent, String containerName, List results) { + if (containerName.equals(parent.getName())) { + results.add(parent); + } else { + for (XmlTag subTag : parent.getSubTags()) { + collectContainers(subTag, containerName, results); + } + } + } + + @Nullable + public static PsiElement findFieldInFormContext(PsiFile file, String containerName, String fieldName, Set visited) { + if (file == null || !visited.add(file)) return null; + if (!(file instanceof XmlFile xmlFile)) return null; + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) return null; + + // 1. Search in all containers matching containerName anywhere in the file + List containers = new ArrayList<>(); + collectContainers(rootTag, containerName, containers); + for (XmlTag container : containers) { + XmlTag fieldsTag = container.findFirstSubTag("FIELDS"); + if (fieldsTag != null) { + PsiElement found = findFieldInTag(fieldsTag, fieldName); + if (found != null) return found; + } + } + + // 2. Search in INCLUDES (Downward) + XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); + if (includesTag != null) { + for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { + String path = includeTag.getAttributeValue("FILE"); + if (path != null) { + PsiFile includedFile = findIncludedFile(file, path); + PsiElement found = findFieldInFormContext(includedFile, containerName, fieldName, visited); + if (found != null) return found; + } + } + } + + // 3. Search in Includers (Upward) + if (visited.size() == 1) { + List includers = findIncluders(file); + for (PsiFile includer : includers) { + PsiElement found = findFieldInFormContext(includer, containerName, fieldName, visited); + if (found != null) return found; + } + } + + return null; + } + + @Nullable + public static PsiElement findUsageInFormContext(PsiFile file, String containerName, String fieldName, Set visited) { + if (file == null || !visited.add(file)) return null; + if (!(file instanceof XmlFile xmlFile)) return null; + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) return null; + + // 1. Search in all containers matching containerName + List containers = new ArrayList<>(); + collectContainers(rootTag, containerName, containers); + for (XmlTag container : containers) { + XmlTag layoutTag = container.findFirstSubTag("LAYOUT"); + if (layoutTag != null) { + PsiElement found = findFieldInTag(layoutTag, fieldName); + if (found != null) return found; + } + XmlTag titlesTag = container.findFirstSubTag("TITLES"); + if (titlesTag != null) { + PsiElement found = findFieldInTag(titlesTag, fieldName); + if (found != null) return found; + } + } + + // 2. Search in INCLUDES (Downward) + XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); + if (includesTag != null) { + for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { + String path = includeTag.getAttributeValue("FILE"); + if (path != null) { + PsiFile includedFile = findIncludedFile(file, path); + PsiElement found = findUsageInFormContext(includedFile, containerName, fieldName, visited); + if (found != null) return found; + } + } + } + + // 3. Search in Includers (Upward) + if (visited.size() == 1) { + List includers = findIncluders(file); + for (PsiFile includer : includers) { + PsiElement found = findUsageInFormContext(includer, containerName, fieldName, visited); + if (found != null) return found; + } + } + + return null; + } + @Nullable public static VirtualFile getModuleBaseDir(@NotNull Project project) { VirtualFile baseDir = project.getBaseDir(); @@ -155,19 +340,6 @@ public class DynFormPathUtils { return null; } - @NotNull - public static List getAllFrmlFiles(@NotNull PsiFile contextFile) { - List files = new ArrayList<>(); - VirtualFile moduleDir = findModuleDir(contextFile); - if (moduleDir != null) { - VirtualFile frmDir = moduleDir.findFileByRelativePath("view/frm"); - if (frmDir != null) { - collectFrmlFilesRecursive(frmDir, contextFile.getProject(), files); - } - } - return files; - } - @NotNull public static List findIncluders(@NotNull PsiFile includedFile) { List includers = new ArrayList<>(); @@ -175,41 +347,380 @@ public class DynFormPathUtils { VirtualFile includedVFile = includedFile.getVirtualFile(); if (includedVFile == null) return includers; - VirtualFile moduleDir = findModuleDir(includedFile); - if (moduleDir == null) return includers; + // Optimization: Use ReferencesSearch to find files that point to this file via + // This leverages IntelliJ's indices and is MUCH faster than scanning all files manually. + com.intellij.psi.search.searches.ReferencesSearch.search(includedFile).forEach(ref -> { + PsiElement element = ref.getElement(); + if (element != null) { + PsiFile file = element.getContainingFile(); + if (file != null && !includers.contains(file)) { + includers.add(file); + } + } + }); - List allFiles = getAllFrmlFiles(includedFile); - for (PsiFile file : allFiles) { - if (file.equals(includedFile)) continue; - if (!(file instanceof XmlFile xmlFile)) continue; + // Fallback: If for some reason index-based search yields nothing, + // we might do a limited scan, but let's stick to indices for performance to avoid freezing. + return includers; + } + public static void addDatasetsInFileRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set visited, boolean includeAjax) { + if (file == null || !visited.add(file)) return; + + if (includeAjax) { + // สำหรับ AJAX-OPTION ให้หาใน ajax.xml เท่านั้น + PsiFile ajaxFile = findAjaxXml(file); + if (ajaxFile != null) { + addDatasetsInFile(ajaxFile, resultSet); + } + return; + } + + // 1. Add datasets from current file + addDatasetsInFile(file, resultSet); + + // 2. Add datasets from files that INCLUDE this file (Parent/Main Files) + if (visited.size() == 1) { + List includers = findIncluders(file); + for (PsiFile includer : includers) { + if (visited.add(includer)) { + addDatasetsInFile(includer, resultSet); + } + } + } + + // 3. Add datasets from included files (Downward) + if (file instanceof XmlFile xmlFile) { XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag == null) return includers; - - XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); - if (includesTag != null) { - for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { - String path = includeTag.getAttributeValue("FILE"); - if (path != null) { - PsiFile resolved = findIncludedFile(file, path); - if (resolved != null && resolved.getVirtualFile().equals(includedVFile)) { - includers.add(file); + if (rootTag != null) { + XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); + if (includesTag != null) { + for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { + String path = includeTag.getAttributeValue("FILE"); + if (path != null) { + PsiFile includedFile = findIncludedFile(file, path); + if (includedFile != null && !visited.contains(includedFile)) { + addDatasetsInFileRecursive(includedFile, resultSet, visited, includeAjax); + } } } } } } - return includers; } - private static void collectFrmlFilesRecursive(VirtualFile dir, Project project, List files) { - for (VirtualFile child : dir.getChildren()) { - if (child.isDirectory()) { - collectFrmlFilesRecursive(child, project, files); - } else if (child.getName().endsWith(".frml")) { - PsiFile psiFile = PsiManager.getInstance(project).findFile(child); - if (psiFile != null) files.add(psiFile); + public static void addDatasetsInFile(PsiFile file, @NotNull CompletionResultSet resultSet) { + if (!(file instanceof XmlFile xmlFile)) return; + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) return; + + XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS"); + if (datasetsContainer == null) datasetsContainer = rootTag; + + for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) { + String id = datasetTag.getAttributeValue("ID"); + if (id != null && !id.isEmpty()) { + resultSet.addElement(LookupElementBuilder.create(id) + .withIcon(AllIcons.Nodes.DataTables) + .withTypeText(datasetTag.getAttributeValue("TABLENAME"))); } } } + + public static void addGridsInFileRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set visited) { + if (file == null || !visited.add(file)) return; + + // 1. Add grids from current file + addGridsInFile(file, resultSet); + + // 2. Add grids from files that INCLUDE this file (Parent/Main Files) + if (visited.size() == 1) { + List includers = findIncluders(file); + for (PsiFile includer : includers) { + if (visited.add(includer)) { + addGridsInFile(includer, resultSet); + } + } + } + + // 3. Add grids from included files (Downward) + if (file instanceof XmlFile xmlFile) { + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag != null) { + XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); + if (includesTag != null) { + for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { + String path = includeTag.getAttributeValue("FILE"); + if (path != null) { + PsiFile includedFile = findIncludedFile(file, path); + if (includedFile != null && !visited.contains(includedFile)) { + addGridsInFileRecursive(includedFile, resultSet, visited); + } + } + } + } + } + } + } + + public static void addGridsInFile(PsiFile file, @NotNull CompletionResultSet resultSet) { + if (!(file instanceof XmlFile xmlFile)) return; + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) return; + + XmlTag dataGridsTag = rootTag.findFirstSubTag("DATA-GRIDS"); + if (dataGridsTag != null) { + for (XmlTag gridTag : dataGridsTag.findSubTags("DATA-GRID")) { + String id = gridTag.getAttributeValue("ID"); + if (id != null && !id.isEmpty()) { + resultSet.addElement(LookupElementBuilder.create(id) + .withIcon(AllIcons.Nodes.DataTables)); + } + } + } + } + + @Nullable + public static PsiElement findDatasetElement(PsiFile file, String id) { + Set visited = new HashSet<>(); + + // 1. Local + PsiElement found = findDatasetInFile(file, id); + if (found != null) return found; + visited.add(file); + + // 2. Includers + List includers = findIncluders(file); + for (PsiFile includer : includers) { + found = findDatasetInFile(includer, id); + if (found != null) return found; + visited.add(includer); + } + + // 3. Ajax + PsiFile ajaxXml = findAjaxXml(file); + if (ajaxXml != null && visited.add(ajaxXml)) { + found = findDatasetInFile(ajaxXml, id); + if (found != null) return found; + } + + // 4. Recursive search + return findDatasetInFileRecursiveForCompletion(file, id, visited); + } + + @Nullable + public static PsiElement findDatasetInFile(PsiFile file, String id) { + if (!(file instanceof XmlFile xmlFile)) return null; + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) return null; + + XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS"); + if (datasetsContainer == null) datasetsContainer = rootTag; + + for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) { + if (id.equals(datasetTag.getAttributeValue("ID"))) { + return datasetTag; + } + } + return null; + } + + @Nullable + public static PsiElement findDatasetInFileRecursiveForCompletion(PsiFile file, String id, Set visited) { + if (file == null || !visited.add(file)) return null; + if (!(file instanceof XmlFile xmlFile)) return null; + + // Try local + PsiElement found = findDatasetInFile(xmlFile, id); + if (found != null) return found; + + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) return null; + + // Downward search + XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); + if (includesTag != null) { + for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { + String path = includeTag.getAttributeValue("FILE"); + if (path != null) { + PsiFile includedFile = findIncludedFile(file, path); + found = findDatasetInFileRecursiveForCompletion(includedFile, id, visited); + if (found != null) return found; + } + } + } + + return null; + } + + public static void addFormFieldsForNameRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set visited) { + if (file == null || !visited.add(file)) return; + if (!(file instanceof XmlFile xmlFile)) return; + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) return; + + // 1. Search in current file FORM_ENTRY tags + for (XmlTag formTag : rootTag.findSubTags("FORM")) { + for (XmlTag entryTag : formTag.findSubTags("FORM_ENTRY")) { + // Collect hidden fields from FIELDS + XmlTag fieldsTag = entryTag.findFirstSubTag("FIELDS"); + if (fieldsTag != null) { + for (XmlTag fieldTag : fieldsTag.findSubTags("FIELD")) { + if ("HIDDEN".equals(fieldTag.getAttributeValue("INPUTTYPE"))) { + String name = fieldTag.getAttributeValue("NAME"); + if (name != null && !name.isEmpty()) { + resultSet.addElement(LookupElementBuilder.create(name) + .withIcon(AllIcons.Nodes.Field) + .withTypeText("Hidden Field")); + } + } + } + } + // Collect any field/tag with NAME from LAYOUT + XmlTag layoutTag = entryTag.findFirstSubTag("LAYOUT"); + if (layoutTag != null) { + addFieldsInLayoutRecursive(layoutTag, resultSet); + } + } + } + + // 2. Search in included files + XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); + if (includesTag != null) { + for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { + String path = includeTag.getAttributeValue("FILE"); + if (path != null) { + PsiFile includedFile = findIncludedFile(file, path); + addFormFieldsForNameRecursive(includedFile, resultSet, visited); + } + } + } + } + + public static void addFieldsInLayoutRecursive(XmlTag container, @NotNull CompletionResultSet resultSet) { + for (XmlTag subTag : container.getSubTags()) { + String name = subTag.getAttributeValue("NAME"); + if (name != null && !name.isEmpty()) { + resultSet.addElement(LookupElementBuilder.create(name) + .withIcon(AllIcons.Nodes.Field) + .withTypeText("Layout: " + subTag.getName())); + } + addFieldsInLayoutRecursive(subTag, resultSet); + } + } + + @Nullable + public static PsiElement findFormFieldByNameRecursive(PsiFile file, String name, Set visited) { + if (file == null || !visited.add(file)) return null; + if (!(file instanceof XmlFile xmlFile)) return null; + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) return null; + + // 1. Search in current file FORM_ENTRY tags + for (XmlTag formTag : rootTag.findSubTags("FORM")) { + for (XmlTag entryTag : formTag.findSubTags("FORM_ENTRY")) { + // Check hidden fields in FIELDS + XmlTag fieldsTag = entryTag.findFirstSubTag("FIELDS"); + if (fieldsTag != null) { + for (XmlTag fieldTag : fieldsTag.findSubTags("FIELD")) { + if ("HIDDEN".equals(fieldTag.getAttributeValue("INPUTTYPE")) && name.equals(fieldTag.getAttributeValue("NAME"))) { + XmlAttribute nameAttr = fieldTag.getAttribute("NAME"); + return nameAttr != null ? nameAttr.getValueElement() : fieldTag; + } + } + } + // Check fields in LAYOUT + XmlTag layoutTag = entryTag.findFirstSubTag("LAYOUT"); + if (layoutTag != null) { + PsiElement found = findFieldInTag(layoutTag, name); + if (found != null) return found; + } + } + } + + // 2. Search in included files + XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); + if (includesTag != null) { + for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { + String path = includeTag.getAttributeValue("FILE"); + if (path != null) { + PsiFile includedFile = findIncludedFile(file, path); + PsiElement found = findFormFieldByNameRecursive(includedFile, name, visited); + if (found != null) return found; + } + } + } + return null; + } + + @Nullable + public static PsiElement findGridInFile(PsiFile file, String id) { + if (!(file instanceof XmlFile xmlFile)) return null; + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) return null; + + XmlTag dataGridsTag = rootTag.findFirstSubTag("DATA-GRIDS"); + if (dataGridsTag != null) { + for (XmlTag gridTag : dataGridsTag.findSubTags("DATA-GRID")) { + if (id.equals(gridTag.getAttributeValue("ID"))) { + return gridTag; + } + } + } + return null; + } + + @Nullable + public static PsiElement findGridElement(PsiFile file, String id) { + Set visited = new HashSet<>(); + + // 1. Local + PsiElement found = findGridInFile(file, id); + if (found != null) return found; + visited.add(file); + + // 2. Includers + List includers = findIncluders(file); + for (PsiFile includer : includers) { + found = findGridInFile(includer, id); + if (found != null) return found; + visited.add(includer); + } + + // 3. Recursive + return findGridInIncludesRecursive(file, id, visited); + } + + @Nullable + private static PsiElement findGridInIncludesRecursive(PsiFile file, String id, Set visited) { + if (file == null || !visited.add(file) || !(file instanceof XmlFile xmlFile)) return null; + + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) return null; + + XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); + if (includesTag != null) { + for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { + String path = includeTag.getAttributeValue("FILE"); + if (path != null) { + PsiFile includedFile = findIncludedFile(file, path); + PsiElement found = findGridInFile(includedFile, id); + if (found != null) return found; + found = findGridInIncludesRecursive(includedFile, id, visited); + if (found != null) return found; + } + } + } + + return null; + } + + public static boolean hasAncestorWithName(XmlTag tag, String name) { + XmlTag current = tag.getParentTag(); + while (current != null) { + if (name.equals(current.getName())) return true; + current = current.getParentTag(); + } + return false; + } } diff --git a/src/main/java/com/sdk/dynform/tools/dynform/DynFormReferenceContributor.java b/src/main/java/com/sdk/dynform/tools/dynform/DynFormReferenceContributor.java index 1b1c6f3..456a584 100644 --- a/src/main/java/com/sdk/dynform/tools/dynform/DynFormReferenceContributor.java +++ b/src/main/java/com/sdk/dynform/tools/dynform/DynFormReferenceContributor.java @@ -56,7 +56,7 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { // XML Reference Provider for Field/Section NAME/ID registrar.registerReferenceProvider(XmlPatterns.xmlAttributeValue() .withParent(XmlPatterns.xmlAttribute().withName("NAME", "ID", "TARGET") - .withParent(XmlPatterns.xmlTag().withName("FIELD", "SECTION"))), + .withParent(XmlPatterns.xmlTag().withName("FIELD", "SECTION", "SECTIONS"))), new PsiReferenceProvider() { @NotNull @Override @@ -74,12 +74,12 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { } // เคส 1: อยู่ใน LAYOUT หรือ TITLES -> ลิงก์ไปหา FIELDS (นิยาม) - if (hasAncestorWithName(tag, "LAYOUT") || hasAncestorWithName(tag, "TITLES")) { + if (DynFormPathUtils.hasAncestorWithName(tag, "LAYOUT") || DynFormPathUtils.hasAncestorWithName(tag, "TITLES")) { return new PsiReference[]{new DynFormFieldDefinitionReference(attrValue, new TextRange(1, value.length() + 1), value)}; } // เคส 2: อยู่ใน FIELDS (นิยาม) -> ลิงก์กลับไปหา LAYOUT หรือ TITLES (การใช้งาน) - if (hasAncestorWithName(tag, "FIELDS")) { + if (DynFormPathUtils.hasAncestorWithName(tag, "FIELDS")) { return new PsiReference[]{new DynFormFieldUsageReference(attrValue, new TextRange(1, value.length() + 1), value)}; } @@ -123,7 +123,7 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { String tagName = tag.getName(); // Only process DATASET tag if it's inside FOREIGN-DATASETS - if ("DATASET".equals(tagName) && !hasAncestorWithName(tag, "FOREIGN-DATASETS")) { + if ("DATASET".equals(tagName) && !DynFormPathUtils.hasAncestorWithName(tag, "FOREIGN-DATASETS")) { return PsiReference.EMPTY_ARRAY; } @@ -263,15 +263,6 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { }); } - private static boolean hasAncestorWithName(XmlTag tag, String name) { - XmlTag current = tag.getParentTag(); - while (current != null) { - if (name.equals(current.getName())) return true; - current = current.getParentTag(); - } - return false; - } - @Nullable private PsiNewExpression getNewDynFormExpression(PsiLiteralExpression literal) { PsiElement parent = literal.getParent(); @@ -328,15 +319,23 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { } @Nullable @Override public PsiElement resolve() { XmlTag container = PsiTreeUtil.getParentOfType(myElement, XmlTag.class); - while (container != null && !"FORM_ENTRY".equals(container.getName()) && !"FORM_BROWSE".equals(container.getName()) && !"GRID-LIST".equals(container.getName())) { + while (container != null && + !"FORM_ENTRY".equals(container.getName()) && + !"FORM_BROWSE".equals(container.getName()) && + !"GRID-LIST".equals(container.getName()) && + !"GRID-EDITOR".equals(container.getName()) && + !"FILTERS".equals(container.getName())) { container = container.getParentTag(); } if (container == null) { - XmlFile file = (XmlFile) myElement.getContainingFile(); - container = file.getRootTag(); + // If not in a specific container, search in all known containers across context + PsiElement found = DynFormPathUtils.findFieldInFormContext(myElement.getContainingFile(), "FORM_ENTRY", fieldName, new HashSet<>()); + if (found != null) return found; + return DynFormPathUtils.findFieldInFormContext(myElement.getContainingFile(), "FORM_BROWSE", fieldName, new HashSet<>()); } - if (container == null) return null; - return findFieldInTag(container, fieldName); + + // Search in current container's FIELDS across context + return DynFormPathUtils.findFieldInFormContext(myElement.getContainingFile(), container.getName(), fieldName, new HashSet<>()); } } @@ -353,28 +352,19 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { XmlTag tag = PsiTreeUtil.getParentOfType(myElement, XmlTag.class); if (tag == null) return null; - // หา container (FORM_ENTRY, FORM_BROWSE, GRID-LIST หรือ FILTERS) + // หา container (FORM_ENTRY, FORM_BROWSE, GRID-LIST, GRID-EDITOR หรือ FILTERS) XmlTag container = tag; while (container != null && - !"LAYOUT".equals(container.getName()) && - !"TITLES".equals(container.getName()) && !"FORM_ENTRY".equals(container.getName()) && !"FORM_BROWSE".equals(container.getName()) && !"GRID-LIST".equals(container.getName()) && + !"GRID-EDITOR".equals(container.getName()) && !"FILTERS".equals(container.getName())) { container = container.getParentTag(); } - if (container != null && ("LAYOUT".equals(container.getName()) || "TITLES".equals(container.getName()))) { - container = container.getParentTag(); - } - if (container == null) return null; - - XmlTag fieldsTag = container.findFirstSubTag("FIELDS"); - if (fieldsTag == null) return null; - - return findFieldInTag(fieldsTag, fieldName); + return DynFormPathUtils.findFieldInFormContext(myElement.getContainingFile(), container.getName(), fieldName, new HashSet<>()); } } @@ -401,41 +391,10 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { XmlTag containerTag = fieldsTag.getParentTag(); if (containerTag == null) return null; - // หาใน LAYOUT ก่อน - XmlTag layoutTag = containerTag.findFirstSubTag("LAYOUT"); - if (layoutTag != null) { - PsiElement found = findFieldInTag(layoutTag, fieldName); - if (found != null) return found; - } - - // ถ้าไม่เจอใน LAYOUT ให้หาใน TITLES - XmlTag titlesTag = containerTag.findFirstSubTag("TITLES"); - if (titlesTag != null) { - PsiElement found = findFieldInTag(titlesTag, fieldName); - if (found != null) return found; - } - - return null; + return DynFormPathUtils.findUsageInFormContext(myElement.getContainingFile(), containerTag.getName(), fieldName, new HashSet<>()); } } - @Nullable - private static PsiElement findFieldInTag(XmlTag container, String name) { - for (XmlTag subTag : container.getSubTags()) { - if ("FIELD".equals(subTag.getName()) && name.equals(subTag.getAttributeValue("NAME"))) { - XmlAttribute nameAttr = subTag.getAttribute("NAME"); - return nameAttr != null ? nameAttr.getValueElement() : subTag; - } - if (("SECTION".equals(subTag.getName()) || "ROW".equals(subTag.getName())) && name.equals(subTag.getAttributeValue("ID"))) { - XmlAttribute idAttr = subTag.getAttribute("ID"); - return idAttr != null ? idAttr.getValueElement() : subTag; - } - PsiElement found = findFieldInTag(subTag, name); - if (found != null) return found; - } - return null; - } - private static class DynFormDatasetReference extends PsiReferenceBase implements PsiPolyVariantReference { private final String datasetId; private final boolean isAjaxOption; @@ -473,31 +432,15 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { // 3. ค้นหาในไฟล์ที่ถูก INCLUDE (Downward) findDatasetInIncludesRecursive(currentFile, datasetId, results, new HashSet<>()); - if (!results.isEmpty()) return filterOpenFiles(results, myElement.getProject()); - - // 4. ค้นหาแบบ Module-wide (fallback สุดท้าย) - List allFiles = DynFormPathUtils.getAllFrmlFiles(currentFile); - for (PsiFile file : allFiles) { - if (file.equals(currentFile) || includers.contains(file)) continue; - findDatasetInFile(file, datasetId, results); - } return filterOpenFiles(results, myElement.getProject()); } private void findDatasetInFile(PsiFile file, String id, List results) { - if (!(file instanceof XmlFile xmlFile)) return; - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag == null) return; - - XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS"); - if (datasetsContainer == null) datasetsContainer = rootTag; - - for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) { - if (id.equals(datasetTag.getAttributeValue("ID"))) { - XmlAttribute idAttr = datasetTag.getAttribute("ID"); - results.add(new PsiElementResolveResult(idAttr != null ? idAttr.getValueElement() : datasetTag)); - } + PsiElement found = DynFormPathUtils.findDatasetInFile(file, id); + if (found instanceof XmlTag datasetTag) { + XmlAttribute idAttr = datasetTag.getAttribute("ID"); + results.add(new PsiElementResolveResult(idAttr != null ? idAttr.getValueElement() : datasetTag)); } } @@ -573,7 +516,7 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { if (parentDataset != null && "DATASET".equals(parentDataset.getName())) { XmlTag fieldsTag = parentDataset.findFirstSubTag("FIELDS"); if (fieldsTag != null) { - PsiElement found = findFieldInTag(fieldsTag, fieldName); + PsiElement found = DynFormPathUtils.findFieldInTag(fieldsTag, fieldName); if (found != null) results.add(new PsiElementResolveResult(found)); } } @@ -605,7 +548,7 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { if (datasetTag != null) { XmlTag fieldsTag = datasetTag.findFirstSubTag("FIELDS"); if (fieldsTag != null) { - PsiElement found = findFieldInTag(fieldsTag, fieldName); + PsiElement found = DynFormPathUtils.findFieldInTag(fieldsTag, fieldName); if (found != null) results.add(new PsiElementResolveResult(found)); } } @@ -642,18 +585,10 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { } private void findDatasetInFile(PsiFile file, String id, List results) { - if (!(file instanceof XmlFile xmlFile)) return; - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag == null) return; - - XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS"); - if (datasetsContainer == null) datasetsContainer = rootTag; - - for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) { - if (id.equals(datasetTag.getAttributeValue("ID"))) { - XmlAttribute idAttr = datasetTag.getAttribute("ID"); - results.add(new PsiElementResolveResult(idAttr != null ? idAttr.getValueElement() : datasetTag)); - } + PsiElement found = DynFormPathUtils.findDatasetInFile(file, id); + if (found instanceof XmlTag datasetTag) { + XmlAttribute idAttr = datasetTag.getAttribute("ID"); + results.add(new PsiElementResolveResult(idAttr != null ? idAttr.getValueElement() : datasetTag)); } } @@ -707,14 +642,6 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { // 3. ค้นหาในไฟล์ที่ถูก INCLUDE (Downward) findGridInIncludesRecursive(currentFile, gridId, results, new HashSet<>()); - if (!results.isEmpty()) return filterOpenFiles(results, myElement.getProject()); - - // 4. ค้นหาแบบ Module-wide (fallback) - List allFiles = DynFormPathUtils.getAllFrmlFiles(currentFile); - for (PsiFile file : allFiles) { - if (file.equals(currentFile) || includers.contains(file)) continue; - findGridInFile(file, gridId, results); - } return filterOpenFiles(results, myElement.getProject()); } @@ -800,7 +727,7 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { if (datasetId.equals(datasetTag.getAttributeValue("ID"))) { XmlTag fieldsTag = datasetTag.findFirstSubTag("FIELDS"); if (fieldsTag != null) { - return findFieldInTag(fieldsTag, fieldName); + return DynFormPathUtils.findFieldInTag(fieldsTag, fieldName); } return datasetTag; } @@ -831,51 +758,7 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { @Nullable @Override public PsiElement resolve() { - return findFieldInFormEntriesRecursive(myElement.getContainingFile(), fieldName, new HashSet<>()); - } - - @Nullable - private PsiElement findFieldInFormEntriesRecursive(PsiFile file, String name, Set visited) { - if (file == null || !visited.add(file)) return null; - if (!(file instanceof XmlFile xmlFile)) return null; - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag == null) return null; - - // 1. Search in current file FORM_ENTRY tags - for (XmlTag formTag : rootTag.findSubTags("FORM")) { - for (XmlTag entryTag : formTag.findSubTags("FORM_ENTRY")) { - // Check hidden fields in FIELDS - XmlTag fieldsTag = entryTag.findFirstSubTag("FIELDS"); - if (fieldsTag != null) { - for (XmlTag fieldTag : fieldsTag.findSubTags("FIELD")) { - if ("HIDDEN".equals(fieldTag.getAttributeValue("INPUTTYPE")) && name.equals(fieldTag.getAttributeValue("NAME"))) { - XmlAttribute nameAttr = fieldTag.getAttribute("NAME"); - return nameAttr != null ? nameAttr.getValueElement() : fieldTag; - } - } - } - // Check fields in LAYOUT - XmlTag layoutTag = entryTag.findFirstSubTag("LAYOUT"); - if (layoutTag != null) { - PsiElement found = findFieldInTag(layoutTag, name); - if (found != null) return found; - } - } - } - - // 2. Search in included files - XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES"); - if (includesTag != null) { - for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) { - String path = includeTag.getAttributeValue("FILE"); - if (path != null) { - PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path); - PsiElement found = findFieldInFormEntriesRecursive(includedFile, name, visited); - if (found != null) return found; - } - } - } - return null; + return DynFormPathUtils.findFormFieldByNameRecursive(myElement.getContainingFile(), fieldName, new HashSet<>()); } } @@ -951,33 +834,17 @@ public class DynFormReferenceContributor extends PsiReferenceContributor { if (dataId != null && !dataId.isEmpty()) { PsiFile file = gridTag.getContainingFile(); - PsiElement datasetElement = findDatasetElement(file, dataId); + PsiElement datasetElement = DynFormPathUtils.findDatasetElement(file, dataId); if (datasetElement instanceof XmlTag datasetTag) { XmlTag fieldsTag = datasetTag.findFirstSubTag("FIELDS"); if (fieldsTag != null) { - return findFieldInTag(fieldsTag, fieldName); + return DynFormPathUtils.findFieldInTag(fieldsTag, fieldName); } } } return null; } - private PsiElement findDatasetElement(PsiFile file, String id) { - if (!(file instanceof XmlFile xmlFile)) return null; - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag == null) return null; - - XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS"); - if (datasetsContainer == null) datasetsContainer = rootTag; - - for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) { - if (id.equals(datasetTag.getAttributeValue("ID"))) { - return datasetTag; - } - } - return null; - } - @Override public Object @NotNull [] getVariants() { return new Object[0];
.frml
<ROW>
MESSAGE
<UNIQ-CHECK>