add whitlist metho for getting status counts
This commit is contained in:
parent
84bdff4613
commit
3ea47162c5
@ -2,6 +2,71 @@ import frappe, json, re
|
|||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
from custom_ui.db_utils import calculate_appointment_scheduled_status, calculate_estimate_sent_status, calculate_payment_recieved_status, calculate_job_status
|
from custom_ui.db_utils import calculate_appointment_scheduled_status, calculate_estimate_sent_status, calculate_payment_recieved_status, calculate_job_status
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_client_status_counts(weekly=False, week_start_date=None, week_end_date=None):
|
||||||
|
# Build base filters for date range if weekly filtering is enabled
|
||||||
|
base_filters = {}
|
||||||
|
if weekly and week_start_date and week_end_date:
|
||||||
|
# Assuming you have a date field to filter by - adjust the field name as needed
|
||||||
|
# Common options: creation, modified, custom_date_field, etc.
|
||||||
|
base_filters["creation"] = ["between", [week_start_date, week_end_date]]
|
||||||
|
|
||||||
|
# Helper function to merge base filters with status filters
|
||||||
|
def get_filters(status_field, status_value):
|
||||||
|
filters = {status_field: status_value}
|
||||||
|
filters.update(base_filters)
|
||||||
|
return filters
|
||||||
|
|
||||||
|
def get_status_total(counts_dicts, status_field):
|
||||||
|
sum_array = []
|
||||||
|
for counts_dict in counts_dicts:
|
||||||
|
sum_array.append(counts_dict[status_field])
|
||||||
|
return sum(sum_array)
|
||||||
|
|
||||||
|
|
||||||
|
onsite_meeting_scheduled_status_counts = {
|
||||||
|
"Not Started": frappe.db.count("Address", filters=get_filters("custom_onsite_meeting_scheduled_status", "Not Started")),
|
||||||
|
"In Progress": frappe.db.count("Address", filters=get_filters("custom_onsite_meeting_scheduled_status", "In Progress")),
|
||||||
|
"Completed": frappe.db.count("Address", filters=get_filters("custom_onsite_meeting_scheduled_status", "Completed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
estimate_sent_status_counts = {
|
||||||
|
"Not Started": frappe.db.count("Address", filters=get_filters("custom_estimate_sent_status", "Not Started")),
|
||||||
|
"In Progress": frappe.db.count("Address", filters=get_filters("custom_estimate_sent_status", "In Progress")),
|
||||||
|
"Completed": frappe.db.count("Address", filters=get_filters("custom_estimate_sent_status", "Completed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
job_status_counts = {
|
||||||
|
"Not Started": frappe.db.count("Address", filters=get_filters("custom_job_status", "Not Started")),
|
||||||
|
"In Progress": frappe.db.count("Address", filters=get_filters("custom_job_status", "In Progress")),
|
||||||
|
"Completed": frappe.db.count("Address", filters=get_filters("custom_job_status", "Completed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
payment_received_status_counts = {
|
||||||
|
"Not Started": frappe.db.count("Address", filters=get_filters("custom_payment_received_status", "Not Started")),
|
||||||
|
"In Progress": frappe.db.count("Address", filters=get_filters("custom_payment_received_status", "In Progress")),
|
||||||
|
"Completed": frappe.db.count("Address", filters=get_filters("custom_payment_received_status", "Completed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
status_dicts = [
|
||||||
|
onsite_meeting_scheduled_status_counts,
|
||||||
|
estimate_sent_status_counts,
|
||||||
|
job_status_counts,
|
||||||
|
payment_received_status_counts
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"totals": {
|
||||||
|
"not_started": get_status_total(status_dicts, "Not Started"),
|
||||||
|
"in_progress": get_status_total(status_dicts, "In Progress"),
|
||||||
|
"completed": get_status_total(status_dicts, "Completed")
|
||||||
|
},
|
||||||
|
"onsite_meeting_scheduled_status": onsite_meeting_scheduled_status_counts,
|
||||||
|
"estimate_sent_status": estimate_sent_status_counts,
|
||||||
|
"job_status": job_status_counts,
|
||||||
|
"payment_received_status": payment_received_status_counts
|
||||||
|
}
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_clients(options):
|
def get_clients(options):
|
||||||
options = json.loads(options)
|
options = json.loads(options)
|
||||||
|
|||||||
@ -30,154 +30,6 @@ class Api {
|
|||||||
|
|
||||||
static async getClientDetails(options = {}) {
|
static async getClientDetails(options = {}) {
|
||||||
return await this.request("custom_ui.api.db.get_clients", { options });
|
return await this.request("custom_ui.api.db.get_clients", { options });
|
||||||
|
|
||||||
// Handle fullName filter by searching in multiple fields
|
|
||||||
// Since fullName is constructed from customer_name + address_line1 + city + state,
|
|
||||||
// we need to search across these fields
|
|
||||||
if (filters.addressTitle && filters.addressTitle.value) {
|
|
||||||
const searchTerm = filters.addressTitle.value;
|
|
||||||
// Search in address fields - this is a simplified approach
|
|
||||||
// In a real implementation, you'd want to join with Customer table and search across all fields
|
|
||||||
addressFilters.address_line1 = ["like", `%${searchTerm}%`];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add any other custom filters
|
|
||||||
Object.keys(filters).forEach((key) => {
|
|
||||||
if (filters[key] && filters[key].value && key !== "fullName") {
|
|
||||||
// Map other frontend filter names to backend field names if needed
|
|
||||||
switch (
|
|
||||||
key
|
|
||||||
// Add other filter mappings as needed
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get total count first for pagination
|
|
||||||
const totalCount = await this.getDocCount("Address", addressFilters);
|
|
||||||
|
|
||||||
// Get paginated addresses
|
|
||||||
const addresses = await this.getDocsList(
|
|
||||||
"Address",
|
|
||||||
["*"],
|
|
||||||
addressFilters,
|
|
||||||
page,
|
|
||||||
pageSize,
|
|
||||||
);
|
|
||||||
|
|
||||||
const data = [];
|
|
||||||
const processedData = [];
|
|
||||||
|
|
||||||
// Process each address to build client details
|
|
||||||
for (const addr of addresses) {
|
|
||||||
try {
|
|
||||||
const clientDetail = {};
|
|
||||||
|
|
||||||
const customer = await this.getDetailedDoc(
|
|
||||||
"Customer",
|
|
||||||
addr["custom_customer_to_bill"],
|
|
||||||
);
|
|
||||||
|
|
||||||
const quotations = await this.getDocsList("Quotation", [], {
|
|
||||||
custom_installation_address: addr["name"],
|
|
||||||
});
|
|
||||||
const quoteDetails =
|
|
||||||
quotations.length > 0
|
|
||||||
? await this.getDetailedDoc("Quotation", quotations[0]["name"])
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const jobs = await this.getDocsList("Project", [], {
|
|
||||||
project_template: "SNW Install",
|
|
||||||
custom_installation_address: addr["name"],
|
|
||||||
});
|
|
||||||
const jobDetails =
|
|
||||||
jobs.length > 0
|
|
||||||
? await this.getDetailedDoc("Project", jobs[0]["name"])
|
|
||||||
: null;
|
|
||||||
|
|
||||||
clientDetail.customer = customer;
|
|
||||||
clientDetail.address = addr;
|
|
||||||
clientDetail.estimate = quoteDetails;
|
|
||||||
clientDetail.job = jobDetails;
|
|
||||||
|
|
||||||
const totalPaid = quoteDetails
|
|
||||||
? quoteDetails.payment_schedule
|
|
||||||
? quoteDetails.payment_schedule.reduce(
|
|
||||||
(sum, payment) => sum + (payment.paid_amount || 0),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
: 0
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
const tableRow = {
|
|
||||||
id: addr.name, // Add unique ID for DataTable
|
|
||||||
fullName: `${customer.customer_name} - ${addr.address_line1}, ${addr.city} ${addr.state}`,
|
|
||||||
appointmentStatus: "not started",
|
|
||||||
estimateStatus: quoteDetails
|
|
||||||
? quoteDetails.custom_response == "Accepted"
|
|
||||||
? "completed"
|
|
||||||
: "in progress"
|
|
||||||
: "not started",
|
|
||||||
paymentStatus: quoteDetails
|
|
||||||
? totalPaid < quoteDetails.grand_total
|
|
||||||
? "in progress"
|
|
||||||
: "completed"
|
|
||||||
: "not started",
|
|
||||||
jobStatus: jobDetails
|
|
||||||
? jobDetails.status === "Completed"
|
|
||||||
? "completed"
|
|
||||||
: "in progress"
|
|
||||||
: "not started",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (forTable) {
|
|
||||||
data.push(tableRow);
|
|
||||||
} else {
|
|
||||||
data.push(clientDetail);
|
|
||||||
}
|
|
||||||
processedData.push(clientDetail);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error processing address ${addr.name}:`, error);
|
|
||||||
// Continue with other addresses even if one fails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply client-side sorting if needed (better to do on server)
|
|
||||||
if (sortField && forTable) {
|
|
||||||
data.sort((a, b) => {
|
|
||||||
const aValue = a[sortField] || "";
|
|
||||||
const bValue = b[sortField] || "";
|
|
||||||
const comparison = aValue.localeCompare(bValue);
|
|
||||||
return sortOrder === -1 ? -comparison : comparison;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since we're applying filters at the database level, use the fetched data as-is
|
|
||||||
let filteredData = data;
|
|
||||||
|
|
||||||
console.log("DEBUG: API - Fetched Client Details:", {
|
|
||||||
total: totalCount,
|
|
||||||
page: page,
|
|
||||||
pageSize: pageSize,
|
|
||||||
returned: filteredData.length,
|
|
||||||
filtersApplied: Object.keys(addressFilters).length > 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return paginated response with metadata
|
|
||||||
return {
|
|
||||||
data: filteredData,
|
|
||||||
pagination: {
|
|
||||||
page: page,
|
|
||||||
pageSize: pageSize,
|
|
||||||
total: totalCount,
|
|
||||||
totalPages: Math.ceil(totalCount / pageSize),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("DEBUG: API - Error fetching client details:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getJobDetails() {
|
static async getJobDetails() {
|
||||||
@ -236,8 +88,8 @@ class Api {
|
|||||||
customer: timesheet.customer,
|
customer: timesheet.customer,
|
||||||
totalHours: timesheet.total_hours,
|
totalHours: timesheet.total_hours,
|
||||||
status: timesheet.status,
|
status: timesheet.status,
|
||||||
totalPay: timesheet.total_costing_amount
|
totalPay: timesheet.total_costing_amount,
|
||||||
}
|
};
|
||||||
console.log("Timesheet Row: ", tableRow);
|
console.log("Timesheet Row: ", tableRow);
|
||||||
data.push(tableRow);
|
data.push(tableRow);
|
||||||
}
|
}
|
||||||
@ -346,7 +198,14 @@ class Api {
|
|||||||
* @param {Object} filters
|
* @param {Object} filters
|
||||||
* @returns {Promise<Object[]>}
|
* @returns {Promise<Object[]>}
|
||||||
*/
|
*/
|
||||||
static async getDocsList(doctype, fields = [], filters = {}, page = 0, start=0, pageLength = 0) {
|
static async getDocsList(
|
||||||
|
doctype,
|
||||||
|
fields = [],
|
||||||
|
filters = {},
|
||||||
|
page = 0,
|
||||||
|
start = 0,
|
||||||
|
pageLength = 0,
|
||||||
|
) {
|
||||||
const docs = await frappe.db.get_list(doctype, {
|
const docs = await frappe.db.get_list(doctype, {
|
||||||
fields,
|
fields,
|
||||||
filters,
|
filters,
|
||||||
@ -414,7 +273,7 @@ class Api {
|
|||||||
|
|
||||||
static async createEstimate(estimateData) {
|
static async createEstimate(estimateData) {
|
||||||
const payload = DataUtils.toSnakeCaseObject(estimateData);
|
const payload = DataUtils.toSnakeCaseObject(estimateData);
|
||||||
const result = await this.request(FRAPPE_UPSERT_ESTIMATE_METHOD, { data: payload} );
|
const result = await this.request(FRAPPE_UPSERT_ESTIMATE_METHOD, { data: payload });
|
||||||
console.log("DEBUG: API - Created Estimate: ", result);
|
console.log("DEBUG: API - Created Estimate: ", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -423,7 +282,7 @@ class Api {
|
|||||||
const payload = DataUtils.toSnakeCaseObject(jobData);
|
const payload = DataUtils.toSnakeCaseObject(jobData);
|
||||||
const result = await this.request(FRAPPE_UPSERT_JOB_METHOD, { data: payload });
|
const result = await this.request(FRAPPE_UPSERT_JOB_METHOD, { data: payload });
|
||||||
console.log("DEBUG: API - Created Job: ", result);
|
console.log("DEBUG: API - Created Job: ", result);
|
||||||
return result
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async createInvoice(invoiceData) {
|
static async createInvoice(invoiceData) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user