diff --git a/AGENTS.md b/AGENTS.md index e36f2ca..0762c8f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Service Factory — ERPNext Custom App Project -> **Current Version: V0.1.0019** — 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.0020** — 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 diff --git a/service_factory/service_factory/doctype/engagement_card/engagement_card.js b/service_factory/service_factory/doctype/engagement_card/engagement_card.js new file mode 100644 index 0000000..74f5364 --- /dev/null +++ b/service_factory/service_factory/doctype/engagement_card/engagement_card.js @@ -0,0 +1,143 @@ +// Copyright (c) 2026, cclohmar and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Engagement Card", { + onload: function (frm) { + load_select_options(frm); + toggle_creation_fields(frm); + + // Filter opportunities by customer + frm.set_query("opportunity", function () { + let filters = {}; + if (frm.doc.customer) { + filters.customer = frm.doc.customer; + } + return { filters: filters }; + }); + }, + refresh: function (frm) { + load_select_options(frm); + toggle_creation_fields(frm); + + // Quick status actions + if (frm.doc.status === "Discovery" && !frm.is_new()) { + frm.add_custom_button(__("Move to Delivery"), function () { + frm.set_value("status", "Delivery"); + frm.save(); + }); + } else if (frm.doc.status === "Delivery") { + frm.add_custom_button(__("Mark Complete"), function () { + frm.set_value("status", "Complete"); + frm.save(); + }); + } else if (frm.doc.status === "Complete") { + frm.add_custom_button(__("Archive"), function () { + frm.set_value("status", "Archived"); + frm.set_value("actual_end_date", frappe.datetime.get_today()); + frm.save(); + }); + } + + // Log Activity quick action (only for saved cards) + if (!frm.is_new()) { + frm.add_custom_button(__("Log Activity"), function () { + let d = new frappe.ui.Dialog({ + title: __("Log Activity"), + fields: [ + { + label: "Activity Type", + fieldname: "activity_type", + fieldtype: "Select", + options: ["Comment", "Milestone", "Note"], + reqd: 1, + }, + { + label: "Content", + fieldname: "content", + fieldtype: "Text Editor", + reqd: 1, + }, + { + label: "Mark as Milestone", + fieldname: "is_milestone", + fieldtype: "Check", + }, + ], + primary_action: function () { + let data = d.get_values(); + let child = frm.add_child("activity_log"); + child.posted_by = frappe.session.user; + child.timestamp = frappe.datetime.now_datetime(); + child.activity_type = data.activity_type; + child.content = data.content; + child.is_milestone = data.is_milestone; + frm.refresh_field("activity_log"); + d.hide(); + }, + }); + d.show(); + }); + } + }, + // Re-filter opportunities when customer changes + customer: function (frm) { + frm.set_query("opportunity", function () { + let filters = {}; + if (frm.doc.customer) { + filters.customer = frm.doc.customer; + } + return { filters: filters }; + }); + }, +}); + +function load_select_options(frm) { + frappe.call({ + method: "frappe.client.get", + args: { + doctype: "Service Factory Settings", + name: "Service Factory Settings", + }, + callback: function (r) { + if (!r.message) return; + let doc = r.message; + + if (doc.task_types && doc.task_types.length) { + let opts = "\n" + doc.task_types.map((t) => t.task_type_name).join("\n"); + frm.set_df_property("task", "options", opts); + frm.refresh_field("task"); + } + + if (doc.teams && doc.teams.length) { + let opts = "\n" + doc.teams.map((t) => t.team_name).join("\n"); + frm.set_df_property("team", "options", opts); + frm.refresh_field("team"); + } + }, + }); +} + +function toggle_creation_fields(frm) { + let hide = frm.is_new(); + let sections = [ + "section_break_desc", + "section_break_budget", + "section_break_assignments", + "section_break_activity", + "section_break_time", + "section_break_subtasks", + ]; + sections.forEach(function (section) { + frm.toggle_display(section, !hide); + }); +} + +// Time log rollup +frappe.ui.form.on("Engagement Card Time Log", { + hours: function (frm) { + frm.trigger("update_total_hours"); + }, + time_log_remove: function (frm) { + frm.trigger("update_total_hours"); + }, +}); diff --git a/service_factory/service_factory/hooks.py b/service_factory/service_factory/hooks.py index c03103d..1434031 100644 --- a/service_factory/service_factory/hooks.py +++ b/service_factory/service_factory/hooks.py @@ -43,7 +43,7 @@ add_to_apps_screen = [ # page_js = {"page" : "public/js/file.js"} # include js in doctype views -doctype_js = {"Engagement Card" : "doctype/engagement_card/engagement_card.js"} +doctype_js = {"Engagement Card" : "service_factory/doctype/engagement_card/engagement_card.js"} # doctype_list_js = {"doctype" : "public/js/doctype_list.js"} # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} # doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} diff --git a/service_factory/service_factory/service_factory/doctype/engagement_card/engagement_card.js b/service_factory/service_factory/service_factory/doctype/engagement_card/engagement_card.js index 74f5364..3d766d1 100644 --- a/service_factory/service_factory/service_factory/doctype/engagement_card/engagement_card.js +++ b/service_factory/service_factory/service_factory/doctype/engagement_card/engagement_card.js @@ -5,15 +5,6 @@ frappe.ui.form.on("Engagement Card", { onload: function (frm) { load_select_options(frm); toggle_creation_fields(frm); - - // Filter opportunities by customer - frm.set_query("opportunity", function () { - let filters = {}; - if (frm.doc.customer) { - filters.customer = frm.doc.customer; - } - return { filters: filters }; - }); }, refresh: function (frm) { load_select_options(frm); @@ -38,30 +29,15 @@ frappe.ui.form.on("Engagement Card", { }); } - // Log Activity quick action (only for saved cards) + // Log Activity quick action if (!frm.is_new()) { frm.add_custom_button(__("Log Activity"), function () { let d = new frappe.ui.Dialog({ title: __("Log Activity"), fields: [ - { - label: "Activity Type", - fieldname: "activity_type", - fieldtype: "Select", - options: ["Comment", "Milestone", "Note"], - reqd: 1, - }, - { - label: "Content", - fieldname: "content", - fieldtype: "Text Editor", - reqd: 1, - }, - { - label: "Mark as Milestone", - fieldname: "is_milestone", - fieldtype: "Check", - }, + { label: "Activity Type", fieldname: "activity_type", fieldtype: "Select", options: ["Comment", "Milestone", "Note"], reqd: 1 }, + { label: "Content", fieldname: "content", fieldtype: "Text Editor", reqd: 1 }, + { label: "Mark as Milestone", fieldname: "is_milestone", fieldtype: "Check" }, ], primary_action: function () { let data = d.get_values(); @@ -79,35 +55,20 @@ frappe.ui.form.on("Engagement Card", { }); } }, - // Re-filter opportunities when customer changes - customer: function (frm) { - frm.set_query("opportunity", function () { - let filters = {}; - if (frm.doc.customer) { - filters.customer = frm.doc.customer; - } - return { filters: filters }; - }); - }, }); function load_select_options(frm) { frappe.call({ method: "frappe.client.get", - args: { - doctype: "Service Factory Settings", - name: "Service Factory Settings", - }, + args: { doctype: "Service Factory Settings", name: "Service Factory Settings" }, callback: function (r) { if (!r.message) return; let doc = r.message; - if (doc.task_types && doc.task_types.length) { let opts = "\n" + doc.task_types.map((t) => t.task_type_name).join("\n"); frm.set_df_property("task", "options", opts); frm.refresh_field("task"); } - if (doc.teams && doc.teams.length) { let opts = "\n" + doc.teams.map((t) => t.team_name).join("\n"); frm.set_df_property("team", "options", opts); @@ -120,7 +81,6 @@ function load_select_options(frm) { function toggle_creation_fields(frm) { let hide = frm.is_new(); let sections = [ - "section_break_desc", "section_break_budget", "section_break_assignments", "section_break_activity", @@ -134,10 +94,6 @@ function toggle_creation_fields(frm) { // Time log rollup frappe.ui.form.on("Engagement Card Time Log", { - hours: function (frm) { - frm.trigger("update_total_hours"); - }, - time_log_remove: function (frm) { - frm.trigger("update_total_hours"); - }, + hours: function (frm) { frm.trigger("update_total_hours"); }, + time_log_remove: function (frm) { frm.trigger("update_total_hours"); }, });