feat: Milestone 1 - All 5 DocTypes with controllers

- Resource Center + Resource Center Member child table
- Engagement Recipe + Engagement Recipe Step child table
- Engagement Order (submittable) + Engagement Order Run Step child table
- Execution Card (submittable) + Execution Card Log child table
- Server-side actions: start_work, pause_work, stop_work on Execution Card
- Cost rollup logic from Execution Card to Engagement Order
- Auto-generation of Execution Cards on Engagement Order submit
- Total duration calculation on Engagement Recipe
This commit is contained in:
Claus Lohmar 2026-07-15 19:54:41 +00:00
parent 7994bb2d17
commit 6b44131abe
24 changed files with 2500 additions and 0 deletions

View file

@ -0,0 +1,549 @@
{
"actions": [],
"allow_auto_repeat": 0,
"allow_events_in_timeline": 1,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "naming_series:",
"creation": "2026-07-15 19:00:00.000000",
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"naming_series",
"customer",
"sales_order",
"engagement_recipe",
"column_break_1",
"status",
"start_date",
"end_date",
"section_break_totals",
"total_allocated_hours",
"total_logged_hours",
"column_break_totals_2",
"actual_cost",
"projected_billing_value",
"section_break_steps",
"run_steps"
],
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Series",
"length": 0,
"no_copy": 0,
"options": "ENG-ORD-.YYYY.-",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "customer",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Customer",
"length": 0,
"no_copy": 0,
"options": "Customer",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "sales_order",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sales Order",
"length": 0,
"no_copy": 0,
"options": "Sales Order",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "engagement_recipe",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Engagement Recipe",
"length": 0,
"no_copy": 0,
"options": "Engagement Recipe",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Draft\nScheduled\nIn Progress\nOn Hold\nCompleted\nCancelled",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "start_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Start Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "end_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "End Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_totals",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Totals",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "total_allocated_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Allocated Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "1",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "total_logged_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Logged Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "1",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_totals_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "actual_cost",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Actual Cost",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "projected_billing_value",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Projected Billing Value",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_steps",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Engagement Steps",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "run_steps",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Run Steps",
"length": 0,
"no_copy": 0,
"options": "Engagement Order Run Step",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
}
],
"has_field_mappings": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_field": "",
"import_source": "",
"is_submittable": 1,
"is_table": 0,
"label": "Engagement Order",
"modified": "2026-07-15 19:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Engagement Order",
"name_case": "",
"nsm_parent_field": "",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"select": 1,
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1,
"track_seen": 1,
"track_views": 0
}

View file

@ -0,0 +1,74 @@
# Copyright (c) 2026, cclohmar and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
class EngagementOrder(Document):
def on_change_engagement_recipe(self):
"""Fetch recipe steps into order run steps when recipe is selected."""
if not self.engagement_recipe:
return
recipe = frappe.get_doc("Engagement Recipe", self.engagement_recipe)
self.set("run_steps", [])
for step in recipe.steps:
row = self.append("run_steps", {})
row.step_name = step.step_name
row.sequence_no = step.sequence_no
row.resource_center = step.resource_center
row.allocated_hours = step.estimated_hours
row.status = "Pending"
def before_save(self):
self.calculate_totals()
def calculate_totals(self):
total_allocated = 0.0
total_logged = 0.0
actual_cost = 0.0
projected_billing = 0.0
if self.get("run_steps"):
for step in self.run_steps:
allocated = step.get("allocated_hours", 0) or 0
logged = step.get("actual_hours", 0) or 0
total_allocated += allocated
total_logged += logged
if step.resource_center:
rc = frappe.get_cached_value(
"Resource Center",
step.resource_center,
["hourly_cost_rate", "hourly_billing_rate"],
as_dict=True,
)
if rc:
actual_cost += logged * (rc.get("hourly_cost_rate", 0) or 0)
projected_billing += logged * (rc.get("hourly_billing_rate", 0) or 0)
self.total_allocated_hours = total_allocated
self.total_logged_hours = total_logged
self.actual_cost = actual_cost
self.projected_billing_value = projected_billing
def on_submit(self):
self.create_execution_cards()
def create_execution_cards(self):
"""Auto-generate Execution Cards for each run step."""
for step in self.get("run_steps", []):
if not step.assigned_employee:
continue
card = frappe.get_doc({
"doctype": "Execution Card",
"engagement_order": self.name,
"step_name": step.step_name,
"assigned_employee": step.assigned_employee,
"allocated_hours": step.allocated_hours or 0,
"status": "Open",
})
card.insert()

View file

@ -0,0 +1,281 @@
{
"actions": [],
"allow_events_in_timeline": 0,
"autoname": "hash",
"creation": "2026-07-15 19:00:00.000000",
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"step_name",
"sequence_no",
"resource_center",
"assigned_employee",
"column_break_1",
"allocated_hours",
"actual_hours",
"status"
],
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "step_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Step Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "sequence_no",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Sequence No",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "resource_center",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Resource Center",
"length": 0,
"no_copy": 0,
"options": "Resource Center",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "assigned_employee",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Assigned Employee",
"length": 0,
"no_copy": 0,
"options": "Employee",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "allocated_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Allocated Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "1",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "actual_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Actual Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "1",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Pending\nActive\nCompleted",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
}
],
"has_field_mappings": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_field": "",
"import_source": "",
"is_submittable": 0,
"is_table": 1,
"label": "Engagement Order Run Step",
"modified": "2026-07-15 19:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Engagement Order Run Step",
"name_case": "",
"nsm_parent_field": "",
"owner": "Administrator",
"permissions": [],
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,8 @@
# Copyright (c) 2026, cclohmar and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class EngagementOrderRunStep(Document):
pass

View file

@ -0,0 +1,245 @@
{
"actions": [],
"allow_auto_repeat": 0,
"allow_events_in_timeline": 1,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "field:recipe_name",
"creation": "2026-07-15 19:00:00.000000",
"doctype": "DocType",
"document_type": "Master",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"recipe_name",
"service_offering",
"column_break_1",
"estimated_total_duration_hours",
"section_break_steps",
"steps"
],
"fields": [
{
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 2,
"fieldname": "recipe_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Recipe Name",
"length": 140,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"sort_options": 0,
"unique": 1
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "service_offering",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Service Offering",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "estimated_total_duration_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Estimated Total Duration Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "1",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_steps",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Engagement Steps",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "steps",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Steps",
"length": 0,
"no_copy": 0,
"options": "Engagement Recipe Step",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
}
],
"has_field_mappings": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_field": "",
"import_source": "",
"is_submittable": 0,
"is_table": 0,
"label": "Engagement Recipe",
"modified": "2026-07-15 19:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Engagement Recipe",
"name_case": "",
"nsm_parent_field": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"select": 1,
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,17 @@
# Copyright (c) 2026, cclohmar and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
class EngagementRecipe(Document):
def before_save(self):
self.calculate_total_duration()
def calculate_total_duration(self):
total = 0.0
if self.get("steps"):
for step in self.steps:
total += step.get("estimated_hours", 0) or 0
self.estimated_total_duration_hours = total

View file

@ -0,0 +1,219 @@
{
"actions": [],
"allow_events_in_timeline": 0,
"autoname": "hash",
"creation": "2026-07-15 19:00:00.000000",
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"step_name",
"sequence_no",
"resource_center",
"column_break_1",
"estimated_hours",
"dependency_step"
],
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "step_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Step Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "sequence_no",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Sequence No",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "resource_center",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Resource Center",
"length": 0,
"no_copy": 0,
"options": "Resource Center",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "estimated_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Estimated Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "1",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "dependency_step",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Dependency Step",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
}
],
"has_field_mappings": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_field": "",
"import_source": "",
"is_submittable": 0,
"is_table": 1,
"label": "Engagement Recipe Step",
"modified": "2026-07-15 19:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Engagement Recipe Step",
"name_case": "",
"nsm_parent_field": "",
"owner": "Administrator",
"permissions": [],
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,8 @@
# Copyright (c) 2026, cclohmar and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class EngagementRecipeStep(Document):
pass

View file

@ -0,0 +1,422 @@
{
"actions": [
{
"action_name": "Start Work",
"group": "Action",
"hidden": 0,
"action_type": "Server Action",
"action": "start_work"
},
{
"action_name": "Pause Work",
"group": "Action",
"hidden": 0,
"action_type": "Server Action",
"action": "pause_work"
},
{
"action_name": "Stop Work",
"group": "Action",
"hidden": 0,
"action_type": "Server Action",
"action": "stop_work"
}
],
"allow_auto_repeat": 0,
"allow_events_in_timeline": 1,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "naming_series:",
"creation": "2026-07-15 19:00:00.000000",
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"naming_series",
"engagement_order",
"step_name",
"column_break_1",
"assigned_employee",
"allocated_hours",
"actual_hours_logged",
"status",
"section_break_timer",
"timer_status",
"elapsed_time",
"section_break_logs",
"time_logs"
],
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Series",
"length": 0,
"no_copy": 0,
"options": "EXE-CRD-.YYYY.-",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "engagement_order",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Engagement Order",
"length": 0,
"no_copy": 0,
"options": "Engagement Order",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "step_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Step Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "assigned_employee",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Assigned Employee",
"length": 0,
"no_copy": 0,
"options": "Employee",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "allocated_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allocated Hours",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "1",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "actual_hours_logged",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Actual Hours Logged",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "2",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Open\nRunning\nPaused\nSubmitted",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_timer",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Time Tracking",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_logs",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Time Logs",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "time_logs",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Time Logs",
"length": 0,
"no_copy": 1,
"options": "Execution Card Log",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
}
],
"has_field_mappings": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_field": "",
"import_source": "",
"is_submittable": 1,
"is_table": 0,
"label": "Execution Card",
"modified": "2026-07-15 19:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Execution Card",
"name_case": "",
"nsm_parent_field": "",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"select": 1,
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1,
"track_seen": 1,
"track_views": 0
}

View file

@ -0,0 +1,90 @@
# Copyright (c) 2026, cclohmar and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
from frappe import _
class ExecutionCard(Document):
def before_save(self):
self.calculate_logged_hours()
def calculate_logged_hours(self):
total = 0.0
if self.get("time_logs"):
for log in self.time_logs:
total += log.get("duration_hours", 0) or 0
self.actual_hours_logged = total
def on_update(self):
self.update_engagement_order_totals()
def on_submit(self):
self.status = "Submitted"
self.update_engagement_order_totals()
def on_cancel(self):
self.update_engagement_order_totals()
def update_engagement_order_totals(self):
if not self.engagement_order:
return
order = frappe.get_doc("Engagement Order", self.engagement_order)
order.calculate_totals()
order.save()
@frappe.whitelist()
def start_work(self):
"""Start work on this execution card."""
if self.status not in ("Open", "Paused"):
frappe.throw(_("Card must be in 'Open' or 'Paused' status to start work."))
log = self.append("time_logs", {})
log.start_time = frappe.utils.now_datetime()
self.status = "Running"
self.save()
@frappe.whitelist()
def pause_work(self):
"""Pause work on this execution card."""
if self.status != "Running":
frappe.throw(_("Card must be in 'Running' status to pause work."))
if self.time_logs and not self.time_logs[-1].end_time:
log = self.time_logs[-1]
log.end_time = frappe.utils.now_datetime()
log.duration_hours = (log.end_time - log.start_time).total_seconds() / 3600
self.calculate_logged_hours()
self.status = "Paused"
self.save()
@frappe.whitelist()
def stop_work(self):
"""Stop work and mark the card as ready for submission."""
if self.status not in ("Running", "Paused"):
frappe.throw(_("Card must be 'Running' or 'Paused' to stop work."))
if self.status == "Running" and self.time_logs and not self.time_logs[-1].end_time:
log = self.time_logs[-1]
log.end_time = frappe.utils.now_datetime()
log.duration_hours = (log.end_time - log.start_time).total_seconds() / 3600
self.calculate_logged_hours()
self.status = "Submitted"
self.save()
@frappe.whitelist()
def get_active_time(self):
"""Return current elapsed time if card is running."""
if self.status != "Running" or not self.time_logs:
return {"elapsed_seconds": 0}
log = self.time_logs[-1]
if log.start_time and not log.end_time:
elapsed = (frappe.utils.now_datetime() - log.start_time).total_seconds()
return {"elapsed_seconds": elapsed}
return {"elapsed_seconds": 0}

View file

@ -0,0 +1,128 @@
{
"actions": [],
"allow_events_in_timeline": 0,
"autoname": "hash",
"creation": "2026-07-15 19:00:00.000000",
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"start_time",
"end_time",
"duration_hours"
],
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "start_time",
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Start Time",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "end_time",
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "End Time",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "duration_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Duration Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "2",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
}
],
"has_field_mappings": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_field": "",
"import_source": "",
"is_submittable": 0,
"is_table": 1,
"label": "Execution Card Log",
"modified": "2026-07-15 19:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Execution Card Log",
"name_case": "",
"nsm_parent_field": "",
"owner": "Administrator",
"permissions": [],
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,8 @@
# Copyright (c) 2026, cclohmar and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class ExecutionCardLog(Document):
pass

View file

@ -0,0 +1,335 @@
{
"actions": [],
"allow_auto_repeat": 0,
"allow_events_in_timeline": 1,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "field:center_name",
"creation": "2026-07-15 19:00:00.000000",
"doctype": "DocType",
"document_type": "Master",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"center_name",
"resource_type",
"column_break_1",
"hourly_cost_rate",
"hourly_billing_rate",
"section_break_capacity",
"daily_capacity_hours",
"section_break_members",
"members"
],
"fields": [
{
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 2,
"fieldname": "center_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Center Name",
"length": 140,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"sort_options": 0,
"unique": 1
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "resource_type",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Resource Type",
"length": 0,
"no_copy": 0,
"options": "Engineering\nSales Engineering\nSupport\nConsulting",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "hourly_cost_rate",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Hourly Cost Rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "hourly_billing_rate",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Hourly Billing Rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_capacity",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Capacity Planning",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "daily_capacity_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Daily Capacity Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "1",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_members",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Team Members",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "members",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Members",
"length": 0,
"no_copy": 0,
"options": "Resource Center Member",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
}
],
"has_field_mappings": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_field": "",
"import_source": "",
"is_submittable": 0,
"is_table": 0,
"label": "Resource Center",
"modified": "2026-07-15 19:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Resource Center",
"name_case": "",
"nsm_parent_field": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"select": 1,
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,9 @@
# Copyright (c) 2026, cclohmar and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
class ResourceCenter(Document):
pass

View file

@ -0,0 +1,99 @@
{
"actions": [],
"allow_events_in_timeline": 0,
"autoname": "field:employee",
"creation": "2026-07-15 19:00:00.000000",
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"employee",
"allocation_percentage"
],
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "employee",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Employee",
"length": 0,
"no_copy": 0,
"options": "Employee",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "allocation_percentage",
"fieldtype": "Percent",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Allocation Percentage",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"unique": 0
}
],
"has_field_mappings": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_field": "",
"import_source": "",
"is_submittable": 0,
"is_table": 1,
"label": "Resource Center Member",
"modified": "2026-07-15 19:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Resource Center Member",
"name_case": "",
"nsm_parent_field": "",
"owner": "Administrator",
"permissions": [],
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,8 @@
# Copyright (c) 2026, cclohmar and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class ResourceCenterMember(Document):
pass