Compare commits
No commits in common. "5e192a61e18b8d89b41e216e69844d4b432a929a" and "f0acbd0630d8959c9557c60a205be18f46c58d83" have entirely different histories.
5e192a61e1
...
f0acbd0630
14 changed files with 156 additions and 420 deletions
|
|
@ -15,7 +15,7 @@ def get_estimate_table_data_v2(filters={}, sortings=[], page=1, page_size=10):
|
||||||
"""Get paginated estimate table data with filtering and sorting."""
|
"""Get paginated estimate table data with filtering and sorting."""
|
||||||
print("DEBUG: Raw estimate options received:", filters, sortings, page, page_size)
|
print("DEBUG: Raw estimate options received:", filters, sortings, page, page_size)
|
||||||
filters, sortings, page, page_size = DbUtils.process_query_conditions(filters, sortings, page, page_size)
|
filters, sortings, page, page_size = DbUtils.process_query_conditions(filters, sortings, page, page_size)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_estimate_table_data(filters={}, sortings=[], page=1, page_size=10):
|
def get_estimate_table_data(filters={}, sortings=[], page=1, page_size=10):
|
||||||
|
|
@ -151,7 +151,7 @@ def send_estimate_email(estimate_name):
|
||||||
print("DEBUG: Sending estimate email for:", estimate_name)
|
print("DEBUG: Sending estimate email for:", estimate_name)
|
||||||
quotation = frappe.get_doc("Quotation", estimate_name)
|
quotation = frappe.get_doc("Quotation", estimate_name)
|
||||||
|
|
||||||
|
|
||||||
if not DbService.exists("Contact", quotation.contact_person):
|
if not DbService.exists("Contact", quotation.contact_person):
|
||||||
return build_error_response("No email found for the customer.", 400)
|
return build_error_response("No email found for the customer.", 400)
|
||||||
party = ContactService.get_or_throw(quotation.contact_person)
|
party = ContactService.get_or_throw(quotation.contact_person)
|
||||||
|
|
@ -414,7 +414,7 @@ def upsert_estimate(data):
|
||||||
# estimate.customer_address = data.get("address_name")
|
# estimate.customer_address = data.get("address_name")
|
||||||
# estimate.letter_head = data.get("company")
|
# estimate.letter_head = data.get("company")
|
||||||
# estimate.from_onsite_meeting = data.get("onsite_meeting", None)
|
# estimate.from_onsite_meeting = data.get("onsite_meeting", None)
|
||||||
|
|
||||||
# Clear existing items and add new ones
|
# Clear existing items and add new ones
|
||||||
estimate.items = []
|
estimate.items = []
|
||||||
for item in data.get("items", []):
|
for item in data.get("items", []):
|
||||||
|
|
@ -481,31 +481,6 @@ def upsert_estimate(data):
|
||||||
print(f"DEBUG: Error in upsert_estimate: {str(e)}")
|
print(f"DEBUG: Error in upsert_estimate: {str(e)}")
|
||||||
return build_error_response(str(e), 500)
|
return build_error_response(str(e), 500)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_unapproved_estimates_count(company):
|
|
||||||
"""Get the number of unapproved estimates."""
|
|
||||||
try:
|
|
||||||
draft_filters = {'status': "Draft", "company": company}
|
|
||||||
submitted_filters = {'status': "Submitted", "company": company}
|
|
||||||
draft_count = frappe.db.count("Quotation", filters=draft_filters)
|
|
||||||
submitted_count = frappe.db.count("Quotation", filters=submitted_filters)
|
|
||||||
return build_success_response([draft_count, submitted_count])
|
|
||||||
except Exception as e:
|
|
||||||
return build_error_response(str(e), 500)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_estimates_half_down_count(company):
|
|
||||||
"""Get the number unpaid half-down estimates."""
|
|
||||||
try:
|
|
||||||
filters = {'requires_half_payment': True, 'company': company}
|
|
||||||
count = frappe.db.count("Quotation", filters=filters)
|
|
||||||
return build_success_response([count])
|
|
||||||
except Exception as e:
|
|
||||||
return build_error_response(str(e), 500)
|
|
||||||
|
|
||||||
|
|
||||||
def get_estimate_history(estimate_name):
|
def get_estimate_history(estimate_name):
|
||||||
"""Get the history of changes for a specific estimate."""
|
"""Get the history of changes for a specific estimate."""
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,11 @@
|
||||||
import frappe, json
|
import frappe, json
|
||||||
from custom_ui.db_utils import process_query_conditions, build_datatable_dict, get_count_or_filters, build_success_response, build_error_response
|
from custom_ui.db_utils import process_query_conditions, build_datatable_dict, get_count_or_filters, build_success_response, build_error_response
|
||||||
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
|
|
||||||
|
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
# INVOICES API METHODS
|
# INVOICES API METHODS
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def create_invoice_for_job(job_name):
|
|
||||||
"""Create the invoice from a sales order of a job."""
|
|
||||||
try:
|
|
||||||
project = frappe.get_doc("Project", job_name)
|
|
||||||
sales_order = project.sales_order
|
|
||||||
invoice = make_sales_invoice(sales_order)
|
|
||||||
invoice.save()
|
|
||||||
return build_success_response(invoice.as_dict())
|
|
||||||
except Exception as e:
|
|
||||||
return build_error_response(str(e), 500)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_invoices_late_count():
|
|
||||||
"""Return Due, 30-day late, 90-day late, and Lien-worthy late accounts."""
|
|
||||||
try:
|
|
||||||
dummy_result = [10, 4, 5, 1]
|
|
||||||
print("DEBUG: DUMMY RESULT:", dummy_result)
|
|
||||||
return build_success_response(dummy_result)
|
|
||||||
except Exception as e:
|
|
||||||
return build_error_response(str(e), 500)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_invoice_table_data(filters={}, sortings=[], page=1, page_size=10):
|
def get_invoice_table_data(filters={}, sortings=[], page=1, page_size=10):
|
||||||
"""Get paginated invoice table data with filtering and sorting support."""
|
"""Get paginated invoice table data with filtering and sorting support."""
|
||||||
|
|
@ -43,7 +18,7 @@ def get_invoice_table_data(filters={}, sortings=[], page=1, page_size=10):
|
||||||
else:
|
else:
|
||||||
count = frappe.db.count("Sales Invoice", filters=processed_filters)
|
count = frappe.db.count("Sales Invoice", filters=processed_filters)
|
||||||
|
|
||||||
print(f"DEBUG: Number of invoices returned: {count}")
|
print(f"DEBUG: Number of invoice returned: {count}")
|
||||||
|
|
||||||
invoices = frappe.db.get_all(
|
invoices = frappe.db.get_all(
|
||||||
"Sales Invoice",
|
"Sales Invoice",
|
||||||
|
|
|
||||||
|
|
@ -7,64 +7,6 @@ from frappe.utils import getdate
|
||||||
# JOB MANAGEMENT API METHODS
|
# JOB MANAGEMENT API METHODS
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_jobs_in_queue_count(company):
|
|
||||||
try:
|
|
||||||
filters = {
|
|
||||||
'company': company,
|
|
||||||
'is_scheduled': True,
|
|
||||||
}
|
|
||||||
count = frappe.db.count("Project", filters=filters)
|
|
||||||
return build_success_response([count])
|
|
||||||
except Exception as e:
|
|
||||||
return build_error_response(str(e), 500)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_jobs_in_progress_count(company):
|
|
||||||
try:
|
|
||||||
today = getdate()
|
|
||||||
filters = {
|
|
||||||
'company': company,
|
|
||||||
'invoice_status': 'Not Ready',
|
|
||||||
'expected_start_date': ['<=', today],
|
|
||||||
'expected_end_date': ['>=', today],
|
|
||||||
}
|
|
||||||
count = frappe.db.count("Project", filters=filters)
|
|
||||||
return build_success_response([count])
|
|
||||||
except Exception as e:
|
|
||||||
return build_error_response(str(e), 500)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_jobs_late_count(company):
|
|
||||||
try:
|
|
||||||
today = getdate()
|
|
||||||
filters = {
|
|
||||||
'company': company,
|
|
||||||
'invoice_status': 'Not Ready',
|
|
||||||
'expected_end_date': ['<', today]
|
|
||||||
}
|
|
||||||
count = frappe.db.count("Project", filters=filters)
|
|
||||||
return build_success_response([count])
|
|
||||||
except Exception as e:
|
|
||||||
return build_error_response(str(e), 500)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_jobs_to_invoice_count(company):
|
|
||||||
try:
|
|
||||||
filters = {
|
|
||||||
'company': company,
|
|
||||||
'invoice_status': 'Ready to Invoice',
|
|
||||||
}
|
|
||||||
count = frappe.db.count("Project", filters=filters)
|
|
||||||
return build_success_response([count])
|
|
||||||
except Exception as e:
|
|
||||||
return build_error_response(str(e), 500)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_job_templates(company=None):
|
def get_job_templates(company=None):
|
||||||
"""Get list of job (project) templates."""
|
"""Get list of job (project) templates."""
|
||||||
|
|
@ -77,7 +19,6 @@ def get_job_templates(company=None):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return build_error_response(str(e), 500)
|
return build_error_response(str(e), 500)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_job_from_sales_order(sales_order_name):
|
def create_job_from_sales_order(sales_order_name):
|
||||||
"""Create a Job (Project) from a given Sales Order"""
|
"""Create a Job (Project) from a given Sales Order"""
|
||||||
|
|
@ -211,10 +152,9 @@ def get_jobs_table_data(filters={}, sortings=[], page=1, page_size=10):
|
||||||
tableRow = {}
|
tableRow = {}
|
||||||
tableRow["id"] = project["name"]
|
tableRow["id"] = project["name"]
|
||||||
tableRow["name"] = project["name"]
|
tableRow["name"] = project["name"]
|
||||||
tableRow["job_address"] = project["job_address"]
|
tableRow["installation_address"] = project.get("custom_installation_address", "")
|
||||||
tableRow["customer"] = project.get("customer", "")
|
tableRow["customer"] = project.get("customer", "")
|
||||||
tableRow["status"] = project.get("status", "")
|
tableRow["status"] = project.get("status", "")
|
||||||
tableRow["invoice_status"] = project.get("invoice_status")
|
|
||||||
tableRow["percent_complete"] = project.get("percent_complete", 0)
|
tableRow["percent_complete"] = project.get("percent_complete", 0)
|
||||||
tableRows.append(tableRow)
|
tableRows.append(tableRow)
|
||||||
|
|
||||||
|
|
@ -263,7 +203,7 @@ def get_projects_for_calendar(start_date, end_date, company=None, project_templa
|
||||||
filters["project_template"] = ["in", project_templates]
|
filters["project_template"] = ["in", project_templates]
|
||||||
unscheduled_filters = filters.copy()
|
unscheduled_filters = filters.copy()
|
||||||
unscheduled_filters["is_scheduled"] = 0
|
unscheduled_filters["is_scheduled"] = 0
|
||||||
|
|
||||||
# add to filter for if expected_start_date is between start_date and end_date OR expected_end_date is between start_date and end_date
|
# add to filter for if expected_start_date is between start_date and end_date OR expected_end_date is between start_date and end_date
|
||||||
filters["expected_start_date"] = ["<=", getdate(end_date)]
|
filters["expected_start_date"] = ["<=", getdate(end_date)]
|
||||||
filters["expected_end_date"] = [">=", getdate(start_date)]
|
filters["expected_end_date"] = [">=", getdate(start_date)]
|
||||||
|
|
@ -299,4 +239,4 @@ def update_job_scheduled_dates(job_name: str, new_start_date: str = None, new_en
|
||||||
project.save()
|
project.save()
|
||||||
return build_success_response(project.as_dict())
|
return build_success_response(project.as_dict())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return build_error_response(str(e), 500)
|
return build_error_response(str(e), 500)
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import frappe
|
|
||||||
from custom_ui.db_utils import build_success_response, build_error_response
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_incomplete_bids(company):
|
|
||||||
print("Getting Incomplete Bids")
|
|
||||||
try:
|
|
||||||
filters = {'status': "Unscheduled", 'company': company}
|
|
||||||
count = frappe.db.count("On-Site Meeting", filters=filters)
|
|
||||||
print("Incomplete Bids:", count)
|
|
||||||
return build_success_response([count])
|
|
||||||
except Exception as e:
|
|
||||||
return build_error_response(str(e), 500)
|
|
||||||
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import frappe
|
import frappe
|
||||||
import datetime
|
|
||||||
from custom_ui.db_utils import process_query_conditions, build_datatable_dict, get_count_or_filters, build_success_response, build_error_response
|
from custom_ui.db_utils import process_query_conditions, build_datatable_dict, get_count_or_filters, build_success_response, build_error_response
|
||||||
from custom_ui.services import DbService
|
from custom_ui.services import DbService
|
||||||
|
|
||||||
|
|
@ -47,26 +46,23 @@ def get_task_status_options():
|
||||||
def get_tasks_due(subject_filter, current_company):
|
def get_tasks_due(subject_filter, current_company):
|
||||||
"""Return the number of items due today of the type of subject_filter"""
|
"""Return the number of items due today of the type of subject_filter"""
|
||||||
try:
|
try:
|
||||||
today = datetime.date.today()
|
|
||||||
due_filters = {
|
due_filters = {
|
||||||
'subject': ['like', f'%{subject_filter}%'],
|
'subject': ['like', f'%{subject_filter}%'],
|
||||||
'status': ['not in', ["Template", "Completed", "Cancelled"]],
|
'status': ['not in', ["Template", "Completed", "Cancelled"]],
|
||||||
'company': current_company,
|
'company': current_company,
|
||||||
'exp_end_date': today,
|
|
||||||
# Add due date filter here
|
# Add due date filter here
|
||||||
}
|
}
|
||||||
completed_filters = {
|
completed_filters = {
|
||||||
'subject': ['like', f'%{subject_filter}%'],
|
'subject': ['like', f'%{subject_filter}%'],
|
||||||
'status': ['not in', ["Template", "Cancelled"]],
|
'status': ['not in', ["Template", "Cancelled"]],
|
||||||
'company': current_company,
|
'company': current_company,
|
||||||
'exp_end_date': today,
|
|
||||||
# Add due date filter here
|
# Add due date filter here
|
||||||
}
|
}
|
||||||
overdue_filters = {
|
overdue_filters = {
|
||||||
'subject': ['like', f'%{subject_filter}%'],
|
'subject': ['like', f'%{subject_filter}%'],
|
||||||
'status': ['not in', ["Template", "Completed", "Cancelled"]],
|
'status': ['not in', ["Template", "Completed", "Cancelled"]],
|
||||||
'company': current_company,
|
'company': current_company,
|
||||||
'exp_end_date': ["<", today]
|
|
||||||
# Add overdue date filtering here
|
# Add overdue date filtering here
|
||||||
}
|
}
|
||||||
due_count = frappe.db.count("Task", filters=due_filters)
|
due_count = frappe.db.count("Task", filters=due_filters)
|
||||||
|
|
|
||||||
|
|
@ -178,8 +178,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:38.816045",
|
"modified": "2026-01-22 11:05:23.661458",
|
||||||
"module": "Custom UI",
|
"module": "Custom UI",
|
||||||
"name": "Customer Task Link",
|
"name": "Customer Task Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -396,8 +396,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:38.886150",
|
"modified": "2026-01-22 11:05:23.730085",
|
||||||
"module": "Custom UI",
|
"module": "Custom UI",
|
||||||
"name": "Address Task Link",
|
"name": "Address Task Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -550,8 +550,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:38.953132",
|
"modified": "2026-01-22 11:05:23.799282",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Lead Companies Link",
|
"name": "Lead Companies Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -768,8 +768,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.023393",
|
"modified": "2026-01-22 11:05:23.867486",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Address Project Link",
|
"name": "Address Project Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -986,8 +986,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.093123",
|
"modified": "2026-01-22 11:05:23.934979",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Address Quotation Link",
|
"name": "Address Quotation Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -1204,8 +1204,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.163081",
|
"modified": "2026-01-22 11:05:24.001835",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Address On-Site Meeting Link",
|
"name": "Address On-Site Meeting Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -1422,8 +1422,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.236784",
|
"modified": "2026-01-22 11:05:24.069255",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Address Sales Order Link",
|
"name": "Address Sales Order Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -1576,8 +1576,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.305461",
|
"modified": "2026-01-22 11:05:24.137117",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Contact Address Link",
|
"name": "Contact Address Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -1730,8 +1730,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.376523",
|
"modified": "2026-01-22 11:05:24.201857",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Lead On-Site Meeting Link",
|
"name": "Lead On-Site Meeting Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -2332,8 +2332,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.472141",
|
"modified": "2026-01-22 11:05:24.288443",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Quotation Template",
|
"name": "Quotation Template",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -2830,8 +2830,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.556506",
|
"modified": "2026-01-22 11:05:24.372116",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Quotation Template Item",
|
"name": "Quotation Template Item",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -2984,8 +2984,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.624602",
|
"modified": "2026-01-22 11:05:24.439961",
|
||||||
"module": "Custom UI",
|
"module": "Custom UI",
|
||||||
"name": "Customer Company Link",
|
"name": "Customer Company Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -3138,8 +3138,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.690715",
|
"modified": "2026-01-22 11:05:24.506463",
|
||||||
"module": "Custom UI",
|
"module": "Custom UI",
|
||||||
"name": "Customer Address Link",
|
"name": "Customer Address Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -3292,8 +3292,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.758626",
|
"modified": "2026-01-22 11:05:24.575135",
|
||||||
"module": "Custom UI",
|
"module": "Custom UI",
|
||||||
"name": "Customer Contact Link",
|
"name": "Customer Contact Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -3446,8 +3446,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.825642",
|
"modified": "2026-01-22 11:05:24.644548",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Address Contact Link",
|
"name": "Address Contact Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -3600,8 +3600,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.896330",
|
"modified": "2026-01-22 11:05:24.712407",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Customer On-Site Meeting Link",
|
"name": "Customer On-Site Meeting Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -3754,8 +3754,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:39.967073",
|
"modified": "2026-01-22 11:05:24.786735",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Customer Project Link",
|
"name": "Customer Project Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -3908,8 +3908,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:40.037175",
|
"modified": "2026-01-22 11:05:24.856324",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Customer Quotation Link",
|
"name": "Customer Quotation Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -4062,8 +4062,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:40.106626",
|
"modified": "2026-01-22 11:05:24.924254",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Customer Sales Order Link",
|
"name": "Customer Sales Order Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -4216,8 +4216,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:40.174670",
|
"modified": "2026-01-22 11:05:24.990458",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Lead Address Link",
|
"name": "Lead Address Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -4370,8 +4370,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:40.246628",
|
"modified": "2026-01-22 11:05:25.058368",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Lead Contact Link",
|
"name": "Lead Contact Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -4524,8 +4524,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:40.318639",
|
"modified": "2026-01-22 11:05:25.124946",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Lead Quotation Link",
|
"name": "Lead Quotation Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
@ -4678,8 +4678,8 @@
|
||||||
"make_attachments_public": 0,
|
"make_attachments_public": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": null,
|
"menu_index": null,
|
||||||
"migration_hash": "c40d7d206c7d4601cdb57fde7ab57c12",
|
"migration_hash": "5a00edceb2f575ccfc93ed99c01bc0b7",
|
||||||
"modified": "2026-01-24 11:42:40.390663",
|
"modified": "2026-01-22 11:05:25.193024",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Address Company Link",
|
"name": "Address Company Link",
|
||||||
"naming_rule": "",
|
"naming_rule": "",
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ def after_migrate():
|
||||||
for doctype in doctypes_to_refresh:
|
for doctype in doctypes_to_refresh:
|
||||||
frappe.clear_cache(doctype=doctype)
|
frappe.clear_cache(doctype=doctype)
|
||||||
frappe.reload_doctype(doctype)
|
frappe.reload_doctype(doctype)
|
||||||
|
|
||||||
check_and_create_holiday_list()
|
check_and_create_holiday_list()
|
||||||
|
|
||||||
# update_address_fields()
|
# update_address_fields()
|
||||||
|
|
@ -56,7 +56,7 @@ def build_frontend():
|
||||||
frappe.log_error(message="No frontend directory found for custom_ui", title="Frontend Build Skipped")
|
frappe.log_error(message="No frontend directory found for custom_ui", title="Frontend Build Skipped")
|
||||||
print(f"⚠️ Frontend directory does not exist. Skipping build. Path was {frontend_path}")
|
print(f"⚠️ Frontend directory does not exist. Skipping build. Path was {frontend_path}")
|
||||||
return
|
return
|
||||||
|
|
||||||
dist_path = os.path.join(app_root, "custom_ui", "public", "dist")
|
dist_path = os.path.join(app_root, "custom_ui", "public", "dist")
|
||||||
should_build = True
|
should_build = True
|
||||||
|
|
||||||
|
|
@ -69,10 +69,10 @@ def build_frontend():
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
frappe.log_error(message=str(e), title="Frontend Build Failed")
|
frappe.log_error(message=str(e), title="Frontend Build Failed")
|
||||||
print(f"\n❌ Frontend build failed: {e}\n")
|
print(f"\n❌ Frontend build failed: {e}\n")
|
||||||
|
|
||||||
def add_custom_fields():
|
def add_custom_fields():
|
||||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||||
|
|
||||||
print("\n🔧 Adding custom fields to doctypes...")
|
print("\n🔧 Adding custom fields to doctypes...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -85,7 +85,7 @@ def add_custom_fields():
|
||||||
print(" ✅ Added 'Service' to Address address_type options.")
|
print(" ✅ Added 'Service' to Address address_type options.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" ⚠️ Failed to update Address address_type: {e}")
|
print(f" ⚠️ Failed to update Address address_type: {e}")
|
||||||
|
|
||||||
custom_fields = {
|
custom_fields = {
|
||||||
"Customer": [
|
"Customer": [
|
||||||
dict(
|
dict(
|
||||||
|
|
@ -267,7 +267,7 @@ def add_custom_fields():
|
||||||
insert_after="full_address"
|
insert_after="full_address"
|
||||||
),
|
),
|
||||||
dict(
|
dict(
|
||||||
fieldname="longitude",
|
fieldname="longitude",
|
||||||
label="Longitude",
|
label="Longitude",
|
||||||
fieldtype="Float",
|
fieldtype="Float",
|
||||||
precision=8,
|
precision=8,
|
||||||
|
|
@ -277,7 +277,7 @@ def add_custom_fields():
|
||||||
fieldname="onsite_meeting_scheduled",
|
fieldname="onsite_meeting_scheduled",
|
||||||
label="On-Site Meeting Scheduled",
|
label="On-Site Meeting Scheduled",
|
||||||
fieldtype="Select",
|
fieldtype="Select",
|
||||||
options="Not Started\nIn Progress\nCompleted",
|
options="Not Started\nIn Progress\nCompleted",
|
||||||
default="Not Started",
|
default="Not Started",
|
||||||
insert_after="longitude"
|
insert_after="longitude"
|
||||||
),
|
),
|
||||||
|
|
@ -291,7 +291,7 @@ def add_custom_fields():
|
||||||
),
|
),
|
||||||
dict(
|
dict(
|
||||||
fieldname="job_status",
|
fieldname="job_status",
|
||||||
label="Job Status",
|
label="Job Status",
|
||||||
fieldtype="Select",
|
fieldtype="Select",
|
||||||
options="Not Started\nIn Progress\nCompleted",
|
options="Not Started\nIn Progress\nCompleted",
|
||||||
default="Not Started",
|
default="Not Started",
|
||||||
|
|
@ -302,7 +302,7 @@ def add_custom_fields():
|
||||||
label="Payment Received Status",
|
label="Payment Received Status",
|
||||||
fieldtype="Select",
|
fieldtype="Select",
|
||||||
options="Not Started\nIn Progress\nCompleted",
|
options="Not Started\nIn Progress\nCompleted",
|
||||||
default="Not Started",
|
default="Not Started",
|
||||||
insert_after="job_status"
|
insert_after="job_status"
|
||||||
),
|
),
|
||||||
dict(
|
dict(
|
||||||
|
|
@ -588,15 +588,7 @@ def add_custom_fields():
|
||||||
fieldtype="Check",
|
fieldtype="Check",
|
||||||
default=0,
|
default=0,
|
||||||
insert_after="expected_end_time"
|
insert_after="expected_end_time"
|
||||||
),
|
)
|
||||||
dict(
|
|
||||||
fieldname="invoice_status",
|
|
||||||
label="Invoice Status",
|
|
||||||
fieldtype="Select",
|
|
||||||
default="Not Ready",
|
|
||||||
options="Not Ready\nReady to Invoice\nInvoice Created\nInvoice Sent",
|
|
||||||
insert_after="is_scheduled"
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
"Project Template": [
|
"Project Template": [
|
||||||
dict(
|
dict(
|
||||||
|
|
@ -624,15 +616,15 @@ def add_custom_fields():
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
print("🔧 Custom fields to check per doctype:")
|
print("🔧 Custom fields to check per doctype:")
|
||||||
for key, value in custom_fields.items():
|
for key, value in custom_fields.items():
|
||||||
print(f" • {key}: {len(value)} fields")
|
print(f" • {key}: {len(value)} fields")
|
||||||
print(f" Total fields to check: {sum(len(v) for v in custom_fields.values())}\n")
|
print(f" Total fields to check: {sum(len(v) for v in custom_fields.values())}\n")
|
||||||
|
|
||||||
missing_fields = []
|
missing_fields = []
|
||||||
fields_to_update = []
|
fields_to_update = []
|
||||||
|
|
||||||
for doctype, field_options in custom_fields.items():
|
for doctype, field_options in custom_fields.items():
|
||||||
meta = frappe.get_meta(doctype)
|
meta = frappe.get_meta(doctype)
|
||||||
for field_spec in field_options:
|
for field_spec in field_options:
|
||||||
|
|
@ -645,7 +637,7 @@ def add_custom_fields():
|
||||||
if frappe.db.exists("Custom Field", custom_field_name):
|
if frappe.db.exists("Custom Field", custom_field_name):
|
||||||
custom_field_doc = frappe.get_doc("Custom Field", custom_field_name)
|
custom_field_doc = frappe.get_doc("Custom Field", custom_field_name)
|
||||||
needs_update = False
|
needs_update = False
|
||||||
|
|
||||||
# Compare important properties
|
# Compare important properties
|
||||||
for key, desired_value in field_spec.items():
|
for key, desired_value in field_spec.items():
|
||||||
if key == "fieldname":
|
if key == "fieldname":
|
||||||
|
|
@ -654,10 +646,10 @@ def add_custom_fields():
|
||||||
if current_value != desired_value:
|
if current_value != desired_value:
|
||||||
needs_update = True
|
needs_update = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if needs_update:
|
if needs_update:
|
||||||
fields_to_update.append((doctype, fieldname, field_spec))
|
fields_to_update.append((doctype, fieldname, field_spec))
|
||||||
|
|
||||||
if missing_fields:
|
if missing_fields:
|
||||||
print("\n❌ Missing custom fields:")
|
print("\n❌ Missing custom fields:")
|
||||||
for entry in missing_fields:
|
for entry in missing_fields:
|
||||||
|
|
@ -666,36 +658,36 @@ def add_custom_fields():
|
||||||
missing_field_specs = build_missing_field_specs(custom_fields, missing_fields)
|
missing_field_specs = build_missing_field_specs(custom_fields, missing_fields)
|
||||||
create_custom_fields(missing_field_specs)
|
create_custom_fields(missing_field_specs)
|
||||||
print("✅ Missing custom fields created.")
|
print("✅ Missing custom fields created.")
|
||||||
|
|
||||||
if fields_to_update:
|
if fields_to_update:
|
||||||
print("\n🔧 Updating custom fields with mismatched specs:")
|
print("\n🔧 Updating custom fields with mismatched specs:")
|
||||||
for doctype, fieldname, field_spec in fields_to_update:
|
for doctype, fieldname, field_spec in fields_to_update:
|
||||||
print(f" • {doctype}: {fieldname}")
|
print(f" • {doctype}: {fieldname}")
|
||||||
custom_field_name = f"{doctype}-{fieldname}"
|
custom_field_name = f"{doctype}-{fieldname}"
|
||||||
custom_field_doc = frappe.get_doc("Custom Field", custom_field_name)
|
custom_field_doc = frappe.get_doc("Custom Field", custom_field_name)
|
||||||
|
|
||||||
# Update all properties from field_spec
|
# Update all properties from field_spec
|
||||||
for key, value in field_spec.items():
|
for key, value in field_spec.items():
|
||||||
if key != "fieldname":
|
if key != "fieldname":
|
||||||
setattr(custom_field_doc, key, value)
|
setattr(custom_field_doc, key, value)
|
||||||
|
|
||||||
custom_field_doc.save(ignore_permissions=True)
|
custom_field_doc.save(ignore_permissions=True)
|
||||||
|
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
print("✅ Custom fields updated.")
|
print("✅ Custom fields updated.")
|
||||||
|
|
||||||
if not missing_fields and not fields_to_update:
|
if not missing_fields and not fields_to_update:
|
||||||
print("✅ All custom fields verified.")
|
print("✅ All custom fields verified.")
|
||||||
|
|
||||||
|
|
||||||
def update_onsite_meeting_fields():
|
def update_onsite_meeting_fields():
|
||||||
"""Update On-Site Meeting doctype fields to make start_time and end_time optional."""
|
"""Update On-Site Meeting doctype fields to make start_time and end_time optional."""
|
||||||
print("\n🔧 Updating On-Site Meeting doctype fields...")
|
print("\n🔧 Updating On-Site Meeting doctype fields...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get the doctype
|
# Get the doctype
|
||||||
doctype = frappe.get_doc("DocType", "On-Site Meeting")
|
doctype = frappe.get_doc("DocType", "On-Site Meeting")
|
||||||
|
|
||||||
# Find and update start_time and end_time fields
|
# Find and update start_time and end_time fields
|
||||||
updated_fields = []
|
updated_fields = []
|
||||||
for field in doctype.fields:
|
for field in doctype.fields:
|
||||||
|
|
@ -703,20 +695,20 @@ def update_onsite_meeting_fields():
|
||||||
if field.reqd == 1:
|
if field.reqd == 1:
|
||||||
field.reqd = 0
|
field.reqd = 0
|
||||||
updated_fields.append(field.fieldname)
|
updated_fields.append(field.fieldname)
|
||||||
|
|
||||||
if updated_fields:
|
if updated_fields:
|
||||||
# Save the doctype
|
# Save the doctype
|
||||||
doctype.save(ignore_permissions=True)
|
doctype.save(ignore_permissions=True)
|
||||||
print(f"✅ Updated fields: {', '.join(updated_fields)} (set to not required)")
|
print(f"✅ Updated fields: {', '.join(updated_fields)} (set to not required)")
|
||||||
else:
|
else:
|
||||||
print("✅ Fields already configured correctly")
|
print("✅ Fields already configured correctly")
|
||||||
|
|
||||||
print("🔧 On-Site Meeting field update complete.\n")
|
print("🔧 On-Site Meeting field update complete.\n")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Error updating On-Site Meeting fields: {str(e)}")
|
print(f"❌ Error updating On-Site Meeting fields: {str(e)}")
|
||||||
frappe.log_error(message=str(e), title="On-Site Meeting Field Update Failed")
|
frappe.log_error(message=str(e), title="On-Site Meeting Field Update Failed")
|
||||||
# Don't raise - this is not critical enough to stop migration
|
# Don't raise - this is not critical enough to stop migration
|
||||||
|
|
||||||
def update_address_fields():
|
def update_address_fields():
|
||||||
quotations = frappe.get_all("Quotation", pluck="name")
|
quotations = frappe.get_all("Quotation", pluck="name")
|
||||||
addresses = frappe.get_all("Address", pluck="name")
|
addresses = frappe.get_all("Address", pluck="name")
|
||||||
|
|
@ -736,9 +728,9 @@ def update_address_fields():
|
||||||
combined_doctypes.append({"doctype": "Address", "name": address})
|
combined_doctypes.append({"doctype": "Address", "name": address})
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
combined_doctypes.append({"doctype": "Task", "name": task})
|
combined_doctypes.append({"doctype": "Task", "name": task})
|
||||||
|
|
||||||
print(f"\n📍 Updating field values for {total_addresses} addresses, {total_quotations} quotations, {total_sales_orders} sales orders, and {total_tasks} tasks...")
|
print(f"\n📍 Updating field values for {total_addresses} addresses, {total_quotations} quotations, {total_sales_orders} sales orders, and {total_tasks} tasks...")
|
||||||
|
|
||||||
# Field update counters
|
# Field update counters
|
||||||
field_counters = {
|
field_counters = {
|
||||||
'quotation_addresses_updated': 0,
|
'quotation_addresses_updated': 0,
|
||||||
|
|
@ -759,7 +751,7 @@ def update_address_fields():
|
||||||
'contacts_updated': 0,
|
'contacts_updated': 0,
|
||||||
'tasks_updated': 0
|
'tasks_updated': 0
|
||||||
}
|
}
|
||||||
|
|
||||||
onsite_meta = frappe.get_meta("On-Site Meeting")
|
onsite_meta = frappe.get_meta("On-Site Meeting")
|
||||||
onsite_status_field = "custom_status" if onsite_meta.has_field("custom_status") else "status"
|
onsite_status_field = "custom_status" if onsite_meta.has_field("custom_status") else "status"
|
||||||
|
|
||||||
|
|
@ -769,7 +761,7 @@ def update_address_fields():
|
||||||
bar_length = 30
|
bar_length = 30
|
||||||
filled_length = int(bar_length * index // total_doctypes)
|
filled_length = int(bar_length * index // total_doctypes)
|
||||||
bar = '█' * filled_length + '░' * (bar_length - filled_length)
|
bar = '█' * filled_length + '░' * (bar_length - filled_length)
|
||||||
|
|
||||||
# Print a three-line, refreshing progress block without adding new lines each loop
|
# Print a three-line, refreshing progress block without adding new lines each loop
|
||||||
progress_line = f"📊 Progress: [{bar}] {progress_percentage:3d}% ({index}/{total_doctypes})"
|
progress_line = f"📊 Progress: [{bar}] {progress_percentage:3d}% ({index}/{total_doctypes})"
|
||||||
counters_line = f" Fields updated: {field_counters['total_field_updates']} | DocTypes updated: {field_counters['addresses_updated'] + field_counters['quotations_updated'] + field_counters['sales_orders_updated'] + field_counters['customers_updated']}"
|
counters_line = f" Fields updated: {field_counters['total_field_updates']} | DocTypes updated: {field_counters['addresses_updated'] + field_counters['quotations_updated'] + field_counters['sales_orders_updated'] + field_counters['customers_updated']}"
|
||||||
|
|
@ -800,7 +792,7 @@ def update_address_fields():
|
||||||
custom_installation_address = getattr(quotation_doc, 'custom_installation_address', None)
|
custom_installation_address = getattr(quotation_doc, 'custom_installation_address', None)
|
||||||
custom_job_address = getattr(quotation_doc, 'custom_job_address', None)
|
custom_job_address = getattr(quotation_doc, 'custom_job_address', None)
|
||||||
custom_project_template = getattr(quotation_doc, 'custom_project_template', None)
|
custom_project_template = getattr(quotation_doc, 'custom_project_template', None)
|
||||||
|
|
||||||
updates = {}
|
updates = {}
|
||||||
if custom_installation_address and not custom_job_address:
|
if custom_installation_address and not custom_job_address:
|
||||||
updates['custom_job_address'] = custom_installation_address
|
updates['custom_job_address'] = custom_installation_address
|
||||||
|
|
@ -810,15 +802,15 @@ def update_address_fields():
|
||||||
updates['custom_project_template'] = "SNW Install"
|
updates['custom_project_template'] = "SNW Install"
|
||||||
field_counters[f"{dict_field}_project_templates_updated"] += 1
|
field_counters[f"{dict_field}_project_templates_updated"] += 1
|
||||||
field_counters['total_field_updates'] += 1
|
field_counters['total_field_updates'] += 1
|
||||||
|
|
||||||
if updates:
|
if updates:
|
||||||
frappe.db.set_value(doc['doctype'], doc['name'], updates)
|
frappe.db.set_value(doc['doctype'], doc['name'], updates)
|
||||||
field_counters[f"{dict_field}s_updated"] += 1
|
field_counters[f"{dict_field}s_updated"] += 1
|
||||||
|
|
||||||
if doc['doctype'] == "Address":
|
if doc['doctype'] == "Address":
|
||||||
address_doc = frappe.get_doc("Address", doc['name'])
|
address_doc = frappe.get_doc("Address", doc['name'])
|
||||||
updates = {}
|
updates = {}
|
||||||
|
|
||||||
# Use getattr with default values instead of direct attribute access
|
# Use getattr with default values instead of direct attribute access
|
||||||
if not getattr(address_doc, 'full_address', None):
|
if not getattr(address_doc, 'full_address', None):
|
||||||
address_parts_1 = [
|
address_parts_1 = [
|
||||||
|
|
@ -830,57 +822,57 @@ def update_address_fields():
|
||||||
address_doc.state or "",
|
address_doc.state or "",
|
||||||
address_doc.pincode or "",
|
address_doc.pincode or "",
|
||||||
]
|
]
|
||||||
|
|
||||||
full_address = ", ".join([
|
full_address = ", ".join([
|
||||||
" ".join(filter(None, address_parts_1)),
|
" ".join(filter(None, address_parts_1)),
|
||||||
" ".join(filter(None, address_parts_2))
|
" ".join(filter(None, address_parts_2))
|
||||||
]).strip()
|
]).strip()
|
||||||
updates['full_address'] = full_address
|
updates['full_address'] = full_address
|
||||||
field_counters['full_address'] += 1
|
field_counters['full_address'] += 1
|
||||||
field_counters['total_field_updates'] += 1
|
field_counters['total_field_updates'] += 1
|
||||||
|
|
||||||
onsite_meeting = "Not Started"
|
onsite_meeting = "Not Started"
|
||||||
estimate_sent = "Not Started"
|
estimate_sent = "Not Started"
|
||||||
job_status = "Not Started"
|
job_status = "Not Started"
|
||||||
payment_received = "Not Started"
|
payment_received = "Not Started"
|
||||||
|
|
||||||
|
|
||||||
onsite_meetings = frappe.get_all("On-Site Meeting", fields=[onsite_status_field], filters={"address": address_doc.address_title})
|
onsite_meetings = frappe.get_all("On-Site Meeting", fields=[onsite_status_field], filters={"address": address_doc.address_title})
|
||||||
if onsite_meetings and onsite_meetings[0]:
|
if onsite_meetings and onsite_meetings[0]:
|
||||||
status_value = onsite_meetings[0].get(onsite_status_field)
|
status_value = onsite_meetings[0].get(onsite_status_field)
|
||||||
onsite_meeting = "Completed" if status_value == "Completed" else "In Progress"
|
onsite_meeting = "Completed" if status_value == "Completed" else "In Progress"
|
||||||
|
|
||||||
estimates = frappe.get_all("Quotation", fields=["custom_sent", "docstatus", "custom_response"], filters={"custom_job_address": address_doc.address_title})
|
estimates = frappe.get_all("Quotation", fields=["custom_sent", "docstatus", "custom_response"], filters={"custom_job_address": address_doc.address_title})
|
||||||
if estimates and estimates[0] and estimates[0]["custom_sent"] == 1 and estimates[0]["custom_response"]:
|
if estimates and estimates[0] and estimates[0]["custom_sent"] == 1 and estimates[0]["custom_response"]:
|
||||||
estimate_sent = "Completed"
|
estimate_sent = "Completed"
|
||||||
elif estimates and estimates[0] and not (estimates[0]["custom_sent"] == 1 and estimates[0]["custom_response"]):
|
elif estimates and estimates[0] and not (estimates[0]["custom_sent"] == 1 and estimates[0]["custom_response"]):
|
||||||
estimate_sent = "In Progress"
|
estimate_sent = "In Progress"
|
||||||
|
|
||||||
jobs = frappe.get_all("Project", fields=["status"], filters={"custom_installation_address": address_doc.address_title, "project_template": "SNW Install"})
|
jobs = frappe.get_all("Project", fields=["status"], filters={"custom_installation_address": address_doc.address_title, "project_template": "SNW Install"})
|
||||||
if jobs and jobs[0] and jobs[0]["status"] == "Completed":
|
if jobs and jobs[0] and jobs[0]["status"] == "Completed":
|
||||||
job_status = "Completed"
|
job_status = "Completed"
|
||||||
elif jobs and jobs[0]:
|
elif jobs and jobs[0]:
|
||||||
job_status = "In Progress"
|
job_status = "In Progress"
|
||||||
|
|
||||||
sales_invoices = frappe.get_all("Sales Invoice", fields=["outstanding_amount"], filters={"custom_installation_address": address_doc.address_title})
|
sales_invoices = frappe.get_all("Sales Invoice", fields=["outstanding_amount"], filters={"custom_installation_address": address_doc.address_title})
|
||||||
# payments = frappe.get_all("Payment Entry", filters={"custom_installation_address": address_doc.address_title})
|
# payments = frappe.get_all("Payment Entry", filters={"custom_installation_address": address_doc.address_title})
|
||||||
if sales_invoices and sales_invoices[0] and sales_invoices[0]["outstanding_amount"] == 0:
|
if sales_invoices and sales_invoices[0] and sales_invoices[0]["outstanding_amount"] == 0:
|
||||||
payment_received = "Completed"
|
payment_received = "Completed"
|
||||||
elif sales_invoices and sales_invoices[0]:
|
elif sales_invoices and sales_invoices[0]:
|
||||||
payment_received = "In Progress"
|
payment_received = "In Progress"
|
||||||
|
|
||||||
customer_name = getattr(address_doc, 'custom_customer_to_bill', None)
|
customer_name = getattr(address_doc, 'custom_customer_to_bill', None)
|
||||||
links = address_doc.get("links", [])
|
links = address_doc.get("links", [])
|
||||||
customer_links = [link for link in links if link.link_doctype == "Customer"]
|
customer_links = [link for link in links if link.link_doctype == "Customer"]
|
||||||
needs_link_update = False
|
needs_link_update = False
|
||||||
|
|
||||||
if customer_name and frappe.db.exists("Customer", customer_name):
|
if customer_name and frappe.db.exists("Customer", customer_name):
|
||||||
customer_doc = frappe.get_doc("Customer", customer_name)
|
customer_doc = frappe.get_doc("Customer", customer_name)
|
||||||
|
|
||||||
# Check if address needs link update
|
# Check if address needs link update
|
||||||
if not customer_links:
|
if not customer_links:
|
||||||
needs_link_update = True
|
needs_link_update = True
|
||||||
|
|
||||||
if not address_doc.name in [link.address_name for link in customer_doc.get("custom_select_address", [])]:
|
if not address_doc.name in [link.address_name for link in customer_doc.get("custom_select_address", [])]:
|
||||||
customer_doc.append("custom_select_address", {
|
customer_doc.append("custom_select_address", {
|
||||||
"address_name": address_doc.name
|
"address_name": address_doc.name
|
||||||
|
|
@ -888,7 +880,7 @@ def update_address_fields():
|
||||||
customer_doc.save(ignore_permissions=True)
|
customer_doc.save(ignore_permissions=True)
|
||||||
field_counters['customers_updated'] += 1
|
field_counters['customers_updated'] += 1
|
||||||
field_counters['total_field_updates'] += 1
|
field_counters['total_field_updates'] += 1
|
||||||
|
|
||||||
if getattr(address_doc, 'custom_onsite_meeting_scheduled', None) != onsite_meeting:
|
if getattr(address_doc, 'custom_onsite_meeting_scheduled', None) != onsite_meeting:
|
||||||
updates['custom_onsite_meeting_scheduled'] = onsite_meeting
|
updates['custom_onsite_meeting_scheduled'] = onsite_meeting
|
||||||
field_counters['custom_onsite_meeting_scheduled'] += 1
|
field_counters['custom_onsite_meeting_scheduled'] += 1
|
||||||
|
|
@ -905,11 +897,11 @@ def update_address_fields():
|
||||||
updates['custom_payment_received_status'] = payment_received
|
updates['custom_payment_received_status'] = payment_received
|
||||||
field_counters['custom_payment_received_status'] += 1
|
field_counters['custom_payment_received_status'] += 1
|
||||||
field_counters['total_field_updates'] += 1
|
field_counters['total_field_updates'] += 1
|
||||||
|
|
||||||
if updates:
|
if updates:
|
||||||
frappe.db.set_value("Address", doc['name'], updates)
|
frappe.db.set_value("Address", doc['name'], updates)
|
||||||
field_counters['addresses_updated'] += 1
|
field_counters['addresses_updated'] += 1
|
||||||
|
|
||||||
# Handle address links after db.set_value to avoid timestamp mismatch
|
# Handle address links after db.set_value to avoid timestamp mismatch
|
||||||
if needs_link_update:
|
if needs_link_update:
|
||||||
# Reload the document to get the latest version
|
# Reload the document to get the latest version
|
||||||
|
|
@ -930,13 +922,13 @@ def update_address_fields():
|
||||||
frappe.db.set_value("Task", doc["name"], "custom_property", project_address if project_address else alt_project_address)
|
frappe.db.set_value("Task", doc["name"], "custom_property", project_address if project_address else alt_project_address)
|
||||||
field_counters['tasks_updated'] += 1
|
field_counters['tasks_updated'] += 1
|
||||||
field_counters['total_field_updates'] += 1
|
field_counters['total_field_updates'] += 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Commit every 100 records to avoid long transactions
|
# Commit every 100 records to avoid long transactions
|
||||||
if index % 100 == 0:
|
if index % 100 == 0:
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
|
|
||||||
# Print completion summary
|
# Print completion summary
|
||||||
print(f"\n\n✅ DocType field value update completed!")
|
print(f"\n\n✅ DocType field value update completed!")
|
||||||
print(f"📊 Summary:")
|
print(f"📊 Summary:")
|
||||||
|
|
@ -958,7 +950,7 @@ def update_address_fields():
|
||||||
print(f" • Sales Order Addresses Updated: {field_counters['sales_order_addresses_updated']:,}")
|
print(f" • Sales Order Addresses Updated: {field_counters['sales_order_addresses_updated']:,}")
|
||||||
print(f" • Sales Order Project Templates Updated: {field_counters['sales_order_project_templates_updated']:,}")
|
print(f" • Sales Order Project Templates Updated: {field_counters['sales_order_project_templates_updated']:,}")
|
||||||
print("📍 DocType field value updates complete.\n")
|
print("📍 DocType field value updates complete.\n")
|
||||||
|
|
||||||
def build_missing_field_specs(custom_fields, missing_fields):
|
def build_missing_field_specs(custom_fields, missing_fields):
|
||||||
missing_field_specs = {}
|
missing_field_specs = {}
|
||||||
for entry in missing_fields:
|
for entry in missing_fields:
|
||||||
|
|
@ -968,14 +960,14 @@ def build_missing_field_specs(custom_fields, missing_fields):
|
||||||
if field_spec["fieldname"] == fieldname:
|
if field_spec["fieldname"] == fieldname:
|
||||||
missing_field_specs[doctype].append(field_spec)
|
missing_field_specs[doctype].append(field_spec)
|
||||||
break
|
break
|
||||||
|
|
||||||
return missing_field_specs
|
return missing_field_specs
|
||||||
|
|
||||||
def check_and_create_holiday_list(year=2026, country="US", weekly_off="Sunday"):
|
def check_and_create_holiday_list(year=2026, country="US", weekly_off="Sunday"):
|
||||||
"""Check if Holiday List for the given year exists, if not create it."""
|
"""Check if Holiday List for the given year exists, if not create it."""
|
||||||
print(f"\n🔧 Checking for Holiday List for {country} in {year}...")
|
print(f"\n🔧 Checking for Holiday List for {country} in {year}...")
|
||||||
holiday_list_name = f"{country} Holidays {year}"
|
holiday_list_name = f"{country} Holidays {year}"
|
||||||
|
|
||||||
if frappe.db.exists("Holiday List", holiday_list_name):
|
if frappe.db.exists("Holiday List", holiday_list_name):
|
||||||
print(f"✅ Holiday List '{holiday_list_name}' already exists.")
|
print(f"✅ Holiday List '{holiday_list_name}' already exists.")
|
||||||
return
|
return
|
||||||
|
|
@ -1007,16 +999,16 @@ def check_and_create_holiday_list(year=2026, country="US", weekly_off="Sunday"):
|
||||||
# hl.make_holiday_entries()
|
# hl.make_holiday_entries()
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
print(f"✅ Holiday List '{holiday_list_name}' created successfully.")
|
print(f"✅ Holiday List '{holiday_list_name}' created successfully.")
|
||||||
|
|
||||||
def get_all_sundays(year):
|
def get_all_sundays(year):
|
||||||
sundays = []
|
sundays = []
|
||||||
d = date(year, 1, 1)
|
d = date(year, 1, 1)
|
||||||
|
|
||||||
while d.weekday() != 6:
|
while d.weekday() != 6:
|
||||||
d += timedelta(days=1)
|
d += timedelta(days=1)
|
||||||
|
|
||||||
while d.year == year:
|
while d.year == year:
|
||||||
sundays.append(d)
|
sundays.append(d)
|
||||||
d += timedelta(days=7)
|
d += timedelta(days=7)
|
||||||
|
|
||||||
return sundays
|
return sundays
|
||||||
|
|
@ -5,8 +5,6 @@ import { useErrorStore } from "./stores/errors";
|
||||||
const ZIPPOPOTAMUS_BASE_URL = "https://api.zippopotam.us/us";
|
const ZIPPOPOTAMUS_BASE_URL = "https://api.zippopotam.us/us";
|
||||||
// Proxy method for external API calls
|
// Proxy method for external API calls
|
||||||
const FRAPPE_PROXY_METHOD = "custom_ui.api.proxy.request";
|
const FRAPPE_PROXY_METHOD = "custom_ui.api.proxy.request";
|
||||||
// On-Site Meeting methods
|
|
||||||
const FRAPPE_GET_INCOMPLETE_BIDS_METHOD = "custom_ui.api.db.on_site_meetings.get_incomplete_bids";
|
|
||||||
// Estimate methods
|
// Estimate methods
|
||||||
const FRAPPE_UPSERT_ESTIMATE_METHOD = "custom_ui.api.db.estimates.upsert_estimate";
|
const FRAPPE_UPSERT_ESTIMATE_METHOD = "custom_ui.api.db.estimates.upsert_estimate";
|
||||||
const FRAPPE_GET_ESTIMATES_METHOD = "custom_ui.api.db.estimates.get_estimate_table_data";
|
const FRAPPE_GET_ESTIMATES_METHOD = "custom_ui.api.db.estimates.get_estimate_table_data";
|
||||||
|
|
@ -16,7 +14,7 @@ const FRAPPE_LOCK_ESTIMATE_METHOD = "custom_ui.api.db.estimates.lock_estimate";
|
||||||
const FRAPPE_ESTIMATE_UPDATE_RESPONSE_METHOD = "custom_ui.api.db.estimates.manual_response";
|
const FRAPPE_ESTIMATE_UPDATE_RESPONSE_METHOD = "custom_ui.api.db.estimates.manual_response";
|
||||||
const FRAPPE_GET_ESTIMATE_TEMPLATES_METHOD = "custom_ui.api.db.estimates.get_estimate_templates";
|
const FRAPPE_GET_ESTIMATE_TEMPLATES_METHOD = "custom_ui.api.db.estimates.get_estimate_templates";
|
||||||
const FRAPPE_CREATE_ESTIMATE_TEMPLATE_METHOD = "custom_ui.api.db.estimates.create_estimate_template";
|
const FRAPPE_CREATE_ESTIMATE_TEMPLATE_METHOD = "custom_ui.api.db.estimates.create_estimate_template";
|
||||||
const FRAPPE_GET_UNAPPROVED_ESTIMATES_COUNT_METHOD = "custom_ui.api.db.estimates.get_unapproved_estimates_count";
|
const FRAPPE_GET_UNAPPROVED_ESTIMATES_COUNT_METHOD = "custom_ui.api.db.estimates.get_unnaproved_estimates_count";
|
||||||
const FRAPPE_GET_ESTIMATES_HALF_DOWN_COUNT_METHOD = "custom_ui.api.db.estimates.get_estimates_half_down_count";
|
const FRAPPE_GET_ESTIMATES_HALF_DOWN_COUNT_METHOD = "custom_ui.api.db.estimates.get_estimates_half_down_count";
|
||||||
// Job methods
|
// Job methods
|
||||||
const FRAPPE_GET_JOB_METHOD = "custom_ui.api.db.jobs.get_job";
|
const FRAPPE_GET_JOB_METHOD = "custom_ui.api.db.jobs.get_job";
|
||||||
|
|
@ -28,10 +26,6 @@ const FRAPPE_GET_INSTALL_PROJECTS_METHOD = "custom_ui.api.db.jobs.get_install_pr
|
||||||
const FRAPPE_GET_JOBS_FOR_CALENDAR_METHOD = "custom_ui.api.db.jobs.get_projects_for_calendar";
|
const FRAPPE_GET_JOBS_FOR_CALENDAR_METHOD = "custom_ui.api.db.jobs.get_projects_for_calendar";
|
||||||
const FRAPPE_GET_JOB_TEMPLATES_METHOD = "custom_ui.api.db.jobs.get_job_templates";
|
const FRAPPE_GET_JOB_TEMPLATES_METHOD = "custom_ui.api.db.jobs.get_job_templates";
|
||||||
const FRAPPE_UPDATE_JOB_SCHEDULED_DATES_METHOD = "custom_ui.api.db.jobs.update_job_scheduled_dates";
|
const FRAPPE_UPDATE_JOB_SCHEDULED_DATES_METHOD = "custom_ui.api.db.jobs.update_job_scheduled_dates";
|
||||||
const FRAPPE_GET_JOBS_IN_QUEUE_METHOD = "custom_ui.api.db.jobs.get_jobs_in_queue_count";
|
|
||||||
const FRAPPE_GET_JOBS_IN_PROGRESS_METHOD = "custom_ui.api.db.jobs.get_jobs_in_progress_count";
|
|
||||||
const FRAPPE_GET_JOBS_LATE_METHOD = "custom_ui.api.db.jobs.get_jobs_late_count";
|
|
||||||
const FRAPPE_GET_JOBS_TO_INVOICE_METHOD = "custom_ui.api.db.jobs.get_jobs_to_invoice_count";
|
|
||||||
// Task methods
|
// Task methods
|
||||||
const FRAPPE_GET_TASKS_METHOD = "custom_ui.api.db.tasks.get_tasks_table_data";
|
const FRAPPE_GET_TASKS_METHOD = "custom_ui.api.db.tasks.get_tasks_table_data";
|
||||||
const FRAPPE_GET_TASKS_STATUS_OPTIONS = "custom_ui.api.db.tasks.get_task_status_options";
|
const FRAPPE_GET_TASKS_STATUS_OPTIONS = "custom_ui.api.db.tasks.get_task_status_options";
|
||||||
|
|
@ -40,8 +34,6 @@ const FRAPPE_GET_TASKS_DUE_METHOD = "custom_ui.api.db.tasks.get_tasks_due";
|
||||||
// Invoice methods
|
// Invoice methods
|
||||||
const FRAPPE_GET_INVOICES_METHOD = "custom_ui.api.db.invoices.get_invoice_table_data";
|
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";
|
const FRAPPE_UPSERT_INVOICE_METHOD = "custom_ui.api.db.invoices.upsert_invoice";
|
||||||
const FRAPPE_GET_INVOICES_LATE_METHOD = "custom_ui.api.db.invoices.get_invoices_late_count";
|
|
||||||
const FRAPPE_CREATE_INVOICE_FOR_JOB = "custom_ui.api.db.invoices.create_invoice_for_job";
|
|
||||||
// Warranty methods
|
// Warranty methods
|
||||||
const FRAPPE_GET_WARRANTY_CLAIMS_METHOD = "custom_ui.api.db.warranties.get_warranty_claims";
|
const FRAPPE_GET_WARRANTY_CLAIMS_METHOD = "custom_ui.api.db.warranties.get_warranty_claims";
|
||||||
// On-Site Meeting methods
|
// On-Site Meeting methods
|
||||||
|
|
@ -248,11 +240,6 @@ class Api {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getIncompleteBidsCount(currentCompany) {
|
|
||||||
const result = await this.request(FRAPPE_GET_INCOMPLETE_BIDS_METHOD, { company: currentCompany });
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async createEstimate(estimateData) {
|
static async createEstimate(estimateData) {
|
||||||
const result = await this.request(FRAPPE_UPSERT_ESTIMATE_METHOD, { data: estimateData });
|
const result = await this.request(FRAPPE_UPSERT_ESTIMATE_METHOD, { data: estimateData });
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -278,12 +265,12 @@ class Api {
|
||||||
return await this.request(FRAPPE_CREATE_ESTIMATE_TEMPLATE_METHOD, { data });
|
return await this.request(FRAPPE_CREATE_ESTIMATE_TEMPLATE_METHOD, { data });
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getUnapprovedEstimatesCount(currentCompany) {
|
static async getUnapprovedEstimatesCount() {
|
||||||
return await this.request(FRAPPE_GET_UNAPPROVED_ESTIMATES_COUNT_METHOD, {company: currentCompany});
|
return await this.request(FRAPPE_GET_UNAPPROVED_ESTIMATES_COUNT_METHOD, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getEstimatesHalfDownCount(currentCompany) {
|
static async getEstimatesHalfDownCount() {
|
||||||
return await this.request(FRAPPE_GET_ESTIMATES_HALF_DOWN_COUNT_METHOD, {company: currentCompany});
|
return await this.request(FRAPPE_GET_ESTIMATES_HALF_DOWN_COUNT_METHOD, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -355,26 +342,6 @@ class Api {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getJobsInQueueCount(currentCompany) {
|
|
||||||
return await this.request(FRAPPE_GET_JOBS_IN_QUEUE_METHOD, {company: currentCompany});
|
|
||||||
}
|
|
||||||
|
|
||||||
static async getJobsInProgressCount(currentCompany) {
|
|
||||||
return await this.request(FRAPPE_GET_JOBS_IN_PROGRESS_METHOD, {company: currentCompany});
|
|
||||||
}
|
|
||||||
|
|
||||||
static async getJobsLateCount(currentCompany) {
|
|
||||||
return await this.request(FRAPPE_GET_JOBS_LATE_METHOD, {company: currentCompany});
|
|
||||||
}
|
|
||||||
|
|
||||||
static async getJobsToInvoiceCount(currentCompany) {
|
|
||||||
return await this.request(FRAPPE_GET_JOBS_TO_INVOICE_METHOD, {company: currentCompany});
|
|
||||||
}
|
|
||||||
|
|
||||||
static async setJobCompleted(jobName) {
|
|
||||||
return await this.request(FRAPPE_SET_JOB_COMPLETE_METHOD, {jobName: jobName});
|
|
||||||
}
|
|
||||||
|
|
||||||
static async getJob(jobName) {
|
static async getJob(jobName) {
|
||||||
if (frappe.db.exists("Project", jobName)) {
|
if (frappe.db.exists("Project", jobName)) {
|
||||||
const result = await this.request(FRAPPE_GET_JOB_METHOD, { jobId: jobName })
|
const result = await this.request(FRAPPE_GET_JOB_METHOD, { jobId: jobName })
|
||||||
|
|
@ -524,16 +491,6 @@ class Api {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getInvoicesLateCount(currentCompany) {
|
|
||||||
const result = await this.request(FRAPPE_GET_INVOICES_LATE_METHOD, { company: currentCompany });
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async createInvoiceForJob(jobName) {
|
|
||||||
const result = await this.request(FRAPPE_CREATE_INVOICE_FOR_JOB, { jobName: jobName });
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// WARRANTY METHODS
|
// WARRANTY METHODS
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,9 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Constants
|
||||||
|
//const categories = ["To-do", "Completed"];
|
||||||
|
|
||||||
//Reactive data
|
//Reactive data
|
||||||
const centerData = ref(null);
|
const centerData = ref(null);
|
||||||
const hoveredSegment = ref(null);
|
const hoveredSegment = ref(null);
|
||||||
|
|
@ -110,7 +113,7 @@ const updateCenterData = () => {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
centerData.value = {
|
centerData.value = {
|
||||||
label: props.categories.labels[0],
|
label: "To-do",
|
||||||
value: todos,
|
value: todos,
|
||||||
percentage: null,
|
percentage: null,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
<TodoChart
|
<TodoChart
|
||||||
title="Incomplete Bids"
|
title="Incomplete Bids"
|
||||||
:categories="chartData.bids"
|
:todoNumber="bidsTodoNumber"
|
||||||
|
:completedNumber="bidsCompletedNumber"
|
||||||
>
|
>
|
||||||
</TodoChart>
|
</TodoChart>
|
||||||
<button class="sidebar-button"
|
<button class="sidebar-button"
|
||||||
|
|
@ -37,7 +38,8 @@
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
<TodoChart
|
<TodoChart
|
||||||
title="Unapproved Estimates"
|
title="Unapproved Estimates"
|
||||||
:categories="chartData.estimates"
|
:todoNumber="estimatesTodoNumber"
|
||||||
|
:completedNumber="estimatesCompletedNumber"
|
||||||
>
|
>
|
||||||
</TodoChart>
|
</TodoChart>
|
||||||
<button class="sidebar-button"
|
<button class="sidebar-button"
|
||||||
|
|
@ -59,7 +61,8 @@
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
<TodoChart
|
<TodoChart
|
||||||
title="Half Down Payments"
|
title="Half Down Payments"
|
||||||
:categories="chartData.halfDown"
|
:todoNumber="halfDownTodoNumber"
|
||||||
|
:completedNumber="halfDownCompletedNumber"
|
||||||
>
|
>
|
||||||
</TodoChart>
|
</TodoChart>
|
||||||
<button class="sidebar-button"
|
<button class="sidebar-button"
|
||||||
|
|
@ -81,7 +84,8 @@
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
<TodoChart
|
<TodoChart
|
||||||
title="Jobs In Queue"
|
title="Jobs In Queue"
|
||||||
:categories="chartData.jobsInQueue"
|
:todoNumber="jobQueueTodoNumber"
|
||||||
|
:completedNumber="jobQueueCompletedNumber"
|
||||||
>
|
>
|
||||||
</TodoChart>
|
</TodoChart>
|
||||||
<button class="sidebar-button"
|
<button class="sidebar-button"
|
||||||
|
|
@ -128,30 +132,22 @@ import Card from "../common/Card.vue";
|
||||||
import DataTable from "../common/DataTable.vue";
|
import DataTable from "../common/DataTable.vue";
|
||||||
import TodoChart from "../common/TodoChart.vue";
|
import TodoChart from "../common/TodoChart.vue";
|
||||||
import { Edit, ChatBubbleQuestion, CreditCard, Hammer } from "@iconoir/vue";
|
import { Edit, ChatBubbleQuestion, CreditCard, Hammer } from "@iconoir/vue";
|
||||||
import { ref, onMounted, watch } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import Api from "../../api";
|
import Api from "../../api";
|
||||||
import { useLoadingStore } from "../../stores/loading";
|
import { useLoadingStore } from "../../stores/loading";
|
||||||
import { usePaginationStore } from "../../stores/pagination";
|
import { usePaginationStore } from "../../stores/pagination";
|
||||||
import { useFiltersStore } from "../../stores/filters";
|
import { useFiltersStore } from "../../stores/filters";
|
||||||
import { useCompanyStore } from "../../stores/company.js";
|
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
const loadingStore = useLoadingStore();
|
const loadingStore = useLoadingStore();
|
||||||
const paginationStore = usePaginationStore();
|
const paginationStore = usePaginationStore();
|
||||||
const filtersStore = useFiltersStore();
|
const filtersStore = useFiltersStore();
|
||||||
const companyStore = useCompanyStore();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const tableData = ref([]);
|
const tableData = ref([]);
|
||||||
const totalRecords = ref(0);
|
const totalRecords = ref(0);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const showSubmitEstimateModal = ref(true);
|
const showSubmitEstimateModal = ref(true);
|
||||||
const chartData = ref({
|
|
||||||
bids: {labels: ["Unscheduled"], data: [0], colors: ['red']},
|
|
||||||
estimates: {labels: ["Draft", "Submitted"], data: [0, 0], colors: ['orange', 'blue']},
|
|
||||||
halfDown: {labels: ["Unpaid"], data: [0], colors: ['red']},
|
|
||||||
jobsInQueue: {labels: ["Queued"], data: [0], colors: ['blue']}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Junk
|
//Junk
|
||||||
const filteredItems= []
|
const filteredItems= []
|
||||||
|
|
@ -281,13 +277,6 @@ const handleLazyLoad = async (event) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadChartData = async () => {
|
|
||||||
chartData.value.bids.data = await Api.getIncompleteBidsCount(companyStore.currentCompany);
|
|
||||||
chartData.value.estimates.data = await Api.getUnapprovedEstimatesCount(companyStore.currentCompany);
|
|
||||||
chartData.value.halfDown.data = await Api.getEstimatesHalfDownCount(companyStore.currentCompany);
|
|
||||||
chartData.value.jobsInQueue.data = await Api.getJobsInQueueCount(companyStore.currentCompany);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load initial data
|
// Load initial data
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// Initialize pagination and filters
|
// Initialize pagination and filters
|
||||||
|
|
@ -308,13 +297,6 @@ onMounted(async () => {
|
||||||
sortOrder: initialSorting.order || initialPagination.sortOrder,
|
sortOrder: initialSorting.order || initialPagination.sortOrder,
|
||||||
filters: initialFilters,
|
filters: initialFilters,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Chart Data
|
|
||||||
await loadChartData();
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(() => companyStore.currentCompany, async (newCompany, oldCompany) => {
|
|
||||||
await loadChartData();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -350,14 +350,14 @@ const chartData = ref({
|
||||||
hydroseed: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
hydroseed: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
||||||
permitFinalizations: {labels: ["Todo", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
permitFinalizations: {labels: ["Todo", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
||||||
warranties: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
warranties: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
||||||
|
bids: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
||||||
|
estimates: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
||||||
|
halfDown: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
||||||
fifteenDayFollowups: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
fifteenDayFollowups: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
||||||
lateBalances: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
lateBalances: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
||||||
backflows: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
backflows: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
||||||
machines: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
machines: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
||||||
deliveries: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
deliveries: {labels: ["To-do", "Completed", "Overdue"], data: [0, 0, 0], colors: defaultColors},
|
||||||
bids: {labels: ["Unscheduled"], data: [0], colors: ['red']},
|
|
||||||
estimates: {labels: ["Draft", "Submitted"], data: [0, 0], colors: ['orange', 'blue']},
|
|
||||||
halfDown: {labels: ["Unpaid"], data: [0], colors: ['red']},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const notifications = useNotificationStore();
|
const notifications = useNotificationStore();
|
||||||
|
|
@ -376,17 +376,11 @@ const navigateTo = (path) => {
|
||||||
|
|
||||||
const loadChartData = async() => {
|
const loadChartData = async() => {
|
||||||
chartData.value.locates.data = await Api.getTasksDue("Locate", companyStore.currentCompany);
|
chartData.value.locates.data = await Api.getTasksDue("Locate", companyStore.currentCompany);
|
||||||
chartData.value.permits.data = await Api.getTasksDue("Permit(s)", companyStore.currentCompany);
|
chartData.value.permits.data = await Api.getTasksDue("Permit", companyStore.currentCompany);
|
||||||
chartData.value.curbing.data = await Api.getTasksDue("Curbing", companyStore.currentCompany);
|
chartData.value.curbing.data = await Api.getTasksDue("Curbing", companyStore.currentCompany);
|
||||||
chartData.value.hydroseed.data = await Api.getTasksDue("Hydroseed", companyStore.currentCompany);
|
chartData.value.hydroseed.data = await Api.getTasksDue("Hydroseed", companyStore.currentCompany);
|
||||||
chartData.value.permitFinalizations.data = await Api.getTasksDue("Permit Close-out", companyStore.currentCompany);
|
|
||||||
chartData.value.warranties.data = await Api.getTasksDue("Warranty", companyStore.currentCompany);
|
|
||||||
chartData.value.fifteenDayFollowups.data = await Api.getTasksDue("15-Day QA", companyStore.currentCompany);
|
|
||||||
chartData.value.backflows.data = await Api.getTasksDue("Backflow", companyStore.currentCompany);
|
|
||||||
//Uncomment below when we can check if half-down payments have/can been paid
|
//Uncomment below when we can check if half-down payments have/can been paid
|
||||||
//chartData.value.estimates.data = await Api.getEstimatesHalfDownCount();
|
//chartData.value.estimates.data = await Api.getEstimatesHalfDownCount();
|
||||||
chartData.value.bids.data = await Api.getIncompleteBidsCount(companyStore.currentCompany);
|
|
||||||
chartData.value.estimates.data = await Api.getUnapprovedEstimatesCount(companyStore.currentCompany);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async() => {
|
onMounted(async() => {
|
||||||
|
|
@ -395,6 +389,7 @@ onMounted(async() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(() => companyStore.currentCompany, async (newCompany, oldCompany) => {
|
watch(() => companyStore.currentCompany, async (newCompany, oldCompany) => {
|
||||||
|
console.log("Wathcing for new Company");
|
||||||
await loadChartData();
|
await loadChartData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
<TodoChart
|
<TodoChart
|
||||||
title="Ready To Invoice"
|
title="Ready To Invoice"
|
||||||
:categories="chartData.jobsToInvoice"
|
:todoNumber="invoiceTodoNumber"
|
||||||
|
:completedNumber="invoiceCompletedNumber"
|
||||||
>
|
>
|
||||||
</TodoChart>
|
</TodoChart>
|
||||||
<button class="sidebar-button"
|
<button class="sidebar-button"
|
||||||
|
|
@ -37,7 +38,8 @@
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
<TodoChart
|
<TodoChart
|
||||||
title="Late Balances"
|
title="Late Balances"
|
||||||
:categories="chartData.invoicesLate"
|
:todoNumber="balancesTodoNumber"
|
||||||
|
:completedNumber="balancesCompletedNumber"
|
||||||
>
|
>
|
||||||
</TodoChart>
|
</TodoChart>
|
||||||
<button class="sidebar-button"
|
<button class="sidebar-button"
|
||||||
|
|
@ -63,28 +65,21 @@
|
||||||
import Card from "../common/Card.vue";
|
import Card from "../common/Card.vue";
|
||||||
import DataTable from "../common/DataTable.vue";
|
import DataTable from "../common/DataTable.vue";
|
||||||
import TodoChart from "../common/TodoChart.vue";
|
import TodoChart from "../common/TodoChart.vue";
|
||||||
import { ref, onMounted, watch } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import Api from "../../api";
|
import Api from "../../api";
|
||||||
import { useLoadingStore } from "../../stores/loading";
|
import { useLoadingStore } from "../../stores/loading";
|
||||||
import { usePaginationStore } from "../../stores/pagination";
|
import { usePaginationStore } from "../../stores/pagination";
|
||||||
import { useFiltersStore } from "../../stores/filters";
|
import { useFiltersStore } from "../../stores/filters";
|
||||||
import { useCompanyStore } from "../../stores/company.js";
|
|
||||||
import { CardNoAccess, CalendarCheck } from "@iconoir/vue";
|
import { CardNoAccess, CalendarCheck } from "@iconoir/vue";
|
||||||
|
|
||||||
const loadingStore = useLoadingStore();
|
const loadingStore = useLoadingStore();
|
||||||
const paginationStore = usePaginationStore();
|
const paginationStore = usePaginationStore();
|
||||||
const filtersStore = useFiltersStore();
|
const filtersStore = useFiltersStore();
|
||||||
const companyStore = useCompanyStore();
|
|
||||||
|
|
||||||
const tableData = ref([]);
|
const tableData = ref([]);
|
||||||
const totalRecords = ref(0);
|
const totalRecords = ref(0);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
|
||||||
const chartData = ref({
|
|
||||||
jobsToInvoice: {labels: ["Ready To Invoice"], data: [0], colors: ['green']},
|
|
||||||
invoicesLate: {labels: ["Due", "30 Days", "60 Days", "80 Days"], data: [0, 0, 0, 0], colors: ["blue", "yellow", "orange", "red"]}
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ label: "Customer Address", fieldName: "address", type: "text", sortable: true },
|
{ label: "Customer Address", fieldName: "address", type: "text", sortable: true },
|
||||||
{ label: "Customer", fieldName: "customer", type: "text", sortable: true, filterable: true },
|
{ label: "Customer", fieldName: "customer", type: "text", sortable: true, filterable: true },
|
||||||
|
|
@ -173,12 +168,6 @@ const handleLazyLoad = async (event) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load Chart Data
|
|
||||||
const loadChartData = async () => {
|
|
||||||
chartData.value.jobsToInvoice.data = await Api.getJobsToInvoiceCount(companyStore.currentCompany);
|
|
||||||
chartData.value.invoicesLate.data = await Api.getInvoicesLateCount(companyStore.currentCompany);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load initial data
|
// Load initial data
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// Initialize pagination and filters
|
// Initialize pagination and filters
|
||||||
|
|
@ -199,12 +188,6 @@ onMounted(async () => {
|
||||||
sortOrder: initialSorting.order || initialPagination.sortOrder,
|
sortOrder: initialSorting.order || initialPagination.sortOrder,
|
||||||
filters: initialFilters,
|
filters: initialFilters,
|
||||||
});
|
});
|
||||||
|
|
||||||
await loadChartData();
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(() => companyStore.currentCompany, async (newCompany, oldCompany) => {
|
|
||||||
await loadChartData();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
{{ job.jobAddress["fullAddress"] || "" }}
|
{{ job.customInstallationAddress || "" }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -32,26 +32,6 @@
|
||||||
</template>
|
</template>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div class="job-info">
|
|
||||||
<Card>
|
|
||||||
<template #header>
|
|
||||||
<div class="widget-header">
|
|
||||||
<h3>Job Status</h3>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<div class="widget-content">
|
|
||||||
Job is {{ job.status }}.
|
|
||||||
<button
|
|
||||||
class="sidebar-button"
|
|
||||||
@click="createInvoiceForJob()"
|
|
||||||
>
|
|
||||||
Create Invoice
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="task-list">
|
<div class="task-list">
|
||||||
<DataTable
|
<DataTable
|
||||||
|
|
@ -144,11 +124,6 @@ const tableActions = computed(() => [
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const createInvoiceForJob = async () => {
|
|
||||||
console.log(job);
|
|
||||||
await Api.createInvoiceForJob(job.value.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleLazyLoad = async (event) => {
|
const handleLazyLoad = async (event) => {
|
||||||
console.log("Task list on Job Page - handling lazy load:", event);
|
console.log("Task list on Job Page - handling lazy load:", event);
|
||||||
try {
|
try {
|
||||||
|
|
@ -257,7 +232,7 @@ const handleLazyLoad = async (event) => {
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
console.log("DEBUG: Query params:", route.query);
|
console.log("DEBUG: Query params:", route.query);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const optionsResult = await Api.getTaskStatusOptions();
|
const optionsResult = await Api.getTaskStatusOptions();
|
||||||
if (optionsResult && optionsResult.length > 0) {
|
if (optionsResult && optionsResult.length > 0) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
<TodoChart
|
<TodoChart
|
||||||
title="Jobs In Queue"
|
title="Jobs In Queue"
|
||||||
:categories="chartData.jobsInQueue"
|
:todoNumber="jobQueueTodoNumber"
|
||||||
|
:completedNumber="jobQueueCompletedNumber"
|
||||||
>
|
>
|
||||||
</TodoChart>
|
</TodoChart>
|
||||||
<button class="sidebar-button"
|
<button class="sidebar-button"
|
||||||
|
|
@ -37,7 +38,8 @@
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
<TodoChart
|
<TodoChart
|
||||||
title="Jobs in Progress"
|
title="Jobs in Progress"
|
||||||
:categories="chartData.jobsInProgress"
|
:todoNumber="progressTodoNumber"
|
||||||
|
:completedNumber="progressCompletedNumber"
|
||||||
>
|
>
|
||||||
</TodoChart>
|
</TodoChart>
|
||||||
<button class="sidebar-button"
|
<button class="sidebar-button"
|
||||||
|
|
@ -59,7 +61,8 @@
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
<TodoChart
|
<TodoChart
|
||||||
title="Late Jobs"
|
title="Late Jobs"
|
||||||
:categories="chartData.jobsLate"
|
:todoNumber="lateTodoNumber"
|
||||||
|
:completedNumber="lateCompletedNumber"
|
||||||
>
|
>
|
||||||
</TodoChart>
|
</TodoChart>
|
||||||
<button class="sidebar-button"
|
<button class="sidebar-button"
|
||||||
|
|
@ -81,7 +84,8 @@
|
||||||
<div class="widget-content">
|
<div class="widget-content">
|
||||||
<TodoChart
|
<TodoChart
|
||||||
title="Ready To Invoice"
|
title="Ready To Invoice"
|
||||||
:categories="chartData.jobsToInvoice"
|
:todoNumber="invoiceTodoNumber"
|
||||||
|
:completedNumber="invoiceCompletedNumber"
|
||||||
>
|
>
|
||||||
</TodoChart>
|
</TodoChart>
|
||||||
<button class="sidebar-button"
|
<button class="sidebar-button"
|
||||||
|
|
@ -108,39 +112,29 @@
|
||||||
import Card from "../common/Card.vue";
|
import Card from "../common/Card.vue";
|
||||||
import DataTable from "../common/DataTable.vue";
|
import DataTable from "../common/DataTable.vue";
|
||||||
import TodoChart from "../common/TodoChart.vue";
|
import TodoChart from "../common/TodoChart.vue";
|
||||||
import { ref, onMounted, watch } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import Api from "../../api";
|
import Api from "../../api";
|
||||||
import { useLoadingStore } from "../../stores/loading";
|
import { useLoadingStore } from "../../stores/loading";
|
||||||
import { usePaginationStore } from "../../stores/pagination";
|
import { usePaginationStore } from "../../stores/pagination";
|
||||||
import { useFiltersStore } from "../../stores/filters";
|
import { useFiltersStore } from "../../stores/filters";
|
||||||
import { useCompanyStore } from "../../stores/company.js";
|
|
||||||
import { useNotificationStore } from "../../stores/notifications-primevue";
|
import { useNotificationStore } from "../../stores/notifications-primevue";
|
||||||
import { Alarm, CalendarCheck, Hammer } from "@iconoir/vue";
|
import { Alarm, CalendarCheck, Hammer } from "@iconoir/vue";
|
||||||
|
|
||||||
const loadingStore = useLoadingStore();
|
const loadingStore = useLoadingStore();
|
||||||
const paginationStore = usePaginationStore();
|
const paginationStore = usePaginationStore();
|
||||||
const filtersStore = useFiltersStore();
|
const filtersStore = useFiltersStore();
|
||||||
const companyStore = useCompanyStore();
|
|
||||||
const notifications = useNotificationStore();
|
const notifications = useNotificationStore();
|
||||||
|
|
||||||
const tableData = ref([]);
|
const tableData = ref([]);
|
||||||
const totalRecords = ref(0);
|
const totalRecords = ref(0);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
|
||||||
const chartData = ref({
|
|
||||||
jobsInQueue: {labels: ["Queued"], data: [0], colors: ['blue']},
|
|
||||||
jobsInProgress: {labels: ["In Progress"], data: [0], colors: ['blue']},
|
|
||||||
jobsLate: {labels: ["Late"], data: [0], colors: ['red']},
|
|
||||||
jobsToInvoice: {labels: ["Ready To Invoice"], data: [0], colors: ['green']},
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ label: "Job ID", fieldName: "name", type: "text", sortable: true, filterable: true },
|
{ label: "Job ID", fieldName: "name", type: "text", sortable: true, filterable: true },
|
||||||
{ label: "Address", fieldName: "jobAddress", type: "text", sortable: true },
|
{ label: "Address", fieldName: "customInstallationAddress", type: "text", sortable: true },
|
||||||
{ label: "Customer", fieldName: "customer", type: "text", sortable: true, filterable: true },
|
{ label: "Customer", fieldName: "customer", type: "text", sortable: true, filterable: true },
|
||||||
{ label: "Overall Status", fieldName: "status", type: "status", sortable: true },
|
{ label: "Overall Status", fieldName: "status", type: "status", sortable: true },
|
||||||
{ label: "Invoice Status", fieldName: "invoiceStatus", type: "text", sortable: true },
|
|
||||||
{ label: "Progress", fieldName: "percentComplete", type: "text", sortable: true }
|
{ label: "Progress", fieldName: "percentComplete", type: "text", sortable: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -266,13 +260,6 @@ const handleRowClick = (event) => {
|
||||||
router.push(`/job?name=${rowData.name}`);
|
router.push(`/job?name=${rowData.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadChartData = async () => {
|
|
||||||
chartData.value.jobsInQueue.data = await Api.getJobsInQueueCount(companyStore.currentCompany);
|
|
||||||
chartData.value.jobsInProgress.data = await Api.getJobsInProgressCount(companyStore.currentCompany);
|
|
||||||
chartData.value.jobsLate.data = await Api.getJobsLateCount(companyStore.currentCompany);
|
|
||||||
chartData.value.jobsToInvoice.data = await Api.getJobsToInvoiceCount(companyStore.currentCompany);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load initial data
|
// Load initial data
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
notifications.addWarning("Jobs page coming soon");
|
notifications.addWarning("Jobs page coming soon");
|
||||||
|
|
@ -293,16 +280,7 @@ onMounted(async () => {
|
||||||
sortField: initialSorting.field || initialPagination.sortField,
|
sortField: initialSorting.field || initialPagination.sortField,
|
||||||
sortOrder: initialSorting.order || initialPagination.sortOrder,
|
sortOrder: initialSorting.order || initialPagination.sortOrder,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Chart Data
|
|
||||||
await loadChartData();
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(() => companyStore.currentCompany, async (newCompany, oldCompany) => {
|
|
||||||
await loadChartData();
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="css">
|
<style lang="css">
|
||||||
.widgets-grid {
|
.widgets-grid {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue