Big update
This commit is contained in:
parent
124b8775fb
commit
b400be3f1a
29 changed files with 31703 additions and 2443 deletions
|
|
@ -11,6 +11,7 @@ def get_week_bid_meetings(week_start, week_end, company):
|
|||
"On-Site Meeting",
|
||||
fields=["*"],
|
||||
filters=[
|
||||
["status", "!=", "Cancelled"],
|
||||
["start_time", ">=", week_start],
|
||||
["start_time", "<=", week_end],
|
||||
["company", "=", company]
|
||||
|
|
@ -169,6 +170,9 @@ def get_bid_meeting(name):
|
|||
if meeting_dict.get("contact"):
|
||||
contact_doc = ContactService.get_or_throw(meeting_dict["contact"])
|
||||
meeting_dict["contact"] = contact_doc.as_dict()
|
||||
if meeting_dict.get("bid_notes"):
|
||||
bid_meeting_note_doc = frappe.get_doc("Bid Meeting Note", meeting_dict["bid_notes"])
|
||||
meeting_dict["bid_notes"] = bid_meeting_note_doc.as_dict()
|
||||
|
||||
return build_success_response(meeting_dict)
|
||||
except frappe.DoesNotExistError:
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ def get_doc_list(doctype, fields=["*"], filters={}, pluck=None):
|
|||
fields=fields,
|
||||
filters=filters,
|
||||
order_by="creation desc",
|
||||
pluck=pluck
|
||||
# pluck=pluck
|
||||
)
|
||||
print(f"DEBUG: Retrieved documents for {doctype} with filters {filters}: {docs}")
|
||||
return build_success_response(docs)
|
||||
|
|
|
|||
6
custom_ui/api/payments.py
Normal file
6
custom_ui/api/payments.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import frappe
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def start_payment(invoice_name: str):
|
||||
|
||||
pass
|
||||
|
|
@ -83,5 +83,5 @@ def on_update_after_submit(doc, method):
|
|||
print("DEBUG: Submitting Sales Order")
|
||||
# new_sales_order.customer_address = backup
|
||||
new_sales_order.submit()
|
||||
frappe.db.commit()
|
||||
# frappe.db.commit()
|
||||
print("DEBUG: Sales Order created successfully:", new_sales_order.name)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import frappe
|
||||
from custom_ui.services import AddressService, ClientService, ServiceAppointmentService, TaskService
|
||||
from datetime import timedelta
|
||||
import traceback
|
||||
|
||||
def after_insert(doc, method):
|
||||
print("DEBUG: After Insert Triggered for Project")
|
||||
|
|
@ -15,26 +16,41 @@ def after_insert(doc, method):
|
|||
doc.customer, "projects", {"project": doc.name, "project_template": doc.project_template}
|
||||
)
|
||||
if doc.project_template == "SNW Install":
|
||||
print("DEBUG: Project template is SNW Install, updating Address status to In Progress")
|
||||
AddressService.update_value(
|
||||
doc.job_address,
|
||||
"job_status",
|
||||
"In Progress"
|
||||
)
|
||||
service_apt = ServiceAppointmentService.create({
|
||||
"project": doc.name,
|
||||
"customer": doc.customer,
|
||||
"address": doc.job_address,
|
||||
"company": doc.company,
|
||||
"project_template": doc.project_template
|
||||
})
|
||||
frappe.db.set_value("Project", doc.name, "service_appointment", service_apt.name)
|
||||
print("DEBUG: Project template is SNW Install, creating Service Appointment")
|
||||
# AddressService.update_value(
|
||||
# doc.job_address,
|
||||
# "job_status",
|
||||
# "In Progress"
|
||||
# )
|
||||
try:
|
||||
service_apt = ServiceAppointmentService.create({
|
||||
"project": doc.name,
|
||||
"customer": doc.customer,
|
||||
"service_address": doc.job_address,
|
||||
"company": doc.company,
|
||||
"project_template": doc.project_template
|
||||
})
|
||||
doc.service_appointment = service_apt.name
|
||||
doc.save(ignore_permissions=True)
|
||||
print("DEBUG: Created Service Appointment:", service_apt.name)
|
||||
except Exception as e:
|
||||
print("ERROR: Failed to create Service Appointment for Project:", e)
|
||||
print(traceback.format_exc())
|
||||
raise e
|
||||
task_names = [task.name for task in TaskService.get_tasks_by_project(doc.name)]
|
||||
for task_name in task_names:
|
||||
doc.append("tasks", {
|
||||
"task": task_name
|
||||
})
|
||||
TaskService.calculate_and_set_due_dates(task_names, "Created")
|
||||
AddressService.append_link_v2(
|
||||
doc.job_address, "tasks", {"task": task_name}
|
||||
)
|
||||
ClientService.append_link_v2(
|
||||
doc.customer, "tasks", {"task": task_name}
|
||||
)
|
||||
if task_names:
|
||||
doc.save(ignore_permissions=True)
|
||||
TaskService.calculate_and_set_due_dates(task_names, "Created", "Project")
|
||||
|
||||
|
||||
|
||||
|
|
@ -60,7 +76,8 @@ def after_save(doc, method):
|
|||
if event:
|
||||
TaskService.calculate_and_set_due_dates(
|
||||
[task.task for task in doc.tasks],
|
||||
event
|
||||
event,
|
||||
"Project"
|
||||
)
|
||||
if doc.project_template == "SNW Install":
|
||||
print("DEBUG: Project template is SNW Install, updating Address Job Status based on Project status")
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ def before_insert(doc, method):
|
|||
# Address.onsite_meetings is a child table with two fields: onsite_meeting (Link) and project_template (Link). Iterate through to see if there is already an SNW Install meeting linked.
|
||||
for link in address_doc.onsite_meetings:
|
||||
if link.project_template == "SNW Install":
|
||||
raise frappe.ValidationError("An On-Site Meeting with project template 'SNW Install' is already linked to this address.")
|
||||
if frappe.db.get_value("On-Site Meeting", link.onsite_meeting, "status") != "Cancelled":
|
||||
raise frappe.ValidationError("An On-Site Meeting with project template 'SNW Install' is already linked to this address.")
|
||||
|
||||
def after_insert(doc, method):
|
||||
print("DEBUG: After Insert Triggered for On-Site Meeting")
|
||||
|
|
@ -22,17 +23,19 @@ def after_insert(doc, method):
|
|||
|
||||
|
||||
def before_save(doc, method):
|
||||
|
||||
print("DEBUG: Before Save Triggered for On-Site Meeting")
|
||||
if doc.status != "Scheduled" and doc.start_time and doc.end_time and doc.status != "Completed":
|
||||
if doc.status != "Scheduled" and doc.start_time and doc.end_time and doc.status != "Completed" and doc.status != "Cancelled":
|
||||
print("DEBUG: Meeting has start and end time, setting status to Scheduled")
|
||||
doc.status = "Scheduled"
|
||||
if doc.project_template == "SNW Install":
|
||||
print("DEBUG: Project template is SNW Install")
|
||||
if doc.status == "Completed":
|
||||
print("DEBUG: Meeting marked as Completed, updating Address status")
|
||||
current_status = AddressService.get_value(doc.address, "onsite_meeting_scheduled")
|
||||
if current_status != doc.status:
|
||||
AddressService.update_value(doc.address, "onsite_meeting_scheduled", "Completed")
|
||||
AddressService.update_value(doc.address, "onsite_meeting_scheduled", "Completed")
|
||||
if doc.status == "Cancelled":
|
||||
print("DEBUG: Meeting marked as Cancelled, updating Address status")
|
||||
AddressService.update_value(doc.address, "onsite_meeting_scheduled", "Not Started")
|
||||
|
||||
def validate_address_link(doc, method):
|
||||
print("DEBUG: Validating Address link for On-Site Meeting")
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ import frappe
|
|||
from custom_ui.services import DbService, AddressService, ClientService
|
||||
|
||||
def before_insert(doc, method):
|
||||
print("DEBUG: before_insert hook triggered for Sales Order:", doc.name)
|
||||
if doc.custom_project_template == "SNW Install":
|
||||
print("DEBUG: Sales Order uses SNW Install template, checking for duplicate linked sales orders.")
|
||||
address_doc = AddressService.get_or_throw(doc.custom_job_address)
|
||||
if "SNW Install" in [link.project_template for link in address_doc.sales_orders]:
|
||||
raise frappe.ValidationError("A Sales Order with project template 'SNW Install' is already linked to this address.")
|
||||
print("DEBUG: before_insert hook triggered for Sales Order")
|
||||
# if doc.custom_project_template == "SNW Install":
|
||||
# print("DEBUG: Sales Order uses SNW Install template, checking for duplicate linked sales orders.")
|
||||
# address_doc = AddressService.get_or_throw(doc.custom_job_address)
|
||||
# if "SNW Install" in [link.project_template for link in address_doc.sales_orders]:
|
||||
# raise frappe.ValidationError("A Sales Order with project template 'SNW Install' is already linked to this address.")
|
||||
|
||||
def on_submit(doc, method):
|
||||
print("DEBUG: Info from Sales Order")
|
||||
|
|
@ -33,7 +33,7 @@ def on_submit(doc, method):
|
|||
})
|
||||
# attatch the job to the sales_order links
|
||||
new_job.insert()
|
||||
frappe.db.commit()
|
||||
# frappe.db.commit()
|
||||
except Exception as e:
|
||||
print("ERROR creating Project from Sales Order:", str(e))
|
||||
|
||||
|
|
@ -50,40 +50,41 @@ def after_insert(doc, method):
|
|||
)
|
||||
|
||||
def create_sales_invoice_from_sales_order(doc, method):
|
||||
try:
|
||||
print("DEBUG: after_submit hook triggered for Sales Order:", doc.name)
|
||||
invoice_ammount = doc.grand_total / 2 if doc.requires_half_payment else doc.grand_total
|
||||
items = []
|
||||
for so_item in doc.items:
|
||||
# proportionally reduce rate if half-payment
|
||||
rate = so_item.rate / 2 if doc.requires_half_payment else so_item.rate
|
||||
qty = so_item.qty # usually full qty, but depends on half-payment rules
|
||||
items.append({
|
||||
"item_code": so_item.item_code,
|
||||
"qty": qty,
|
||||
"rate": rate,
|
||||
"income_account": so_item.income_account,
|
||||
"cost_center": so_item.cost_center,
|
||||
"so_detail": so_item.name # links item to Sales Order
|
||||
})
|
||||
invoice = frappe.get_doc({
|
||||
"doctype": "Sales Invoice",
|
||||
"customer": doc.customer,
|
||||
"company": doc.company,
|
||||
"posting_date": frappe.utils.nowdate(),
|
||||
"due_date": frappe.utils.nowdate(), # or calculate from payment terms
|
||||
"currency": doc.currency,
|
||||
"update_stock": 0,
|
||||
"items": items,
|
||||
"sales_order": doc.name, # link invoice to Sales Order
|
||||
"ignore_pricing_rule": 1,
|
||||
"payment_schedule": doc.payment_schedule if not half_payment else [] # optional
|
||||
})
|
||||
pass
|
||||
# try:
|
||||
# print("DEBUG: after_submit hook triggered for Sales Order:", doc.name)
|
||||
# invoice_ammount = doc.grand_total / 2 if doc.requires_half_payment else doc.grand_total
|
||||
# items = []
|
||||
# for so_item in doc.items:
|
||||
# # proportionally reduce rate if half-payment
|
||||
# rate = so_item.rate / 2 if doc.requires_half_payment else so_item.rate
|
||||
# qty = so_item.qty # usually full qty, but depends on half-payment rules
|
||||
# items.append({
|
||||
# "item_code": so_item.item_code,
|
||||
# "qty": qty,
|
||||
# "rate": rate,
|
||||
# "income_account": so_item.income_account,
|
||||
# "cost_center": so_item.cost_center,
|
||||
# "so_detail": so_item.name # links item to Sales Order
|
||||
# })
|
||||
# invoice = frappe.get_doc({
|
||||
# "doctype": "Sales Invoice",
|
||||
# "customer": doc.customer,
|
||||
# "company": doc.company,
|
||||
# "posting_date": frappe.utils.nowdate(),
|
||||
# "due_date": frappe.utils.nowdate(), # or calculate from payment terms
|
||||
# "currency": doc.currency,
|
||||
# "update_stock": 0,
|
||||
# "items": items,
|
||||
# "sales_order": doc.name, # link invoice to Sales Order
|
||||
# "ignore_pricing_rule": 1,
|
||||
# "payment_schedule": doc.payment_schedule if not half_payment else [] # optional
|
||||
# })
|
||||
|
||||
invoice.insert()
|
||||
invoice.submit()
|
||||
frappe.db.commit()
|
||||
return invoice
|
||||
except Exception as e:
|
||||
print("ERROR creating Sales Invoice from Sales Order:", str(e))
|
||||
frappe.log_error(f"Error creating Sales Invoice from Sales Order {doc.name}: {str(e)}", "Sales Order after_submit Error")
|
||||
# invoice.insert()
|
||||
# invoice.submit()
|
||||
# frappe.db.commit()
|
||||
# return invoice
|
||||
# except Exception as e:
|
||||
# print("ERROR creating Sales Invoice from Sales Order:", str(e))
|
||||
# frappe.log_error(f"Error creating Sales Invoice from Sales Order {doc.name}: {str(e)}", "Sales Order after_submit Error")
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ def on_update(doc, method):
|
|||
if event:
|
||||
tasks = TaskService.get_tasks_by_project(doc.project)
|
||||
task_names = [task.name for task in tasks]
|
||||
TaskService.calculate_and_set_due_dates(task_names=task_names, event=event)
|
||||
TaskService.calculate_and_set_due_dates(task_names=task_names, event=event, triggering_doctype="Service Address 2")
|
||||
|
||||
def after_insert(doc, method):
|
||||
print("DEBUG: After Insert Triggered for Service Appointment")
|
||||
task_names = [task.name for task in TaskService.get_tasks_by_project(doc.project)]
|
||||
TaskService.calculate_and_set_due_dates(task_names=task_names, event="Created")
|
||||
TaskService.calculate_and_set_due_dates(task_names=task_names, event="Created", triggering_doctype="Service Address 2")
|
||||
|
||||
|
|
@ -6,6 +6,7 @@ def before_insert(doc, method):
|
|||
print("DEBUG: Before Insert Triggered for Task")
|
||||
project_doc = frappe.get_doc("Project", doc.project)
|
||||
doc.project_template = project_doc.project_template
|
||||
doc.customer = project_doc.customer
|
||||
if project_doc.job_address:
|
||||
doc.custom_property = project_doc.job_address
|
||||
|
||||
|
|
@ -22,12 +23,12 @@ def after_insert(doc, method):
|
|||
doc.customer, "tasks", {"task": doc.name, "project_template": doc.project_template }
|
||||
)
|
||||
task_names = [task.name for task in TaskService.get_tasks_by_project(doc.project)]
|
||||
TaskService.calculate_and_set_due_dates(task_names, "Created")
|
||||
TaskService.calculate_and_set_due_dates(task_names, "Created", "Task")
|
||||
|
||||
def after_save(doc, method):
|
||||
print("DEBUG: After Save Triggered for Task:", doc.name)
|
||||
event = TaskService.determine_event(doc)
|
||||
if event:
|
||||
task_names = [task.name for task in TaskService.get_tasks_by_project(doc.project)]
|
||||
TaskService.calculate_and_set_due_dates(task_names, event)
|
||||
TaskService.calculate_and_set_due_dates(task_names, event, "Task")
|
||||
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1 @@
|
|||
[
|
||||
{
|
||||
"docstatus": 0,
|
||||
"doctype": "Email Template",
|
||||
"modified": "2026-01-24 07:18:15.939258",
|
||||
"name": "Customer Invoice",
|
||||
"response": "<div class=\"ql-editor read-mode\"><p>-- Copywriting goes here --</p><p>-- Customized Payment Link goes here --</p><p>-- In the meantime --</p><p>Invoice number: {{ name }}</p><p>Amount: {{ grand_total }}</p><p>https://sprinklersnorthwest.com/product/bill-pay/</p></div>",
|
||||
"response_html": null,
|
||||
"subject": "Your Invoice is Ready",
|
||||
"use_html": 0
|
||||
}
|
||||
]
|
||||
[]
|
||||
|
|
@ -7343,22 +7343,6 @@
|
|||
"row_name": null,
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Contact",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2024-12-23 13:33:08.995392",
|
||||
"module": null,
|
||||
"name": "Contact-main-naming_rule",
|
||||
"property": "naming_rule",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "Set by user"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Contact",
|
||||
|
|
@ -7391,22 +7375,6 @@
|
|||
"row_name": null,
|
||||
"value": "creation"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Contact",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2024-12-23 16:11:50.106128",
|
||||
"module": null,
|
||||
"name": "Contact-main-autoname",
|
||||
"property": "autoname",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "full_name"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Contact",
|
||||
|
|
@ -12575,22 +12543,6 @@
|
|||
"row_name": null,
|
||||
"value": "ISS-.YYYY.-"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Contact",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2025-11-26 03:43:13.493067",
|
||||
"module": null,
|
||||
"name": "Contact-main-field_order",
|
||||
"property": "field_order",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "[\"sb_01\", \"custom_column_break_g4zvy\", \"first_name\", \"custom_column_break_hpz5b\", \"middle_name\", \"custom_column_break_3pehb\", \"last_name\", \"contact_section\", \"links\", \"phone_nos\", \"email_ids\", \"custom_column_break_nfqbi\", \"is_primary_contact\", \"is_billing_contact\", \"custom_service_address\", \"user\", \"unsubscribed\", \"more_info\", \"custom_column_break_sn9hu\", \"full_name\", \"address\", \"company_name\", \"designation\", \"department\", \"image\", \"sb_00\", \"custom_column_break_kmlkz\", \"email_id\", \"mobile_no\", \"phone\", \"status\", \"gender\", \"salutation\", \"contact_details\", \"cb_00\", \"custom_test_label\", \"google_contacts\", \"google_contacts_id\", \"sync_with_google_contacts\", \"cb00\", \"pulled_from_google_contacts\", \"custom_column_break_ejxjz\"]"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Contact",
|
||||
|
|
@ -12671,54 +12623,6 @@
|
|||
"row_name": null,
|
||||
"value": "[\"naming_series\", \"salutation\", \"first_name\", \"middle_name\", \"last_name\", \"custom_customer_name\", \"column_break_1\", \"lead_name\", \"customer_type\", \"companies\", \"quotations\", \"onsite_meetings\", \"job_title\", \"gender\", \"source\", \"col_break123\", \"lead_owner\", \"status\", \"customer\", \"type\", \"request_type\", \"contact_info_tab\", \"email_id\", \"website\", \"column_break_20\", \"mobile_no\", \"whatsapp_no\", \"column_break_16\", \"phone\", \"phone_ext\", \"organization_section\", \"company_name\", \"no_of_employees\", \"column_break_28\", \"annual_revenue\", \"industry\", \"market_segment\", \"column_break_31\", \"territory\", \"fax\", \"address_section\", \"address_html\", \"column_break_38\", \"city\", \"state\", \"country\", \"column_break2\", \"contact_html\", \"qualification_tab\", \"qualification_status\", \"column_break_64\", \"qualified_by\", \"qualified_on\", \"other_info_tab\", \"campaign_name\", \"company\", \"column_break_22\", \"language\", \"image\", \"title\", \"column_break_50\", \"disabled\", \"unsubscribed\", \"blog_subscriber\", \"activities_tab\", \"open_activities_html\", \"all_activities_section\", \"all_activities_html\", \"notes_tab\", \"notes_html\", \"notes\", \"dashboard_tab\", \"contacts\", \"primary_contact\", \"properties\", \"custom_billing_address\"]"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Lead",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-19 16:24:58.030843",
|
||||
"module": null,
|
||||
"name": "Lead-main-autoname",
|
||||
"property": "autoname",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "format:{lead_name}-#-{YYYY}-{MM}-{####}"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Project",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-19 17:52:08.608481",
|
||||
"module": null,
|
||||
"name": "Project-main-autoname",
|
||||
"property": "autoname",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "format:{job_address}-{project_template}-#-PRO-{#####}-{YYYY}"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Project",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-19 17:52:08.652993",
|
||||
"module": null,
|
||||
"name": "Project-main-field_order",
|
||||
"property": "field_order",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "[\"custom_column_break_k7sgq\", \"custom_installation_address\", \"naming_series\", \"project_name\", \"job_address\", \"status\", \"custom_warranty_duration_days\", \"custom_warranty_expiration_date\", \"custom_warranty_information\", \"project_type\", \"percent_complete_method\", \"percent_complete\", \"column_break_5\", \"project_template\", \"expected_start_date\", \"expected_end_date\", \"custom_completion_date\", \"priority\", \"custom_foreman\", \"custom_hidden_fields\", \"department\", \"is_active\", \"custom_address\", \"custom_section_break_lgkpd\", \"custom_workflow_related_custom_fields__landry\", \"custom_permit_status\", \"custom_utlity_locate_status\", \"custom_crew_scheduling\", \"customer_details\", \"customer\", \"column_break_14\", \"sales_order\", \"users_section\", \"users\", \"copied_from\", \"section_break0\", \"notes\", \"section_break_18\", \"actual_start_date\", \"actual_time\", \"column_break_20\", \"actual_end_date\", \"project_details\", \"estimated_costing\", \"total_costing_amount\", \"total_expense_claim\", \"total_purchase_cost\", \"company\", \"column_break_28\", \"total_sales_amount\", \"total_billable_amount\", \"total_billed_amount\", \"total_consumed_material_cost\", \"cost_center\", \"margin\", \"gross_margin\", \"column_break_37\", \"per_gross_margin\", \"monitor_progress\", \"collect_progress\", \"holiday_list\", \"frequency\", \"from_time\", \"to_time\", \"first_email\", \"second_email\", \"daily_time_to_send\", \"day_to_send\", \"weekly_time_to_send\", \"column_break_45\", \"subject\", \"message\"]"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Address",
|
||||
|
|
@ -12767,38 +12671,6 @@
|
|||
"row_name": null,
|
||||
"value": "[\"address_details\", \"custom_column_break_vqa4d\", \"custom_column_break_jw2ty\", \"custom_installationservice_address\", \"custom_billing_address\", \"is_shipping_address\", \"is_primary_address\", \"custom_is_compnay_address\", \"custom_column_break_ky1zo\", \"custom_estimate_sent_status\", \"custom_onsite_meeting_scheduled\", \"custom_job_status\", \"custom_payment_received_status\", \"custom_section_break_fvgdt\", \"address_title\", \"primary_contact\", \"address_type\", \"address_line1\", \"address_line2\", \"custom_linked_city\", \"custom_subdivision\", \"is_your_company_address\", \"custom_column_break_3mo7x\", \"state\", \"city\", \"pincode\", \"county\", \"country\", \"full_address\", \"latitude\", \"longitude\", \"onsite_meeting_scheduled\", \"estimate_sent_status\", \"job_status\", \"payment_received_status\", \"custom_column_break_rrto0\", \"custom_customer_to_bill\", \"lead_name\", \"customer_type\", \"customer_name\", \"contacts\", \"companies\", \"quotations\", \"onsite_meetings\", \"projects\", \"sales_orders\", \"tasks\", \"custom_contact_name\", \"phone\", \"email_id\", \"fax\", \"tax_category\", \"disabled\", \"custom_section_break_aecpx\", \"column_break0\", \"custom_show_irrigation_district\", \"custom_google_map\", \"custom_latitude\", \"custom_longitude\", \"custom_address_for_coordinates\", \"linked_with\", \"custom_linked_contacts\", \"links\", \"custom_column_break_9cbvb\", \"custom_linked_companies\", \"custom_irrigation\", \"custom_upcoming_services\", \"custom_service_type\", \"custom_service_route\", \"custom_confirmation_status\", \"custom_backflow_test_form_filed\", \"custom_column_break_j79td\", \"custom_technician_assigned\", \"custom_scheduled_date\", \"custom_column_break_sqplk\", \"custom_test_route\", \"custom_tech\", \"custom_column_break_wcs7g\", \"custom_section_break_zruvq\", \"custom_irrigation_district\", \"custom_serial_\", \"custom_makemodel_\", \"custom_column_break_djjw3\", \"custom_backflow_location\", \"custom_shutoff_location\", \"custom_valve_boxes\", \"custom_timer_type_and_location\", \"custom_column_break_slusf\", \"custom_section_break_5d1cf\", \"custom_installed_by_sprinklers_nw\", \"custom_column_break_th7rq\", \"custom_installed_for\", \"custom_install_month\", \"custom_install_year\", \"custom_column_break_4itse\", \"custom_section_break_xfdtv\", \"custom_backflow_test_report\", \"custom_column_break_oxppn\", \"custom_photo_attachment\"]"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Address",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-21 03:31:53.933678",
|
||||
"module": null,
|
||||
"name": "Address-main-links_order",
|
||||
"property": "links_order",
|
||||
"property_type": "Small Text",
|
||||
"row_name": null,
|
||||
"value": "[\"21ddd8462e\", \"c26b89d0d3\", \"ee207f2316\"]"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Address",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-21 03:31:53.991156",
|
||||
"module": null,
|
||||
"name": "Address-main-states_order",
|
||||
"property": "states_order",
|
||||
"property_type": "Small Text",
|
||||
"row_name": null,
|
||||
"value": "[\"62m56h85vo\", \"62m5uugrvr\", \"62m57bgpkf\", \"62m5fgrjb0\"]"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Project Template",
|
||||
|
|
@ -15134,5 +15006,133 @@
|
|||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "eval:doc.calculate_from == \"Task\""
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Lead",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-26 01:51:02.536818",
|
||||
"module": null,
|
||||
"name": "Lead-main-autoname",
|
||||
"property": "autoname",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "format:{custom_customer_name}-#-{YYYY}-{MM}-{####}"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Address",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-26 02:35:09.522811",
|
||||
"module": null,
|
||||
"name": "Address-main-links_order",
|
||||
"property": "links_order",
|
||||
"property_type": "Small Text",
|
||||
"row_name": null,
|
||||
"value": "[\"21ddd8462e\", \"c26b89d0d3\", \"ee207f2316\"]"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Address",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-26 02:35:09.598292",
|
||||
"module": null,
|
||||
"name": "Address-main-states_order",
|
||||
"property": "states_order",
|
||||
"property_type": "Small Text",
|
||||
"row_name": null,
|
||||
"value": "[\"62m56h85vo\", \"62m5uugrvr\", \"62m57bgpkf\", \"62m5fgrjb0\"]"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Contact",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-26 02:40:01.394710",
|
||||
"module": null,
|
||||
"name": "Contact-main-naming_rule",
|
||||
"property": "naming_rule",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "Expression"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Contact",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-26 02:40:01.427255",
|
||||
"module": null,
|
||||
"name": "Contact-main-autoname",
|
||||
"property": "autoname",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "format:{full-name}-#-{MM}-{YYYY}-{####}"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Contact",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-26 02:40:01.458831",
|
||||
"module": null,
|
||||
"name": "Contact-main-field_order",
|
||||
"property": "field_order",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "[\"sb_01\", \"custom_column_break_g4zvy\", \"first_name\", \"custom_column_break_hpz5b\", \"middle_name\", \"custom_column_break_3pehb\", \"last_name\", \"email\", \"customer_type\", \"customer_name\", \"addresses\", \"contact_section\", \"links\", \"phone_nos\", \"email_ids\", \"custom_column_break_nfqbi\", \"is_primary_contact\", \"is_billing_contact\", \"custom_service_address\", \"user\", \"unsubscribed\", \"more_info\", \"custom_column_break_sn9hu\", \"full_name\", \"address\", \"company_name\", \"designation\", \"role\", \"department\", \"image\", \"sb_00\", \"custom_column_break_kmlkz\", \"email_id\", \"mobile_no\", \"phone\", \"status\", \"gender\", \"salutation\", \"contact_details\", \"cb_00\", \"custom_test_label\", \"google_contacts\", \"google_contacts_id\", \"sync_with_google_contacts\", \"cb00\", \"pulled_from_google_contacts\", \"custom_column_break_ejxjz\"]"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Project",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-26 10:42:06.682515",
|
||||
"module": null,
|
||||
"name": "Project-main-autoname",
|
||||
"property": "autoname",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "format:{project_template}-#-PRO-{#####}-{YYYY}"
|
||||
},
|
||||
{
|
||||
"default_value": null,
|
||||
"doc_type": "Project",
|
||||
"docstatus": 0,
|
||||
"doctype": "Property Setter",
|
||||
"doctype_or_field": "DocType",
|
||||
"field_name": null,
|
||||
"is_system_generated": 0,
|
||||
"modified": "2026-01-26 10:42:06.862234",
|
||||
"module": null,
|
||||
"name": "Project-main-field_order",
|
||||
"property": "field_order",
|
||||
"property_type": "Data",
|
||||
"row_name": null,
|
||||
"value": "[\"custom_column_break_k7sgq\", \"custom_installation_address\", \"naming_series\", \"project_name\", \"job_address\", \"status\", \"custom_warranty_duration_days\", \"custom_warranty_expiration_date\", \"custom_warranty_information\", \"project_type\", \"percent_complete_method\", \"percent_complete\", \"column_break_5\", \"project_template\", \"expected_start_date\", \"expected_start_time\", \"expected_end_date\", \"expected_end_time\", \"is_scheduled\", \"invoice_status\", \"custom_completion_date\", \"priority\", \"custom_foreman\", \"custom_hidden_fields\", \"department\", \"service_appointment\", \"tasks\", \"is_active\", \"custom_address\", \"custom_section_break_lgkpd\", \"custom_workflow_related_custom_fields__landry\", \"custom_permit_status\", \"custom_utlity_locate_status\", \"custom_crew_scheduling\", \"customer_details\", \"customer\", \"column_break_14\", \"sales_order\", \"users_section\", \"users\", \"copied_from\", \"section_break0\", \"notes\", \"section_break_18\", \"actual_start_date\", \"actual_start_time\", \"actual_time\", \"column_break_20\", \"actual_end_date\", \"actual_end_time\", \"project_details\", \"estimated_costing\", \"total_costing_amount\", \"total_expense_claim\", \"total_purchase_cost\", \"company\", \"column_break_28\", \"total_sales_amount\", \"total_billable_amount\", \"total_billed_amount\", \"total_consumed_material_cost\", \"cost_center\", \"margin\", \"gross_margin\", \"column_break_37\", \"per_gross_margin\", \"monitor_progress\", \"collect_progress\", \"holiday_list\", \"frequency\", \"from_time\", \"to_time\", \"first_email\", \"second_email\", \"daily_time_to_send\", \"day_to_send\", \"weekly_time_to_send\", \"column_break_45\", \"subject\", \"message\"]"
|
||||
}
|
||||
]
|
||||
|
|
@ -8,7 +8,7 @@ class ServiceAppointmentService:
|
|||
"""Create a new Service Appointment document."""
|
||||
print("DEBUG: Creating Service Appointment with data:", data)
|
||||
service_appointment_doc = frappe.get_doc({
|
||||
"doctype": "Service Appointment",
|
||||
"doctype": "Service Address 2",
|
||||
**data
|
||||
})
|
||||
service_appointment_doc.insert()
|
||||
|
|
@ -19,7 +19,7 @@ class ServiceAppointmentService:
|
|||
def get_full_dict(service_appointment_name: str) -> dict:
|
||||
"""Retrieve a Service Appointment document as a full dictionary."""
|
||||
print(f"DEBUG: Retrieving Service Appointment document with name: {service_appointment_name}")
|
||||
service_appointment = frappe.get_doc("Service Appointment", service_appointment_name).as_dict()
|
||||
service_appointment = frappe.get_doc("Service Address 2", service_appointment_name).as_dict()
|
||||
service_appointment["service_address"] = AddressService.get_or_throw(service_appointment["service_address"]).as_dict()
|
||||
service_appointment["customer"] = ClientService.get_client_or_throw(service_appointment["customer"]).as_dict()
|
||||
service_appointment["project"] = DbService.get_doc_or_throw("Project", service_appointment["project"]).as_dict()
|
||||
|
|
@ -30,7 +30,7 @@ class ServiceAppointmentService:
|
|||
def update_scheduled_dates(service_appointment_name: str, start_date, end_date, start_time=None, end_time=None):
|
||||
"""Update the scheduled start and end dates of a Service Appointment."""
|
||||
print(f"DEBUG: Updating scheduled dates for Service Appointment {service_appointment_name} to start: {start_date}, end: {end_date}")
|
||||
service_appointment = DbService.get_or_throw("Service Appointment", service_appointment_name)
|
||||
service_appointment = DbService.get_or_throw("Service Address 2", service_appointment_name)
|
||||
service_appointment.expected_start_date = start_date
|
||||
service_appointment.expected_end_date = end_date
|
||||
if start_time:
|
||||
|
|
@ -45,7 +45,7 @@ class ServiceAppointmentService:
|
|||
def update_field(service_appointment_name: str, updates: list[tuple[str, any]]):
|
||||
"""Update specific fields of a Service Appointment."""
|
||||
print(f"DEBUG: Updating fields for Service Appointment {service_appointment_name} with updates: {updates}")
|
||||
service_appointment = DbService.get_or_throw("Service Appointment", service_appointment_name)
|
||||
service_appointment = DbService.get_or_throw("Service Address 2", service_appointment_name)
|
||||
for field, value in updates:
|
||||
setattr(service_appointment, field, value)
|
||||
service_appointment.save()
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ class TaskService:
|
|||
|
||||
|
||||
@staticmethod
|
||||
def calculate_and_set_due_dates(task_names: list[str], event: str):
|
||||
def calculate_and_set_due_dates(task_names: list[str], event: str, triggering_doctype: str):
|
||||
"""Calculate the due date for a list of tasks based on their expected end dates."""
|
||||
for task_name in task_names:
|
||||
TaskService.check_and_update_task_due_date(task_name, event)
|
||||
TaskService.check_and_update_task_due_date(task_name, event, triggering_doctype)
|
||||
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -19,9 +19,15 @@ class TaskService:
|
|||
return tasks
|
||||
|
||||
@staticmethod
|
||||
def check_and_update_task_due_date(task_name: str, event: str):
|
||||
def check_and_update_task_due_date(task_name: str, event: str, triggering_doctype: str):
|
||||
"""Determine the triggering configuration for a given task."""
|
||||
task_type_doc = TaskService.get_task_type_doc(task_name)
|
||||
if task_type_doc.no_due_date:
|
||||
print(f"DEBUG: Task {task_name} is marked as no due date, skipping calculation.")
|
||||
return
|
||||
if task_type_doc.triggering_doctype != triggering_doctype:
|
||||
print(f"DEBUG: Task {task_name} triggering doctype {task_type_doc.triggering_doctype} does not match triggering doctype {triggering_doctype}, skipping calculation.")
|
||||
return
|
||||
if task_type_doc.trigger != event:
|
||||
print(f"DEBUG: Task {task_name} trigger {task_type_doc.trigger} does not match event {event}, skipping calculation.")
|
||||
return
|
||||
|
|
@ -31,9 +37,9 @@ class TaskService:
|
|||
if task_type_doc.no_due_date:
|
||||
print(f"DEBUG: Task {task_name} is marked as no due date, skipping calculation.")
|
||||
return
|
||||
calculated_from = task_type_doc.calculated_from
|
||||
calculate_from = task_type_doc.calculate_from
|
||||
trigger = task_type_doc.trigger
|
||||
print(f"DEBUG: Calculating triggering data for Task {task_name} from {calculated_from} on trigger {trigger}")
|
||||
print(f"DEBUG: Calculating triggering data for Task {task_name} from {calculate_from} on trigger {trigger}")
|
||||
|
||||
|
||||
triggering_doc_dict = TaskService.get_triggering_doc_dict(task_name=task_name, task_type_doc=task_type_doc)
|
||||
|
|
@ -59,7 +65,7 @@ class TaskService:
|
|||
|
||||
@staticmethod
|
||||
def get_task_type_doc(task_name: str):
|
||||
task_type_name = frappe.get_value("Task", task_name, "task_type")
|
||||
task_type_name = frappe.get_value("Task", task_name, "type")
|
||||
return frappe.get_doc("Task Type", task_type_name)
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -99,12 +105,12 @@ class TaskService:
|
|||
def get_triggering_doc_dict(task_name: str, task_type_doc) -> dict | None:
|
||||
project_name = frappe.get_value("Task", task_name, "project")
|
||||
dict = None
|
||||
if task_type_doc.calculated_from == "Project":
|
||||
if task_type_doc.calculate_from == "Project":
|
||||
dict = frappe.get_doc("Project", project_name).to_dict()
|
||||
if task_type_doc.calculated_from == "Service Appointment":
|
||||
if task_type_doc.calculate_from == "Service Address 2":
|
||||
service_name = frappe.get_value("Project", project_name, "service_appointment")
|
||||
dict = frappe.get_doc("Service Appointment", service_name).to_dict()
|
||||
if task_type_doc.calculated_from == "Task":
|
||||
dict = frappe.get_doc("Service Address 2", service_name).to_dict()
|
||||
if task_type_doc.calculate_from == "Task":
|
||||
project_doc = frappe.get_doc("Project", project_name)
|
||||
for task in project_doc.tasks:
|
||||
if task.task_type == task_type_doc.task_type_calculate_from:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue