243 lines
7.8 KiB
Python
243 lines
7.8 KiB
Python
"""
|
|
Creates a standalone "Dashboard1" Workspace for MAKAP Backend:
|
|
- 4 Number Cards (stat row) backed by real doctype counts
|
|
- 24 doctypes as shortcuts, grouped into 8 sections, matching the
|
|
approved dashboard mockup
|
|
|
|
Idempotent: safe to re-run. Re-running clears and rebuilds this
|
|
workspace's own content/shortcuts/number_cards — it does not touch
|
|
the existing "MAKAP Backend" workspace or its workspace_setup.py.
|
|
|
|
Run with:
|
|
bench --site makap.local execute makap_backend.makap_backend.dashboard1_setup.create_dashboard1
|
|
|
|
(Adjust the dotted path if your app's inner module isn't doubled
|
|
"makap_backend.makap_backend" — check with:
|
|
bench --site makap.local console
|
|
>>> import makap_backend.makap_backend.dashboard1_setup
|
|
)
|
|
"""
|
|
|
|
import json
|
|
import frappe
|
|
|
|
|
|
WORKSPACE_NAME = "Dashboard1"
|
|
|
|
# ---------------------------------------------------------------------
|
|
# 1. Number Cards (top stat row)
|
|
# ---------------------------------------------------------------------
|
|
NUMBER_CARDS = [
|
|
{
|
|
"label": "Onboarding Requests",
|
|
"document_type": "Customer Onboarding Request",
|
|
"filters_json": "[]",
|
|
"color": "#2E6BE6",
|
|
},
|
|
{
|
|
"label": "Packaging Specifications",
|
|
"document_type": "Packaging Specification",
|
|
"filters_json": "[]",
|
|
"color": "#1F9D6B",
|
|
},
|
|
{
|
|
"label": "Approved for Manufacture",
|
|
"document_type": "Packaging Specification",
|
|
"filters_json": json.dumps(
|
|
[["Packaging Specification", "specification_status", "=", "Approved for Manufacture"]]
|
|
),
|
|
"color": "#C8791C",
|
|
},
|
|
{
|
|
# Swapped in for the child-table "Critical Requirements Tracked" card —
|
|
# see module docstring for why.
|
|
"label": "Artwork Pending",
|
|
"document_type": "Packaging Specification",
|
|
"filters_json": json.dumps(
|
|
[["Packaging Specification", "artwork_status", "=", "Pending"]]
|
|
),
|
|
"color": "#D94B6A",
|
|
},
|
|
]
|
|
|
|
# ---------------------------------------------------------------------
|
|
# 2. Sidebar groups -> (label, doctype, is_custom)
|
|
# is_custom just drives shortcut color (Blue vs Grey) so custom
|
|
# MAKAP doctypes visually stand out from stock ERPNext/Frappe ones.
|
|
# ---------------------------------------------------------------------
|
|
SHORTCUT_GROUPS = [
|
|
("Customer & Commercial — M1-2", [
|
|
("Onboarding Requests", "Customer Onboarding Request", True),
|
|
("Customer", "Customer", False),
|
|
("Contact", "Contact", False),
|
|
("Opportunity", "Opportunity", False),
|
|
("Quotation", "Quotation", False),
|
|
("Sales Order", "Sales Order", False),
|
|
]),
|
|
("Specification & Prepress — M3", [
|
|
("Packaging Specifications", "Packaging Specification", True),
|
|
("Material Structure", "Material Structure", True),
|
|
("Critical Acceptance Requirement", "Critical Acceptance Requirement", True),
|
|
("Item", "Item", False),
|
|
]),
|
|
("Production & Planning — M4", [
|
|
("Operation", "Operation", False),
|
|
("Work Order", "Work Order", False),
|
|
("Production Plan", "Production Plan", False),
|
|
]),
|
|
("Materials & Inventory — M5", [
|
|
("Material Request", "Material Request", False),
|
|
("Purchase Order", "Purchase Order", False),
|
|
("Purchase Receipt", "Purchase Receipt", False),
|
|
]),
|
|
("Quality — M6", [
|
|
("Quality Inspection", "Quality Inspection", False),
|
|
]),
|
|
("Fulfilment & Billing — M8", [
|
|
("Delivery Note", "Delivery Note", False),
|
|
("Sales Invoice", "Sales Invoice", False),
|
|
("Payment Entry", "Payment Entry", False),
|
|
("Purchase Invoice", "Purchase Invoice", False),
|
|
]),
|
|
("Operating Control", [
|
|
("Order Closure & Margin", "Order Closure And Margin View", True),
|
|
]),
|
|
("Reference & Lookups", [
|
|
("User", "User", False),
|
|
("UOM", "UOM", False),
|
|
]),
|
|
]
|
|
|
|
|
|
def _ensure_number_card(spec):
|
|
"""Get-or-create a public Number Card. Returns the card name (== label)."""
|
|
if not frappe.db.exists("DocType", spec["document_type"]):
|
|
frappe.logger().warning(
|
|
f"[dashboard1_setup] Skipping Number Card '{spec['label']}': "
|
|
f"DocType '{spec['document_type']}' not found."
|
|
)
|
|
return None
|
|
|
|
if frappe.db.exists("Number Card", spec["label"]):
|
|
card = frappe.get_doc("Number Card", spec["label"])
|
|
else:
|
|
card = frappe.new_doc("Number Card")
|
|
card.label = spec["label"]
|
|
|
|
card.document_type = spec["document_type"]
|
|
card.type = "Document Type"
|
|
card.function = "Count"
|
|
card.filters_json = spec["filters_json"]
|
|
card.is_public = 1
|
|
card.show_percentage_stats = 0
|
|
if hasattr(card, "color"):
|
|
card.color = spec["color"]
|
|
|
|
card.save(ignore_permissions=True)
|
|
return card.name
|
|
|
|
|
|
def _content_block(block_type, data, block_id):
|
|
return {"id": block_id, "type": block_type, "data": data}
|
|
|
|
|
|
def create_dashboard1():
|
|
frappe.set_user("Administrator")
|
|
|
|
# -- 1. Number Cards --------------------------------------------------
|
|
card_names = []
|
|
for spec in NUMBER_CARDS:
|
|
name = _ensure_number_card(spec)
|
|
if name:
|
|
card_names.append(name)
|
|
|
|
# -- 2. Workspace shell -------------------------------------------------
|
|
if frappe.db.exists("Workspace", WORKSPACE_NAME):
|
|
ws = frappe.get_doc("Workspace", WORKSPACE_NAME)
|
|
else:
|
|
ws = frappe.new_doc("Workspace")
|
|
ws.name = WORKSPACE_NAME
|
|
|
|
ws.label = WORKSPACE_NAME
|
|
ws.title = WORKSPACE_NAME
|
|
ws.module = "Makap Backend"
|
|
ws.public = 1
|
|
ws.is_hidden = 0
|
|
ws.icon = "dashboard"
|
|
ws.indicator_color = "blue"
|
|
|
|
# Reset child tables before rebuilding
|
|
ws.set("shortcuts", [])
|
|
ws.set("links", [])
|
|
if ws.meta.has_field("number_cards"):
|
|
ws.set("number_cards", [])
|
|
|
|
content_blocks = []
|
|
block_seq = 0
|
|
|
|
def next_id(prefix):
|
|
nonlocal block_seq
|
|
block_seq += 1
|
|
return f"{prefix}_{block_seq}"
|
|
|
|
# Header
|
|
content_blocks.append(_content_block(
|
|
"header",
|
|
{"text": "<span class=\"h4\">MAKAP Backend — Dashboard1</span>", "col": 12},
|
|
next_id("header"),
|
|
))
|
|
|
|
# Number card row
|
|
for name in card_names:
|
|
content_blocks.append(_content_block(
|
|
"number_card",
|
|
{"number_card_name": name, "col": 3},
|
|
next_id("card"),
|
|
))
|
|
if ws.meta.has_field("number_cards"):
|
|
ws.append("number_cards", {"number_card_name": name})
|
|
|
|
# Group headers + shortcuts
|
|
skipped = []
|
|
total_shortcuts = 0
|
|
for group_label, items in SHORTCUT_GROUPS:
|
|
content_blocks.append(_content_block(
|
|
"header",
|
|
{"text": f"<span class=\"h5\">{group_label}</span>", "col": 12},
|
|
next_id("group"),
|
|
))
|
|
|
|
for shortcut_label, doctype, is_custom in items:
|
|
if not frappe.db.exists("DocType", doctype):
|
|
skipped.append(doctype)
|
|
continue
|
|
|
|
ws.append("shortcuts", {
|
|
"label": shortcut_label,
|
|
"type": "DocType",
|
|
"link_to": doctype,
|
|
"doc_view": "List",
|
|
"icon": "list",
|
|
"color": "Blue" if is_custom else "Grey",
|
|
"stats_filter": "{}",
|
|
})
|
|
content_blocks.append(_content_block(
|
|
"shortcut",
|
|
{"shortcut_name": shortcut_label, "col": 3},
|
|
next_id("shortcut"),
|
|
))
|
|
total_shortcuts += 1
|
|
|
|
ws.content = json.dumps(content_blocks)
|
|
ws.save(ignore_permissions=True)
|
|
frappe.db.commit()
|
|
|
|
result = {
|
|
"workspace": ws.name,
|
|
"number_cards_created": card_names,
|
|
"shortcuts_created": total_shortcuts,
|
|
"doctypes_skipped_not_found": skipped,
|
|
}
|
|
print(json.dumps(result, indent=2))
|
|
return result
|