Files
Makap/makap_backend/makap_backend/dashboard1_setup.py
T
2026-10-05 16:04:29 +03:00

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