From 74ff4d6aef9196a294264d16238da2af12236318 Mon Sep 17 00:00:00 2001 From: Adam Pitel Date: Mon, 23 Mar 2026 16:24:41 -0500 Subject: [PATCH] Filter to active items only, remove Status column, document widget binding rules - Add WHERE item_active filter to show only active unused items - Remove Status column from query and table widget - Add widget binding guidance to AGENTS.md (NUMBER inputs cannot use NULLIF pattern due to prepared statement typing) Co-Authored-By: Claude Opus 4.6 (1M context) --- AGENTS.md | 16 +++++++- .../queries/unused_items/metadata.json | 2 +- .../queries/unused_items/unused_items.txt | 4 +- .../widgets/Table1.json | 41 ++----------------- 4 files changed, 22 insertions(+), 41 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c1af0a0..cbf2909 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -101,10 +101,24 @@ Every `widgetId` across all pages must be globally unique. Use short descriptive - SQL source of truth: `.txt` (readable, real newlines). - `metadata.json` `body` field: same SQL but JSON-escaped (`\n`, `\"`). - Table binding: `{{query_name.data}}`. -- Optional date filter pattern: `NULLIF('{{Widget.selectedDate}}','') IS NULL OR ... BETWEEN ...::date AND ...::date`. - All production queries use `xTuple_GoLive` datasource. - **Database credentials are managed by Appsmith server-side** — they are not stored in this repository. The datasource JSON only contains the name and plugin reference. When developing/testing SQL queries locally, use separate read-only credentials provided by the user — never embed them in committed files. +### Widget Bindings in SQL (IMPORTANT) + +Appsmith uses prepared statements (`pluginSpecifiedTemplates[0].value = true`). When a query body contains `{{Widget.property}}`, Appsmith replaces each binding with a typed prepared statement parameter (`$1`, `$2`, etc.). + +**For DATE_PICKER_WIDGET2** (text/date type parameters): +- The `NULLIF` pattern works: `NULLIF('{{DateWidget.selectedDate}}','')::date` +- This is safe because the parameter is always typed as `text`. + +**For INPUT_WIDGET_V2 with `inputType: "NUMBER"`** (integer type parameters): +- Do **NOT** use `NULLIF('{{Widget.text}}','')::int` — this fails because Appsmith may type the parameter as `integer`, and `NULLIF(integer, '')` causes a type mismatch error (`invalid input syntax for integer: ""`). +- Instead, use a direct cast: `'{{Widget.text}}'::int` +- The widget's `defaultText` property ensures there is always a value on page load, so NULL/empty handling is unnecessary. + +**General rule**: Match the SQL cast to the widget's output type. When in doubt, test with a hardcoded value first to confirm the base query works, then add the binding. + ## Common Tasks ### Add a new page diff --git a/pages/Operations - Unused Items/queries/unused_items/metadata.json b/pages/Operations - Unused Items/queries/unused_items/metadata.json index c554fdd..c1d4ae1 100644 --- a/pages/Operations - Unused Items/queries/unused_items/metadata.json +++ b/pages/Operations - Unused Items/queries/unused_items/metadata.json @@ -5,7 +5,7 @@ "pluginType": "DB", "unpublishedAction": { "actionConfiguration": { - "body": "SELECT\n i.item_number AS \"Item Number\",\n i.item_descrip1 AS \"Description\",\n CASE i.item_type\n WHEN 'P' THEN 'Purchased'\n WHEN 'M' THEN 'Manufactured'\n WHEN 'R' THEN 'Reference'\n WHEN 'T' THEN 'Tooling'\n WHEN 'O' THEN 'Outside Process'\n WHEN 'B' THEN 'Breeder'\n WHEN 'C' THEN 'Co-Product'\n WHEN 'F' THEN 'Phantom'\n ELSE i.item_type\n END AS \"Item Type\",\n CASE WHEN i.item_active THEN 'Active' ELSE 'Inactive' END AS \"Status\",\n cc.comment_user AS \"Created By\",\n COALESCE(i.item_created, cc.comment_date)::date AS \"Date Created\",\n last_txn.last_transaction_date::date AS \"Last Transaction\",\n (CURRENT_DATE - COALESCE(last_txn.last_transaction_date, i.item_created, cc.comment_date)::date) AS \"Days Unused\"\nFROM item i\nLEFT JOIN comment cc\n ON cc.comment_source_id = i.item_id\n AND cc.comment_source = 'I'\n AND cc.comment_text = 'Created'\nLEFT JOIN (\n SELECT\n isite.itemsite_item_id,\n MAX(ih.invhist_transdate) AS last_transaction_date\n FROM itemsite isite\n JOIN invhist ih ON ih.invhist_itemsite_id = isite.itemsite_id\n GROUP BY isite.itemsite_item_id\n) last_txn ON last_txn.itemsite_item_id = i.item_id\nWHERE (CURRENT_DATE - COALESCE(last_txn.last_transaction_date, i.item_created, cc.comment_date)::date)\n >= '{{UnusedDays.text}}'::int\nORDER BY \"Days Unused\" DESC", + "body": "SELECT\n i.item_number AS \"Item Number\",\n i.item_descrip1 AS \"Description\",\n CASE i.item_type\n WHEN 'P' THEN 'Purchased'\n WHEN 'M' THEN 'Manufactured'\n WHEN 'R' THEN 'Reference'\n WHEN 'T' THEN 'Tooling'\n WHEN 'O' THEN 'Outside Process'\n WHEN 'B' THEN 'Breeder'\n WHEN 'C' THEN 'Co-Product'\n WHEN 'F' THEN 'Phantom'\n ELSE i.item_type\n END AS \"Item Type\",\n cc.comment_user AS \"Created By\",\n COALESCE(i.item_created, cc.comment_date)::date AS \"Date Created\",\n last_txn.last_transaction_date::date AS \"Last Transaction\",\n (CURRENT_DATE - COALESCE(last_txn.last_transaction_date, i.item_created, cc.comment_date)::date) AS \"Days Unused\"\nFROM item i\nLEFT JOIN comment cc\n ON cc.comment_source_id = i.item_id\n AND cc.comment_source = 'I'\n AND cc.comment_text = 'Created'\nLEFT JOIN (\n SELECT\n isite.itemsite_item_id,\n MAX(ih.invhist_transdate) AS last_transaction_date\n FROM itemsite isite\n JOIN invhist ih ON ih.invhist_itemsite_id = isite.itemsite_id\n GROUP BY isite.itemsite_item_id\n) last_txn ON last_txn.itemsite_item_id = i.item_id\nWHERE i.item_active\n AND (CURRENT_DATE - COALESCE(last_txn.last_transaction_date, i.item_created, cc.comment_date)::date)\n >= '{{UnusedDays.text}}'::int\nORDER BY \"Days Unused\" DESC", "encodeParamsToggle": true, "paginationType": "NONE", "pluginSpecifiedTemplates": [ diff --git a/pages/Operations - Unused Items/queries/unused_items/unused_items.txt b/pages/Operations - Unused Items/queries/unused_items/unused_items.txt index 3b66da7..4a04662 100644 --- a/pages/Operations - Unused Items/queries/unused_items/unused_items.txt +++ b/pages/Operations - Unused Items/queries/unused_items/unused_items.txt @@ -12,7 +12,6 @@ SELECT WHEN 'F' THEN 'Phantom' ELSE i.item_type END AS "Item Type", - CASE WHEN i.item_active THEN 'Active' ELSE 'Inactive' END AS "Status", cc.comment_user AS "Created By", COALESCE(i.item_created, cc.comment_date)::date AS "Date Created", last_txn.last_transaction_date::date AS "Last Transaction", @@ -30,6 +29,7 @@ LEFT JOIN ( JOIN invhist ih ON ih.invhist_itemsite_id = isite.itemsite_id GROUP BY isite.itemsite_item_id ) last_txn ON last_txn.itemsite_item_id = i.item_id -WHERE (CURRENT_DATE - COALESCE(last_txn.last_transaction_date, i.item_created, cc.comment_date)::date) +WHERE i.item_active + AND (CURRENT_DATE - COALESCE(last_txn.last_transaction_date, i.item_created, cc.comment_date)::date) >= '{{UnusedDays.text}}'::int ORDER BY "Days Unused" DESC diff --git a/pages/Operations - Unused Items/widgets/Table1.json b/pages/Operations - Unused Items/widgets/Table1.json index b74845b..17c6e6f 100644 --- a/pages/Operations - Unused Items/widgets/Table1.json +++ b/pages/Operations - Unused Items/widgets/Table1.json @@ -35,7 +35,6 @@ "Item Number", "Description", "Item Type", - "Status", "Created By", "Date Created", "Last Transaction", @@ -57,7 +56,6 @@ {"key": "primaryColumns.Item Number.computedValue"}, {"key": "primaryColumns.Description.computedValue"}, {"key": "primaryColumns.Item Type.computedValue"}, - {"key": "primaryColumns.Status.computedValue"}, {"key": "primaryColumns.Created By.computedValue"}, {"key": "primaryColumns.Date Created.computedValue"}, {"key": "primaryColumns.Last Transaction.computedValue"}, @@ -187,37 +185,6 @@ "verticalAlignment": "CENTER", "width": 150 }, - "Status": { - "alias": "Status", - "allowCellWrapping": false, - "allowSameOptionsInNewRow": true, - "columnType": "text", - "computedValue": "{{(() => { const tableData = Table1.processedTableData || []; return tableData.length > 0 ? tableData.map((currentRow, currentIndex) => (currentRow[\"Status\"])) : [] })()}}", - "currencyCode": "USD", - "decimals": 0, - "enableFilter": true, - "enableSort": true, - "horizontalAlignment": "LEFT", - "id": "Status", - "index": 3, - "isCellEditable": false, - "isCellVisible": true, - "isDerived": false, - "isDisabled": false, - "isDiscardVisible": true, - "isEditable": false, - "isSaveVisible": true, - "isVisible": true, - "label": "Status", - "notation": "standard", - "originalId": "Status", - "sticky": "", - "textSize": "0.775rem", - "thousandSeparator": true, - "validation": {}, - "verticalAlignment": "CENTER", - "width": 150 - }, "Created By": { "alias": "Created By", "allowCellWrapping": false, @@ -230,7 +197,7 @@ "enableSort": true, "horizontalAlignment": "LEFT", "id": "Created By", - "index": 4, + "index": 3, "isCellEditable": false, "isCellVisible": true, "isDerived": false, @@ -261,7 +228,7 @@ "enableSort": true, "horizontalAlignment": "LEFT", "id": "Date Created", - "index": 5, + "index": 4, "isCellEditable": false, "isCellVisible": true, "isDerived": false, @@ -292,7 +259,7 @@ "enableSort": true, "horizontalAlignment": "LEFT", "id": "Last Transaction", - "index": 6, + "index": 5, "isCellEditable": false, "isCellVisible": true, "isDerived": false, @@ -323,7 +290,7 @@ "enableSort": true, "horizontalAlignment": "LEFT", "id": "Days Unused", - "index": 7, + "index": 6, "isCellEditable": false, "isCellVisible": true, "isDerived": false,