diff --git a/uberleben_custom/api.py b/uberleben_custom/api.py index 089f0d9..0056590 100644 --- a/uberleben_custom/api.py +++ b/uberleben_custom/api.py @@ -3,21 +3,24 @@ import erpnext @frappe.whitelist() -def get_item_per_wh(): - items = frappe.db.get_list("Item", pluck="name") +def get_item_per_wh(filters={}): + items = frappe.db.get_list("Item", fields=["name", "item_group"], filters=filters) warehouses = frappe.db.get_list("Warehouse", pluck="name") data = dict() for item in items: - data[item] = dict() + item_name = item['name'] + item_group = item['item_group'] + data[item_name] = dict(item_group=item_group, warehouses=dict()) for item in items: + item_name = item["name"] for warehouse in warehouses: - quantity = erpnext.stock.utils.get_latest_stock_qty(item, warehouse) + quantity = erpnext.stock.utils.get_latest_stock_qty(item_name, warehouse) if quantity is None: quantity = 0 - data[item][warehouse] = quantity # erpnext.stock.utils.get_latest_stock_qty(item, warehouse) + data[item_name]['warehouses'][warehouse] = quantity return data @@ -33,3 +36,8 @@ def get_warehouse_data(): data[warehouse]['disabled'] = doc.disabled data[warehouse]['is_group_warehouse'] = doc.is_group return data + + +@frappe.whitelist() +def get_item_groups(): + return frappe.db.get_list("Item Group", pluck="name") diff --git a/uberleben_custom/fixtures/custom_html_block.json b/uberleben_custom/fixtures/custom_html_block.json index 7b5d97f..0971491 100644 --- a/uberleben_custom/fixtures/custom_html_block.json +++ b/uberleben_custom/fixtures/custom_html_block.json @@ -3,11 +3,11 @@ "docstatus": 0, "doctype": "Custom HTML Block", "html": "
\n
\n

Filters

\n \n
\n
\n

Items

\n
\n
\n
", - "modified": "2025-10-30 13:42:13.557964", + "modified": "2025-10-30 16:19:26.461685", "name": "Inventory Status Workspace", "private": 0, "roles": [], - "script": "var warehouse_data;\n\nfrappe.call({\n method: \"uberleben_custom.api.get_warehouse_data\",\n callback: function(r) {\n warehouse_data = r.message;\n }\n});\nfrappe.call({\n method: \"uberleben_custom.api.get_item_per_wh\",\n callback: function(r){\n if (r.message) {\n populateItemTable(r.message);\n }\n }\n});\n\nfunction toggleDetails($rowId) {\n console.log($rowId.attr(\"detail\"));\n if ($rowId.attr(\"detail\")) {\n $rowId.removeAttr(\"detail\");\n }\n else {\n $rowId.attr(\"detail\", true);\n }\n}\n\nfunction toggleInnerTable($tableId) {\n if ($tableId.attr(\"hidden\") != null) {\n $tableId.removeAttr(\"hidden\");\n } else {\n $tableId.attr(\"hidden\", \"\");\n }\n}\n\nfunction generateInnerTableRow(warehouse, number) {\n if (number == 0)\n return \"\"\n return \"
\"+warehouse+\"
\"+number+\"
\";\n}\n\nfunction populateItemTable(items) {\n console.log(\"Prior to building table.\")\n console.log(warehouse_data);\n let $itemTable = $(\"#item_table\", root_element);\n let itemNum = 0;\n let rowNum = 0;\n //let tableHTML = \"\";\n //tableHTML += \"\";\n //tableHTML += \"\";\n let tableHTML = \"
\"\n\n for (const[item, stock_data] of Object.entries(items)) {\n let rowId = rowNum + \"-row\";\n let rowButtonId = rowId + \"-button\";\n tableHTML += `
`;\n tableHTML += ''+item+'';\n tableHTML += \"
\";\n for (const[warehouse, number] of Object.entries(stock_data)) {\n let wh = warehouse_data[warehouse]\n if (wh.disabled == 1 || number == 0)\n continue;\n rowId = ++rowNum +\"-row\";\n rowButtonId = rowId + \"-button\";\n if (wh.is_substatus_warehouse == 0) {\n tableHTML += `
`;\n tableHTML += `
${warehouse}
`;\n tableHTML += `
Sellable Units: ${number}
`;\n if (wh.is_group_warehouse)\n tableHTML += `
`;\n tableHTML += '
';\n }\n }\n // Generate Inner Nested Table\n let innerTableId = rowNum + \"-table\";\n let innerHTML = `
\"\n //innerHTML += `
StatusNumber
\";\n innerHTML += \"\";\n tableHTML += innerHTML;\n console.log(innerHTML);\n itemNum++;\n rowNum++;\n }\n\n //tableHTML += \"\";\n\n $itemTable.html(tableHTML);\n \n for (let i=0;i {toggleInnerTable($innerTable)});\n }\n }\n }", - "style": ".info-button {\n border: none;\n color: white;\n background-color: var(--btn-primary);\n box-shadow: none;\n font-size: var(--text-base);\n font-weight: var(--weight-regular);\n letter-spacing: 0.02em;\n height: var(--btn-height);\n margin-left: var(--margin-sm, 8px);\n border-radius: var(--border-radius);\n line-height: 1;\n padding: 2px 8px;\n}\n.table-container {\n}\n\n.table {\n display: flex;\n flex-direction: column;\n margin: 10px;\n border: 1px solid;\n border-radius: var(--border-radius);\n}\n\n.table-cell {\n flex: 1;\n padding: 5px;\n border-right: 1px solid;\n}\n\n.table-cell .button-cell {\n align-self: flex-end;\n}\n\n.table-cell:last-child {\n border-right: none;\n}\n\n.inner-table {\n display: flex;\n flex-direction: column;\n margin: 10px;\n border: 1px solid;\n border-radius: var(--border-radius);\n}\n\n.item-table-row {\n display: flex;\n margin: 5px;\n border-bottom: 1px solid;\n}\n.item-table-row:hover {\n background-color: var(--highlight-color);\n}\n.outer-table-row {\n display: flex;\n margin: 5px;\n}\n.outer-table-row:hover {\n background-color: var(--highlight-color);\n border-radius: var(--border-radius);\n}\n.inner-table-row {\n display: flex;\n margin: 10px;\n}\n.inner-table-row:hover {\n background-color: var(--highlight-color);\n border-radius: var(--border-radius);\n}" + "script": "var warehouse_data;\n\nfrappe.call({\n method: \"uberleben_custom.api.get_item_groups\",\n callback: function(r) {setItemGroupSelect(r.message);}\n});\n\nfrappe.call({\n method: \"uberleben_custom.api.get_warehouse_data\",\n callback: function(r) {\n warehouse_data = r.message;\n }\n});\nfrappe.call({\n method: \"uberleben_custom.api.get_item_per_wh\",\n args: {\n filters: {}\n },\n callback: function(r){\n if (r.message) {\n populateItemTable(r.message);\n }\n }\n});\n\nfunction setItemGroupSelect(groupNames) {\n let $group_select = $(`#group-select`, root_element);\n for (const groupName of groupNames) {\n let option = ``;\n $group_select.append(option);\n }\n}\n\nfunction toggleDetails($rowId) {\n console.log($rowId.attr(\"detail\"));\n if ($rowId.attr(\"detail\")) {\n $rowId.removeAttr(\"detail\");\n }\n else {\n $rowId.attr(\"detail\", true);\n }\n}\n\nfunction toggleInnerTable($tableId) {\n if ($tableId.attr(\"hidden\") != null) {\n $tableId.removeAttr(\"hidden\");\n } else {\n $tableId.attr(\"hidden\", \"\");\n }\n}\n\nfunction generateInnerTableRow(warehouse, number) {\n if (number == 0)\n return \"\"\n return \"
\"+warehouse+\"
\"+number+\"
\";\n}\n\nfunction generateItemTableRow(item, itemGroup, rowId) {\n let row = `
`;\n row += `
${item}
`;\n row += `
${itemGroup}
`;\n row += \"
\"\n return row\n}\n\nfunction populateItemTable(items) {\n console.log(\"Prior to building table.\")\n console.log(warehouse_data);\n let $itemTable = $(\"#item_table\", root_element);\n let itemNum = 0;\n let rowNum = 0;\n let tableHTML = \"
\";\n\n for (const[item, stock_data] of Object.entries(items)) {\n let itemGroup = stock_data['item_group']\n let rowId = rowNum + \"-row\";\n let rowButtonId = rowId + \"-button\";\n tableHTML += generateItemTableRow(item, itemGroup, rowId);\n for (const[warehouse, number] of Object.entries(stock_data[\"warehouses\"])) {\n let wh = warehouse_data[warehouse];\n if (wh.disabled == 1 || number == 0)\n continue;\n rowId = ++rowNum +\"-row\";\n rowButtonId = rowId + \"-button\";\n if (wh.is_substatus_warehouse == 0) {\n tableHTML += `
`;\n tableHTML += `
${warehouse}
`;\n tableHTML += `
Sellable Units: ${number}
`;\n if (wh.is_group_warehouse)\n tableHTML += `
`;\n tableHTML += '
';\n }\n }\n // Generate Inner Nested Table\n let innerTableId = rowNum + \"-table\";\n let innerHTML = `\";\n tableHTML += innerHTML;\n console.log(innerHTML);\n itemNum++;\n rowNum++;\n }\n\n $itemTable.html(tableHTML);\n \n for (let i=0;i {toggleInnerTable($innerTable)});\n }\n }\n }", + "style": ".info-button {\n border: none;\n color: white;\n background-color: var(--btn-primary);\n box-shadow: none;\n font-size: var(--text-base);\n font-weight: var(--weight-regular);\n letter-spacing: 0.02em;\n height: var(--btn-height);\n margin-left: var(--margin-sm, 8px);\n border-radius: var(--border-radius);\n line-height: 1;\n padding: 2px 8px;\n}\n.table-container {\n}\n\n.table {\n display: flex;\n flex-direction: column;\n margin: 10px;\n border: 1px solid;\n border-radius: var(--border-radius);\n}\n\n.header-cell {\n font-weight: bold;\n flex: 1;\n}\n\n.table-cell {\n display: flex;\n flex: 1;\n padding: 5px;\n border-right: 1px solid;\n}\n\n.button-cell {\n display: flex;\n flex: 1;\n justify-content: center;\n}\n\n.table-cell:last-child {\n border-right: none;\n}\n\n.inner-table {\n display: flex;\n flex-direction: column;\n margin: 10px;\n border: 1px solid;\n border-radius: var(--border-radius);\n}\n\n.item-table-row {\n display: flex;\n justify-content: flex-start;\n margin: 5px;\n border-bottom: 1px solid;\n background-color: var(--highlight-color);\n}\n.item-table-row:hover {\n background-color: var(--highlight-color);\n}\n.outer-table-row {\n display: flex;\n margin: 5px;\n justify-content: flex-start;\n}\n.outer-table-row:hover {\n background-color: var(--highlight-color);\n border-radius: var(--border-radius);\n}\n.inner-table-row {\n display: flex;\n justify-content: flex-start;\n}\n.inner-table-row:hover {\n background-color: var(--highlight-color);\n border-radius: var(--border-radius);\n}" } ] \ No newline at end of file diff --git a/uberleben_custom/fixtures/warehouse.json b/uberleben_custom/fixtures/warehouse.json index 2e516da..0dc1d89 100644 --- a/uberleben_custom/fixtures/warehouse.json +++ b/uberleben_custom/fixtures/warehouse.json @@ -199,31 +199,6 @@ "warehouse_name": "QC Hold - U", "warehouse_type": null }, - { - "account": null, - "address_line_1": null, - "address_line_2": null, - "city": null, - "company": "Uberleben", - "custom_is_substatus_warehouse": 1, - "default_in_transit_warehouse": null, - "disabled": 0, - "docstatus": 0, - "doctype": "Warehouse", - "email_id": null, - "is_group": 0, - "is_rejected_warehouse": 0, - "mobile_no": null, - "modified": "2025-10-29 21:29:18.720725", - "name": "Staged - U", - "old_parent": "Uberleben HQ - U", - "parent_warehouse": "Uberleben HQ - U", - "phone_no": null, - "pin": null, - "state": null, - "warehouse_name": "Staged - U", - "warehouse_type": null - }, { "account": null, "address_line_1": null, @@ -264,14 +239,14 @@ "is_group": 0, "is_rejected_warehouse": 0, "mobile_no": null, - "modified": "2025-10-29 21:29:26.476951", - "name": "Inbound - U", + "modified": "2025-10-29 21:29:18.720725", + "name": "Staged - U", "old_parent": "Uberleben HQ - U", "parent_warehouse": "Uberleben HQ - U", "phone_no": null, "pin": null, "state": null, - "warehouse_name": "Inbound", + "warehouse_name": "Staged - U", "warehouse_type": null }, { @@ -298,5 +273,30 @@ "state": null, "warehouse_name": "All Warehouses", "warehouse_type": null + }, + { + "account": null, + "address_line_1": null, + "address_line_2": null, + "city": null, + "company": "Uberleben", + "custom_is_substatus_warehouse": 1, + "default_in_transit_warehouse": null, + "disabled": 0, + "docstatus": 0, + "doctype": "Warehouse", + "email_id": null, + "is_group": 0, + "is_rejected_warehouse": 0, + "mobile_no": null, + "modified": "2025-10-29 21:29:26.476951", + "name": "Inbound - U", + "old_parent": "Uberleben HQ - U", + "parent_warehouse": "Uberleben HQ - U", + "phone_no": null, + "pin": null, + "state": null, + "warehouse_name": "Inbound", + "warehouse_type": null } ] \ No newline at end of file