- Core Logic Enhancements:
- Implement bidirectional field referencing between <FIELDS>, <LAYOUT>, and <TITLES> tags in .frml files, enabling seamless navigation from definitions to usages and vice versa.
- Add robust support for AJAX-OPTION field mapping:
- SRC attribute: Links to field definitions within defs/ajax.xml datasets.
- TARGET attribute: Links to local field definitions within the same form.
- Implement global grid resolution: GRID-ID now searches across the current file and all recursively included files (<INCLUDE>).
- Enhance deep recursive search for fields/sections within nested tags like <SECTION>, <ROW>, and <FIELD-LIST>.
- Path Resolution & Helpers (DynFormPathUtils):
- Added support for module-relative paths starting with # (mapping to view/frm/).
- Added support for cross-module paths starting with / (mapping to WEB-INF/app/module/{module}/view/frm/).
- Implemented auto-correction for common keyboard typos (Thai 'ิ' instead of /).
- Added specialized helpers for locating ajax.xml and included files within the framework's structure.
- Smart Completion Enhancements:
- Added context-aware completion for TARGET and SRC fields in AJAX update-fields.
- Enabled global GRID-ID completion by scanning all included resources.
- Improved dataset completion to include both local and AJAX-defined datasets.
- Test Resources:
- Added a comprehensive set of real-world examples (bdgt04, bdgt05, bdgt06) in DevResources/full-examples/ to validate complex cross-module and master-detail scenarios.
264 lines
11 KiB
XML
264 lines
11 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<FORMS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/#dynf_form_def.xsd">
|
|
<DATASETS>
|
|
<DATASET ID="DS-PROJECT_BUDGET_ITEMS">
|
|
<SCHEMA>APP</SCHEMA>
|
|
<TABLENAME>PROJECT_BUDGET_ITEMS</TABLENAME>
|
|
<KEYFIELDS>PROJ_ID,BGM_CODE,PBDG_ID</KEYFIELDS>
|
|
<SQL>
|
|
<SELECT>
|
|
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
|
|
</SELECT>
|
|
<FROM>
|
|
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
|
|
</FROM>
|
|
<ORDER>ORDER BY ACTT.ACM_SEQ</ORDER>
|
|
</SQL>
|
|
<SAVE-FILTER>
|
|
<FIELD NAME="NODE_TYPE" VALUE="[C,P]" ACTION="EXCLUDE"/>
|
|
</SAVE-FILTER>
|
|
<FIELDS>
|
|
<FIELD NAME="PROJ_ID" TYPE="TEXT" LABEL="รหัสอ้างอิงโครงการ" WIDTH="32"/>
|
|
<FIELD NAME="PBDG_ID" TYPE="TEXT" LABEL="รหัสงบประมาณโครงการ" WIDTH="32" FORM-NAME="ACM_CODE"/>
|
|
<FIELD NAME="BGM_CODE" TYPE="TEXT" LABEL="รหัสหมวดงบประมาณ" WIDTH="20"/>
|
|
<FIELD NAME="PBGI_QT_01" TYPE="NUMBER" LABEL="จำนวนไตรมาส 1" WIDTH="15"/>
|
|
<FIELD NAME="PBGI_QT_02" TYPE="NUMBER" LABEL="จำนวนไตรมาส 2" WIDTH="15"/>
|
|
<FIELD NAME="PBGI_QT_03" TYPE="NUMBER" LABEL="จำนวนไตรมาส 3" WIDTH="15"/>
|
|
<FIELD NAME="PBGI_QT_04" TYPE="NUMBER" LABEL="จำนวนไตรมาส 4" WIDTH="15"/>
|
|
<FIELD NAME="PBGI_QTY" TYPE="NUMBER" LABEL="จำนวน" WIDTH="10"/>
|
|
<FIELD NAME="PBGI_COST" TYPE="NUMBER" LABEL="ราคาต่อหน่วย" WIDTH="15"/>
|
|
<FIELD NAME="PBGI_FREQ" TYPE="NUMBER" LABEL="ความถี่/จำนวนครั้ง" WIDTH="10"/>
|
|
<FIELD NAME="PBGI_ASSET_TYPE" TYPE="TEXT" LABEL="ประเภทสินทรัพย์" WIDTH="10"/>
|
|
<FIELD NAME="PBGI_REPLACE" TYPE="NUMBER" LABEL="ทดแทนของเดิม" WIDTH="10"/>
|
|
<FIELD NAME="PBGI_ADDITION" TYPE="NUMBER" LABEL="เพิ่มเติม" WIDTH="10"/>
|
|
<FIELD NAME="PBGI_ACQUIRE" TYPE="NUMBER" LABEL="จัดหาใหม่" WIDTH="10"/>
|
|
<FIELD NAME="PBGI_TOTAL" TYPE="NUMBER" LABEL="รวมเป็นเงิน" WIDTH="15"/>
|
|
<FIELD NAME="PBGI_REASON" TYPE="TEXT" LABEL="เหตุผลความจำเป็น" WIDTH="4000"/>
|
|
</FIELDS>
|
|
</DATASET>
|
|
</DATASETS>
|
|
|
|
<DATA-GRIDS>
|
|
<DATA-GRID ID="GRID-CONST-BUDGET">
|
|
<MASTER-DATA DATASET-ID="DS-MASTER" MASTER-FIELDS="PROJ_ID" DETAIL-FIELDS="PROJ_ID"/>
|
|
<UNIQ-CHECK CHECK-FIELDS="ACM_CODE" MESSAGE="pctbi.const_code duplicate !"></UNIQ-CHECK>
|
|
<GRID-LIST DATAID="DS-PROJECT_BUDGET_ITEMS" EDIT="Y" DELETE="Y" CLASS="table-primary">
|
|
<KEYFIELDS>PROJ_ID,BGM_CODE</KEYFIELDS>
|
|
<TITLES>
|
|
<ROW>
|
|
<FIELD NAME="ACM_CODE" LABEL="pctbi.const_code" WIDTH="10em" ALIGN="left" ROWS-SPAN="2"/>
|
|
<FIELD NAME="ACM_NAME" LABEL="pctbi.const_name" ALIGN="left" ROWS-SPAN="2"/>
|
|
<FIELD NAME="BUDGET_INFO" LABEL="pctbi.budget" ALIGN="center" COLS-SPAN="3"/>
|
|
</ROW>
|
|
<ROW>
|
|
<FIELD NAME="PBGI_QTY" LABEL="pctbi.qty" WIDTH="5em" ALIGN="right"/>
|
|
<FIELD NAME="PBGI_COST" LABEL="pctbi.cost" WIDTH="6em" ALIGN="right"/>
|
|
<FIELD NAME="PBGI_TOTAL" LABEL="pctbi.total" WIDTH="8em" ALIGN="right"/>
|
|
</ROW>
|
|
</TITLES>
|
|
|
|
<FIELDS>
|
|
<FIELD NAME="PBDG_ID" LABEL="pctbi.const_code" WIDTH="15em" ALIGN="left">
|
|
<DATA-FORMATTER>
|
|
<![CDATA[ (value,row,idx)=>{return $(`<div class="col offset-${row["node_level"]}">${value}</div>`);}]]>
|
|
</DATA-FORMATTER>
|
|
</FIELD>
|
|
<FIELD NAME="ACM_NAME" LABEL="pctbi.const_name" ALIGN="left">
|
|
<DATA-FORMATTER>
|
|
<![CDATA[ (value,row,idx)=>{return $(`<div class="col offset-${row["node_level"]}">${value}</div>`);}]]>
|
|
</DATA-FORMATTER>
|
|
</FIELD>
|
|
<FIELD NAME="PBGI_QTY" LABEL="pbgz.qty" WIDTH="5em" ALIGN="right"/>
|
|
<FIELD NAME="PBGI_COST" LABEL="pbgz.cost" WIDTH="6em" ALIGN="right"/>
|
|
<FIELD NAME="PBGI_TOTAL" LABEL="pbgz.total" WIDTH="8em" ALIGN="right"/>
|
|
<COMMAND-BUTTONS>
|
|
<BUTTONS-FILTER>
|
|
<EDIT><![CDATA[({$ctx,row})=>{return row["node_type"] !== "C"}]]></EDIT>
|
|
<DELETE><![CDATA[({$ctx,row})=>{return row["node_type"] !== "C"}]]></DELETE>
|
|
<VIEW><![CDATA[({$ctx,row})=>{return row["node_type"] !== "C"}]]></VIEW>
|
|
</BUTTONS-FILTER>
|
|
</COMMAND-BUTTONS>
|
|
</FIELDS>
|
|
<FOOTER SHOW="Y"/>
|
|
<SCRIPT>
|
|
<INITIALIZE>
|
|
<![CDATA[
|
|
(ctx)=> {
|
|
ctx.sum_of = {};
|
|
console.log("init grid-budget");
|
|
const fieldFormat = (field,value,row) => {
|
|
if (row["node_type"] === "D" || field === "pbgi_total") {
|
|
return formatNumber(value, 0);
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
const summary = (field, data) => {
|
|
let total = 0;
|
|
for (const row of data) {
|
|
total += (+row[field])||0;
|
|
}
|
|
ctx.sum_of[field] = total;
|
|
|
|
if (ctx.$dataGrid && ctx.$dataGrid.load && !ctx.$dataGrid.reload) {
|
|
ctx.$dataGrid.reload = true;
|
|
for (let id=data.length-1; id>=0; id--) {
|
|
let row = data[id];
|
|
if (row["node_type"] === "C") {
|
|
let grpTotal = 0;
|
|
let mainNode = row["acm_code"];
|
|
let nodes = [];
|
|
let dataNode = $$("main_node="+mainNode,data);
|
|
if (dataNode) {
|
|
nodes.push(dataNode);
|
|
for (let node of nodes.flat()) {
|
|
grpTotal += +node[field];
|
|
}
|
|
data[id][field] = grpTotal;
|
|
}
|
|
}
|
|
}
|
|
ctx.$dataGrid.load(data);
|
|
ctx.$dataGrid.reload = false;
|
|
}
|
|
|
|
return formatNumber(total,0);
|
|
}
|
|
|
|
const fieldList = ["pbgi_qty","pbgi_cost","pbgi_total"];
|
|
for (let field of fieldList) {
|
|
let column = $$(`field=${field}`,ctx.gridColumn.flat());
|
|
column.formatter = (value,row) => {
|
|
return fieldFormat(field, value, row);
|
|
}
|
|
column.footerFormatter = (data)=>{
|
|
return summary(field,data);
|
|
}
|
|
}
|
|
|
|
ctx.option.rowStyle = function (row, index) {
|
|
if (row["node_type"] === "C") {
|
|
return {classes : `group-level-${row["node_level"]}`};
|
|
}
|
|
return {}; // Default style for other rows
|
|
}
|
|
|
|
}
|
|
]]>
|
|
</INITIALIZE>
|
|
</SCRIPT>
|
|
</GRID-LIST>
|
|
|
|
<GRID-EDITOR DATAID="DS-PROJECT_BUDGET_ITEMS">
|
|
<FIELDS>
|
|
<FIELD NAME="PROJ_ID" INPUTTYPE="HIDDEN"/>
|
|
<FIELD NAME="PBDG_ID" INPUTTYPE="HIDDEN"/>
|
|
<FIELD NAME="ACM_CODE" INPUTTYPE="HIDDEN"/>
|
|
<FIELD NAME="BGM_NAME" INPUTTYPE="HIDDEN"/>
|
|
<FIELD NAME="ACM_NAME" INPUTTYPE="HIDDEN"/>
|
|
|
|
<FIELD NAME="VACM_CODE" CAPTION="pctbi.const_code" INPUTTYPE="TEXT" REQUIRE="Y" READONLY="Y"/>
|
|
<FIELD NAME="ACM_CODE" CAPTION="pctbi.const_name" INPUTTYPE="COMBOBOX" REQUIRE="Y" EDIT-READONLY="Y">
|
|
<AJAX-OPTION URL="/api-data.jbx" DATASET="DS-ACTIVITY-04" VALUE-FIELD="ACM_CODE" TEXT-FIELD="ACM_NAME" PARAMETERS="DV_YEAR=PROJ_YEAR,STM_CODE=STM_CODE">
|
|
<UPDATE-FIELDS>
|
|
<FIELD SRC="ACM_CODE" TARGET="VACM_CODE"/>
|
|
<FIELD SRC="ACM_CODE" TARGET="PBDG_ID"/>
|
|
<FIELD SRC="ACM_NAME" TARGET="ACM_NAME"/>
|
|
<FIELD SRC="ACM_UNIT" TARGET="PBGI_UNIT"/>
|
|
</UPDATE-FIELDS>
|
|
</AJAX-OPTION>
|
|
<LIST-OPTION>
|
|
<FORMATTER><![CDATA[
|
|
(data) => {
|
|
console.log("call formater with ", data);
|
|
return $(`<div class="d-flex flex-row gap-1"><label class="flex-column text-nowrap col-3">[${data.acm_code}]</label> : <label>${data.acm_name}</label></div>`);
|
|
}
|
|
]]></FORMATTER>
|
|
|
|
</LIST-OPTION>
|
|
</FIELD>
|
|
|
|
<FIELD NAME="VBGM_CODE" CAPTION="pwbg.bgm_code" INPUTTYPE="TEXT" READONLY="Y"/>
|
|
<FIELD NAME="BGM_CODE" CAPTION="pwbg.bgm_name" INPUTTYPE="COMBOBOX" REQUIRE="Y" EDIT-READONLY="Y">
|
|
<AJAX-OPTION URL="/api-data.jbx" DATASET="DS-BUDGET-04" VALUE-FIELD="BGM_CODE" TEXT-FIELD="BGM_NAME">
|
|
<UPDATE-FIELDS>
|
|
<FIELD SRC="BGM_CODE" TARGET="VBGM_CODE"></FIELD>
|
|
<FIELD SRC="BGM_NAME" TARGET="BGM_NAME"></FIELD>
|
|
</UPDATE-FIELDS>
|
|
</AJAX-OPTION>
|
|
<LIST-OPTION>
|
|
<FORMATTER><![CDATA[
|
|
(data) => {
|
|
console.log("call formater with ", data);
|
|
return $(`<div class="d-flex flex-row gap-1"><label class="flex-column text-nowrap col-3">[${data.bgm_code}]</label> : <label>${data.bgm_name}</label></div>`);
|
|
}
|
|
]]></FORMATTER>
|
|
</LIST-OPTION>
|
|
</FIELD>
|
|
|
|
<FIELD NAME="PBGI_UNIT" CAPTION="pctbi.unit" INPUTTYPE="TEXT" DATATYPE="TEXT" READONLY="Y"/>
|
|
<FIELD NAME="PBGI_QTY" CAPTION="pctbi.qty" INPUTTYPE="TEXT" DATATYPE="NUMBER" DECIMAL="0" CLASS-NAME="budget-data"/>
|
|
<FIELD NAME="PBGI_COST" CAPTION="pctbi.cost" INPUTTYPE="TEXT" DATATYPE="NUMBER" DECIMAL="0" CLASS-NAME="budget-data"/>
|
|
<FIELD NAME="PBGI_TOTAL" CAPTION="pctbi.total" INPUTTYPE="TEXT" DATATYPE="NUMBER" DECIMAL="0" READONLY="Y"/>
|
|
</FIELDS>
|
|
<LAYOUT CLASS="block-layout-form">
|
|
<ROW>
|
|
<FIELD NAME="VACM_CODE" LAYOUT_WIDTH="6"/>
|
|
<FIELD NAME="ACM_CODE" LAYOUT_WIDTH="16"/>
|
|
</ROW>
|
|
<ROW ID="ROW-BUDGET-DATA">
|
|
<FIELD NAME="PBGI_QTY" LAYOUT_WIDTH="3" OFFSET="6"/>
|
|
<FIELD NAME="PBGI_UNIT" LAYOUT_WIDTH="3"/>
|
|
<FIELD NAME="PBGI_COST" LAYOUT_WIDTH="3"/>
|
|
<FIELD NAME="PBGI_TOTAL" LAYOUT_WIDTH="3"/>
|
|
</ROW>
|
|
<ROW>
|
|
<FIELD NAME="VBGM_CODE" LAYOUT_WIDTH="4" OFFSET="6"/>
|
|
<FIELD NAME="BGM_CODE" LAYOUT_WIDTH="12"/>
|
|
</ROW>
|
|
<ROW TYPE="CONTENT">
|
|
<![CDATA[<div style="height: 350px"></div>]]>
|
|
</ROW>
|
|
</LAYOUT>
|
|
<SCRIPT>
|
|
<INITIALIZE><![CDATA[
|
|
console.log("sum budget value");
|
|
let budgetField = $$("input.budget-data");
|
|
const sumBudget = ()=>{
|
|
$$("PBGI_TOTAL").setValue(budgetField.multiply());
|
|
}
|
|
budgetField.on("change",sumBudget);
|
|
]]></INITIALIZE>
|
|
</SCRIPT>
|
|
</GRID-EDITOR>
|
|
</DATA-GRID>
|
|
</DATA-GRIDS>
|
|
</FORMS>
|