add project template into estimate creation to help project generation flow
This commit is contained in:
parent
8f90ef09fb
commit
54ae6d14f8
@ -429,6 +429,7 @@ def upsert_estimate(data):
|
||||
"customer_address": data.get("address_name"),
|
||||
"contact_person": data.get("contact_name"),
|
||||
"letter_head": data.get("company"),
|
||||
"custom_project_template": data.get("project_template", None)
|
||||
})
|
||||
for item in data.get("items", []):
|
||||
item = json.loads(item) if isinstance(item, str) else item
|
||||
|
||||
@ -5,6 +5,17 @@ from custom_ui.db_utils import process_query_conditions, build_datatable_dict, g
|
||||
# JOB MANAGEMENT API METHODS
|
||||
# ===============================================================================
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_job_templates(company=None):
|
||||
"""Get list of job (project) templates."""
|
||||
filters = {}
|
||||
if company:
|
||||
filters["company"] = company
|
||||
try:
|
||||
templates = frappe.get_all("Project Template", fields=["*"], filters=filters)
|
||||
return build_success_response(templates)
|
||||
except Exception as e:
|
||||
return build_error_response(str(e), 500)
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_job_from_sales_order(sales_order_name):
|
||||
|
||||
@ -1,59 +1 @@
|
||||
[
|
||||
{
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"collapsible_depends_on": null,
|
||||
"columns": 0,
|
||||
"default": null,
|
||||
"depends_on": null,
|
||||
"description": null,
|
||||
"docstatus": 0,
|
||||
"doctype": "Custom Field",
|
||||
"dt": "Lead",
|
||||
"fetch_from": null,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "custom_customer_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"hide_border": 0,
|
||||
"hide_days": 0,
|
||||
"hide_seconds": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"insert_after": "last_name",
|
||||
"is_system_generated": 0,
|
||||
"is_virtual": 0,
|
||||
"label": "Customer Name",
|
||||
"length": 0,
|
||||
"link_filters": null,
|
||||
"mandatory_depends_on": null,
|
||||
"modified": "2026-01-07 04:41:50.654606",
|
||||
"module": null,
|
||||
"name": "Lead-custom_customer_name",
|
||||
"no_copy": 0,
|
||||
"non_negative": 0,
|
||||
"options": null,
|
||||
"permlevel": 0,
|
||||
"placeholder": null,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": null,
|
||||
"read_only": 0,
|
||||
"read_only_depends_on": null,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"show_dashboard": 0,
|
||||
"sort_options": 0,
|
||||
"translatable": 1,
|
||||
"unique": 0,
|
||||
"width": null
|
||||
}
|
||||
]
|
||||
[]
|
||||
@ -498,8 +498,8 @@
|
||||
"make_attachments_public": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": null,
|
||||
"migration_hash": null,
|
||||
"modified": "2026-01-02 11:26:31.164108",
|
||||
"migration_hash": "2521636464aadbebbd70dfbf13252950",
|
||||
"modified": "2026-01-07 11:10:03.317996",
|
||||
"module": "Selling",
|
||||
"name": "Quotation Template",
|
||||
"naming_rule": "",
|
||||
@ -996,8 +996,8 @@
|
||||
"make_attachments_public": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": null,
|
||||
"migration_hash": null,
|
||||
"modified": "2025-12-23 02:00:30.908719",
|
||||
"migration_hash": "2521636464aadbebbd70dfbf13252950",
|
||||
"modified": "2026-01-07 11:10:03.406758",
|
||||
"module": "Selling",
|
||||
"name": "Quotation Template Item",
|
||||
"naming_rule": "",
|
||||
|
||||
@ -191,7 +191,17 @@ fixtures = [
|
||||
"dt": "Custom Field",
|
||||
"filters": [
|
||||
["dt", "=", "Quotation"],
|
||||
["fieldname", "=", "custom_quotation_template"]
|
||||
["fieldname", "in", [
|
||||
"custom_quotation_template",
|
||||
"custom_project_template"
|
||||
]]
|
||||
]
|
||||
},
|
||||
{
|
||||
"dt": "Custom Field",
|
||||
"filters": [
|
||||
["dt", "=", "Sales Order"],
|
||||
["fieldname", "=", "custom_project_template"]
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -200,6 +210,13 @@ fixtures = [
|
||||
["dt", "=", "Lead"],
|
||||
["fieldname", "=", "custom_customer_name"]
|
||||
]
|
||||
},
|
||||
{
|
||||
"dt": "Custom Field",
|
||||
"filters": [
|
||||
["dt", "=", "Project Template"],
|
||||
["fieldname", "=", "company"]
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@ -193,6 +193,24 @@ def add_custom_fields():
|
||||
fieldtype="Check",
|
||||
default=0,
|
||||
insert_after="custom_installation_address"
|
||||
),
|
||||
dict(
|
||||
fieldname="custom_project_template",
|
||||
label="Project Template",
|
||||
fieldtype="Link",
|
||||
options="Project Template",
|
||||
insert_after="custom_quotation_template",
|
||||
module="custom_ui",
|
||||
description="The project template to use when creating a project from this quotation."
|
||||
),
|
||||
dict(
|
||||
fieldname="custom_quotation_template",
|
||||
label="Quotation Template",
|
||||
fieldtype="Link",
|
||||
options="Quotation Template",
|
||||
insert_after="terms_and_conditions",
|
||||
module="custom_ui",
|
||||
description="The template used for generating this quotation."
|
||||
)
|
||||
],
|
||||
"Sales Order": [
|
||||
@ -202,6 +220,25 @@ def add_custom_fields():
|
||||
fieldtype="Check",
|
||||
default=0,
|
||||
insert_after="custom_installation_address"
|
||||
),
|
||||
dict(
|
||||
fieldname="custom_project_template",
|
||||
label="Project Template",
|
||||
fieldtype="Link",
|
||||
options="Project Template",
|
||||
module="custom_ui",
|
||||
description="The project template to use when creating a project from this sales order."
|
||||
)
|
||||
],
|
||||
"Project Template": [
|
||||
dict(
|
||||
fieldname="company",
|
||||
label="Company",
|
||||
fieldtype="Link",
|
||||
options="Company",
|
||||
insert_after="project_type",
|
||||
module="custom_ui",
|
||||
description="The company associated with this project template."
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ const FRAPPE_GET_JOBS_METHOD = "custom_ui.api.db.jobs.get_jobs_table_data";
|
||||
const FRAPPE_UPSERT_JOB_METHOD = "custom_ui.api.db.jobs.upsert_job";
|
||||
const FRAPPE_GET_JOB_TASK_LIST_METHOD = "custom_ui.api.db.jobs.get_job_task_table_data";
|
||||
const FRAPPE_GET_INSTALL_PROJECTS_METHOD = "custom_ui.api.db.jobs.get_install_projects";
|
||||
const FRAPPE_GET_JOB_TEMPLATES_METHOD = "custom_ui.api.db.jobs.get_job_templates";
|
||||
// Invoice methods
|
||||
const FRAPPE_GET_INVOICES_METHOD = "custom_ui.api.db.invoices.get_invoice_table_data";
|
||||
const FRAPPE_UPSERT_INVOICE_METHOD = "custom_ui.api.db.invoices.upsert_invoice";
|
||||
@ -236,6 +237,10 @@ class Api {
|
||||
// JOB / PROJECT METHODS
|
||||
// ============================================================================
|
||||
|
||||
static async getJobTemplates(company) {
|
||||
return await this.request(FRAPPE_GET_JOB_TEMPLATES_METHOD, { company });
|
||||
}
|
||||
|
||||
static async getJobDetails() {
|
||||
const projects = await this.getDocsList("Project");
|
||||
const data = [];
|
||||
|
||||
@ -59,6 +59,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Project Template Section -->
|
||||
<div class="project-template-section">
|
||||
<label for="projectTemplate" class="field-label">
|
||||
Project Template
|
||||
<span class="required">*</span>
|
||||
</label>
|
||||
<Select
|
||||
v-model="formData.projectTemplate"
|
||||
:options="projectTemplates"
|
||||
optionLabel="name"
|
||||
optionValue="name"
|
||||
placeholder="Select a project template"
|
||||
:disabled="!isEditable"
|
||||
fluid
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Template Section -->
|
||||
<div class="template-section">
|
||||
<div v-if="isNew">
|
||||
@ -356,6 +373,7 @@ const formData = reactive({
|
||||
contact: "",
|
||||
estimateName: null,
|
||||
requiresHalfPayment: false,
|
||||
projectTemplate: null,
|
||||
});
|
||||
|
||||
const selectedAddress = ref(null);
|
||||
@ -369,6 +387,7 @@ const quotationItems = ref([]);
|
||||
const selectedItems = ref([]);
|
||||
const responses = ref(["Accepted", "Rejected"]);
|
||||
const templates = ref([]);
|
||||
const projectTemplates = ref([]);
|
||||
const selectedTemplate = ref(null);
|
||||
|
||||
const showAddressModal = ref(false);
|
||||
@ -396,6 +415,16 @@ const itemColumns = [
|
||||
];
|
||||
|
||||
// Methods
|
||||
const fetchProjectTemplates = async () => {
|
||||
try {
|
||||
const result = await Api.getJobTemplates(company.currentCompany);
|
||||
projectTemplates.value = [...result, { name: "Other" }];
|
||||
} catch (error) {
|
||||
console.error("Error fetching project templates:", error);
|
||||
notificationStore.addNotification("Failed to fetch project templates", "error");
|
||||
}
|
||||
};
|
||||
|
||||
const fetchTemplates = async () => {
|
||||
if (!isNew.value) return;
|
||||
try {
|
||||
@ -556,6 +585,10 @@ const onQtyChange = (item) => {
|
||||
};
|
||||
|
||||
const saveDraft = async () => {
|
||||
if (!formData.projectTemplate) {
|
||||
notificationStore.addNotification("Project Template is required.", "error");
|
||||
return;
|
||||
}
|
||||
isSubmitting.value = true;
|
||||
try {
|
||||
const data = {
|
||||
@ -570,6 +603,7 @@ const saveDraft = async () => {
|
||||
})),
|
||||
estimateName: formData.estimateName,
|
||||
requiresHalfPayment: formData.requiresHalfPayment,
|
||||
projectTemplate: formData.projectTemplate,
|
||||
company: company.currentCompany
|
||||
};
|
||||
estimate.value = await Api.createEstimate(data);
|
||||
@ -673,7 +707,10 @@ watch(
|
||||
);
|
||||
|
||||
watch(() => company.currentCompany, () => {
|
||||
fetchTemplates();
|
||||
if (isNew.value) {
|
||||
fetchTemplates();
|
||||
fetchProjectTemplates();
|
||||
}
|
||||
});
|
||||
|
||||
// Watch for query param changes to refresh page behavior
|
||||
@ -773,7 +810,8 @@ onMounted(async () => {
|
||||
} catch (error) {
|
||||
console.error("Error loading quotation items:", error);
|
||||
}
|
||||
|
||||
fetchProjectTemplates();
|
||||
|
||||
if (isNew.value) {
|
||||
fetchTemplates();
|
||||
}
|
||||
@ -854,6 +892,7 @@ onMounted(async () => {
|
||||
|
||||
.address-section,
|
||||
.contact-section,
|
||||
.project-template-section,
|
||||
.template-section {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user