chore: replace old DocTypes with single Service Factory Settings

This commit is contained in:
root 2026-07-16 14:22:34 +00:00
parent aa2725ae1f
commit 53ffa92fb4
29 changed files with 270 additions and 326 deletions

View file

@ -1,6 +1,6 @@
# Service Factory — ERPNext Custom App Project
> **Current Version: V0.1.0014** — Each completed prompt/step increments the patch number by 1 (V0.1.0000 → V0.1.0001 → V0.1.0002…). The agent automatically commits and pushes after every step.
> **Current Version: V0.1.0015** — Each completed prompt/step increments the patch number by 1 (V0.1.0000 → V0.1.0001 → V0.1.0002…). The agent automatically commits and pushes after every step.
## Project Overview

View file

@ -1,22 +0,0 @@
[
{
"name": "SE",
"card_type_name": "SE",
"doctype": "Engagement Card Type"
},
{
"name": "PS",
"card_type_name": "PS",
"doctype": "Engagement Card Type"
},
{
"name": "MS",
"card_type_name": "MS",
"doctype": "Engagement Card Type"
},
{
"name": "Support",
"card_type_name": "Support",
"doctype": "Engagement Card Type"
}
]

View file

@ -1,27 +0,0 @@
[
{
"name": "Demo",
"task_name": "Demo",
"doctype": "Engagement Task Type"
},
{
"name": "POC",
"task_name": "POC",
"doctype": "Engagement Task Type"
},
{
"name": "Deployment",
"task_name": "Deployment",
"doctype": "Engagement Task Type"
},
{
"name": "RFP / RFI",
"task_name": "RFP / RFI",
"doctype": "Engagement Task Type"
},
{
"name": "Other",
"task_name": "Other",
"doctype": "Engagement Task Type"
}
]

View file

@ -172,8 +172,7 @@ doctype_js = {"Engagement Card" : "doctype/engagement_card/engagement_card.js"}
# across all server instances.
# -----------------------------------------
fixtures = [
{"dt": "Engagement Task Type"},
{"dt": "Engagement Card Type"},
{"dt": "Service Factory Settings"},
]
# Recreate Service Factory Desktop Icon after each migrate

View file

@ -3,7 +3,7 @@
frappe.ui.form.on("Engagement Card", {
refresh: function (frm) {
// Quick status actions in toolbar
// Quick status actions
if (frm.doc.status === "Discovery") {
frm.add_custom_button(__("Move to Delivery"), function () {
frm.set_value("status", "Delivery");
@ -61,22 +61,11 @@ frappe.ui.form.on("Engagement Card", {
d.show();
});
},
// Filter opportunities by selected customer
customer: function (frm) {
frm.set_query("opportunity", function () {
let filters = {};
if (frm.doc.customer) {
filters.customer = frm.doc.customer;
}
return { filters: filters };
});
},
});
// Time log hours rollup
// Time log rollup
frappe.ui.form.on("Engagement Card Time Log", {
hours: function (frm, cdt, cdn) {
hours: function (frm) {
frm.trigger("update_total_hours");
},
time_log_remove: function (frm) {

View file

@ -58,20 +58,20 @@
},
{
"fieldname": "card_type",
"fieldtype": "Link",
"fieldtype": "Select",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Card Type",
"options": "Engagement Card Type",
"options": "service_factory.service_factory.doctype.service_factory_settings.service_factory_settings.get_card_type_options",
"reqd": 1
},
{
"fieldname": "task",
"fieldtype": "Link",
"fieldtype": "Select",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Task",
"options": "Engagement Task Type",
"options": "service_factory.service_factory.doctype.service_factory_settings.service_factory_settings.get_task_type_options",
"reqd": 1
},
{
@ -120,9 +120,9 @@
},
{
"fieldname": "team",
"fieldtype": "Link",
"fieldtype": "Select",
"label": "Team",
"options": "Team Template"
"options": "service_factory.service_factory.doctype.service_factory_settings.service_factory_settings.get_team_options"
},
{
"fieldname": "section_break_desc",

View file

@ -22,8 +22,7 @@ class EngagementCard(Document):
opp_name = frappe.db.get_value("Opportunity", self.opportunity, "opportunity_name")
parts.append(opp_name or self.opportunity)
if self.task:
task_name = frappe.db.get_value("Engagement Task Type", self.task, "task_name")
parts.append(task_name or self.task)
parts.append(self.task)
if parts:
self.title = " | ".join(parts)
@ -31,17 +30,25 @@ class EngagementCard(Document):
self.title = f"EC-{self.name or 'new'}"
def sync_team_members(self):
"""Populate assignments from selected Team Template."""
"""Populate assignments from selected Team in Settings."""
if not self.team:
return
# Only sync if team changed (not on every save)
if not self.has_value_changed("team"):
return
template = frappe.get_doc("Team Template", self.team)
settings = frappe.get_single("Service Factory Settings")
team_row = None
for t in settings.teams:
if t.team_name == self.team:
team_row = t
break
if not team_row:
return
self.set("assignments", [])
for member in template.members:
for member in team_row.members:
row = self.append("assignments", {})
row.employee = member.employee
row.role = member.role
@ -76,15 +83,6 @@ class EngagementCard(Document):
assignment.logged_hours = employee_hours[emp]
@frappe.whitelist()
def get_teams_by_department(department=None):
"""Return team templates optionally filtered by department."""
filters = {}
if department:
filters["department"] = department
return frappe.get_all("Team Template", filters=filters, fields=["name", "team_name", "department"])
@frappe.whitelist()
def get_kpi_data():
"""Return dashboard KPI data for Engagement Cards."""

View file

@ -1,61 +0,0 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:card_type_name",
"creation": "2026-07-16 12:00:00.000000",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
"card_type_name"
],
"fields": [
{
"fieldname": "card_type_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Card Type Name",
"reqd": 1,
"unique": 1
}
],
"links": [],
"modified": "2026-07-16 12:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Engagement Card Type",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"select": 1,
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"select": 1,
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View file

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

View file

@ -1,61 +0,0 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:task_name",
"creation": "2026-07-16 10:00:00.000000",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
"task_name"
],
"fields": [
{
"fieldname": "task_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Task Name",
"reqd": 1,
"unique": 1
}
],
"links": [],
"modified": "2026-07-16 10:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Engagement Task Type",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"select": 1,
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"select": 1,
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View file

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

View file

@ -0,0 +1,92 @@
{
"actions": [],
"allow_rename": 0,
"autoname": "service_factory_settings",
"creation": "2026-07-16 12:00:00.000000",
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"section_card_types",
"card_types",
"section_task_types",
"task_types",
"section_teams",
"teams"
],
"fields": [
{
"fieldname": "section_card_types",
"fieldtype": "Section Break",
"label": "Engagement Card Types"
},
{
"fieldname": "card_types",
"fieldtype": "Table",
"label": "Card Types",
"options": "SF Settings Card Type",
"reqd": 0
},
{
"fieldname": "section_task_types",
"fieldtype": "Section Break",
"label": "Engagement Task Types"
},
{
"fieldname": "task_types",
"fieldtype": "Table",
"label": "Task Types",
"options": "SF Settings Task Type",
"reqd": 0
},
{
"fieldname": "section_teams",
"fieldtype": "Section Break",
"label": "Teams"
},
{
"fieldname": "teams",
"fieldtype": "Table",
"label": "Teams",
"options": "SF Settings Team",
"reqd": 0
}
],
"issingle": 1,
"links": [],
"modified": "2026-07-16 12:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Service Factory Settings",
"naming_rule": "Set by user",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 0
}

View file

@ -0,0 +1,24 @@
import frappe
from frappe.model.document import Document
class ServiceFactorySettings(Document):
pass
def get_card_type_options(doc=None, fieldname=None):
"""Return card type names from Settings for dynamic Select field."""
settings = frappe.get_single("Service Factory Settings")
return [row.card_type_name for row in settings.card_types]
def get_task_type_options(doc=None, fieldname=None):
"""Return task type names from Settings for dynamic Select field."""
settings = frappe.get_single("Service Factory Settings")
return [row.task_type_name for row in settings.task_types]
def get_team_options(doc=None, fieldname=None):
"""Return team names from Settings for dynamic Select field."""
settings = frappe.get_single("Service Factory Settings")
return [row.team_name for row in settings.teams]

View file

@ -0,0 +1,32 @@
{
"actions": [],
"allow_rename": 0,
"autoname": "hash",
"creation": "2026-07-16 12:00:00.000000",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": ["card_type_name"],
"fields": [
{
"fieldname": "card_type_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Card Type",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2026-07-16 12:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "SF Settings Card Type",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 0
}

View file

@ -0,0 +1,4 @@
from frappe.model.document import Document
class SFSettingsCardType(Document):
pass

View file

@ -0,0 +1,32 @@
{
"actions": [],
"allow_rename": 0,
"autoname": "hash",
"creation": "2026-07-16 12:00:00.000000",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": ["task_type_name"],
"fields": [
{
"fieldname": "task_type_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Task Type",
"reqd": 1
}
],
"istable": 1,
"links": [],
"modified": "2026-07-16 12:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "SF Settings Task Type",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 0
}

View file

@ -0,0 +1,4 @@
from frappe.model.document import Document
class SFSettingsTaskType(Document):
pass

View file

@ -0,0 +1,46 @@
{
"actions": [],
"allow_rename": 0,
"autoname": "hash",
"creation": "2026-07-16 12:00:00.000000",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": ["team_name", "department", "members"],
"fields": [
{
"fieldname": "team_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Team Name",
"reqd": 1
},
{
"fieldname": "department",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Department",
"options": "Department"
},
{
"fieldname": "members",
"fieldtype": "Table",
"label": "Team Members",
"options": "SF Settings Team Member",
"reqd": 0
}
],
"istable": 1,
"links": [],
"modified": "2026-07-16 12:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "SF Settings Team",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 0
}

View file

@ -0,0 +1,4 @@
from frappe.model.document import Document
class SFSettingsTeam(Document):
pass

View file

@ -2,15 +2,11 @@
"actions": [],
"allow_rename": 0,
"autoname": "hash",
"creation": "2026-07-16 10:00:00.000000",
"creation": "2026-07-16 12:00:00.000000",
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"employee",
"role"
],
"field_order": ["employee", "role"],
"fields": [
{
"fieldname": "employee",
@ -29,10 +25,10 @@
],
"istable": 1,
"links": [],
"modified": "2026-07-16 10:00:00.000000",
"modified": "2026-07-16 12:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Team Template Member",
"name": "SF Settings Team Member",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [],

View file

@ -0,0 +1,4 @@
from frappe.model.document import Document
class SFSettingsTeamMember(Document):
pass

View file

@ -1,71 +0,0 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:team_name",
"creation": "2026-07-16 10:00:00.000000",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
"team_name",
"department",
"section_break_members",
"members"
],
"fields": [
{
"fieldname": "team_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Team Name",
"reqd": 1,
"unique": 1
},
{
"fieldname": "department",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Department",
"options": "Department"
},
{
"fieldname": "section_break_members",
"fieldtype": "Section Break",
"label": "Team Members"
},
{
"fieldname": "members",
"fieldtype": "Table",
"label": "Members",
"options": "Team Template Member",
"reqd": 0
}
],
"links": [],
"modified": "2026-07-16 10:00:00.000000",
"modified_by": "Administrator",
"module": "Service Factory",
"name": "Team Template",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"select": 1,
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View file

@ -1,14 +0,0 @@
# Copyright (c) 2026, cclohmar and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
class TeamTemplate(Document):
def on_update(self):
"""Clear team cache when template changes."""
frappe.cache.delete_key("team_templates")
def after_insert(self):
self.on_update()

View file

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