add changes

This commit is contained in:
Casey Wittrock 2025-11-12 15:26:39 -06:00
parent 1af288aa62
commit 172927e069
3 changed files with 79 additions and 344 deletions

View File

@ -1,6 +1,6 @@
import frappe, json, re
from datetime import datetime, date
from custom_ui.db_utils import process_query_conditions, build_datatable_response, get_count_or_filters
from custom_ui.db_utils import build_error_response, build_success_response, process_query_conditions, build_datatable_dict, get_count_or_filters
# ===============================================================================
# WARRANTY MANAGEMENT API METHODS
@ -9,78 +9,84 @@ from custom_ui.db_utils import process_query_conditions, build_datatable_respons
@frappe.whitelist()
def get_warranty_claims(filters={}, sortings=[], page=1, page_size=10):
"""Get paginated warranty claims table data with filtering and sorting support."""
print("DEBUG: Raw warranty options received:", filters, sortings, page, page_size)
processed_filters, processed_sortings, is_or, page, page_size = process_query_conditions(filters, sortings, page, page_size)
# Handle count with proper OR filter support
if is_or:
count = frappe.db.sql(*get_count_or_filters("Warranty Claim", processed_filters))[0][0]
else:
count = frappe.db.count("Warranty Claim", filters=processed_filters)
warranty_claims = frappe.db.get_all(
"Warranty Claim",
fields=["*"],
filters=processed_filters if not is_or else None,
or_filters=processed_filters if is_or else None,
limit=page_size,
start=(page - 1) * page_size,
order_by=processed_sortings
)
tableRows = []
for warranty in warranty_claims:
tableRow = {}
tableRow["id"] = warranty["name"]
tableRow["warrantyId"] = warranty["name"]
tableRow["customer"] = warranty.get("customer_name", "")
tableRow["serviceAddress"] = warranty.get("service_address", warranty.get("address_display", ""))
# Extract a brief description from the complaint HTML
complaint_text = warranty.get("complaint", "")
if complaint_text:
# Simple HTML stripping for display - take first 100 chars
clean_text = re.sub('<.*?>', '', complaint_text)
clean_text = clean_text.strip()
if len(clean_text) > 100:
clean_text = clean_text[:100] + "..."
tableRow["issueDescription"] = clean_text
try:
print("DEBUG: Raw warranty options received:", filters, sortings, page, page_size)
processed_filters, processed_sortings, is_or, page, page_size = process_query_conditions(filters, sortings, page, page_size)
# Handle count with proper OR filter support
if is_or:
count = frappe.db.sql(*get_count_or_filters("Warranty Claim", processed_filters))[0][0]
else:
tableRow["issueDescription"] = ""
count = frappe.db.count("Warranty Claim", filters=processed_filters)
warranty_claims = frappe.db.get_all(
"Warranty Claim",
fields=["*"],
filters=processed_filters if not is_or else None,
or_filters=processed_filters if is_or else None,
limit=page_size,
start=(page - 1) * page_size,
order_by=processed_sortings
)
tableRows = []
for warranty in warranty_claims:
tableRow = {}
tableRow["id"] = warranty["name"]
tableRow["warrantyId"] = warranty["name"]
tableRow["customer"] = warranty.get("customer_name", "")
tableRow["serviceAddress"] = warranty.get("service_address", warranty.get("address_display", ""))
tableRow["status"] = warranty.get("status", "")
tableRow["complaintDate"] = warranty.get("complaint_date", "")
tableRow["complaintRaisedBy"] = warranty.get("complaint_raised_by", "")
tableRow["fromCompany"] = warranty.get("from_company", "")
tableRow["territory"] = warranty.get("territory", "")
tableRow["resolutionDate"] = warranty.get("resolution_date", "")
tableRow["warrantyStatus"] = warranty.get("warranty_amc_status", "")
# Add priority based on status and date (can be customized)
if warranty.get("status") == "Open":
# Calculate priority based on complaint date
if warranty.get("complaint_date"):
complaint_date = warranty.get("complaint_date")
if isinstance(complaint_date, str):
complaint_date = datetime.strptime(complaint_date, "%Y-%m-%d").date()
elif isinstance(complaint_date, datetime):
complaint_date = complaint_date.date()
days_old = (date.today() - complaint_date).days
if days_old > 7:
tableRow["priority"] = "High"
elif days_old > 3:
tableRow["priority"] = "Medium"
else:
tableRow["priority"] = "Low"
# Extract a brief description from the complaint HTML
complaint_text = warranty.get("complaint", "")
if complaint_text:
# Simple HTML stripping for display - take first 100 chars
clean_text = re.sub('<.*?>', '', complaint_text)
clean_text = clean_text.strip()
if len(clean_text) > 100:
clean_text = clean_text[:100] + "..."
tableRow["issueDescription"] = clean_text
else:
tableRow["priority"] = "Medium"
else:
tableRow["priority"] = "Low"
tableRows.append(tableRow)
return build_datatable_response(data=tableRows, count=count, page=page, page_size=page_size)
tableRow["issueDescription"] = ""
tableRow["status"] = warranty.get("status", "")
tableRow["complaintDate"] = warranty.get("complaint_date", "")
tableRow["complaintRaisedBy"] = warranty.get("complaint_raised_by", "")
tableRow["fromCompany"] = warranty.get("from_company", "")
tableRow["territory"] = warranty.get("territory", "")
tableRow["resolutionDate"] = warranty.get("resolution_date", "")
tableRow["warrantyStatus"] = warranty.get("warranty_amc_status", "")
# Add priority based on status and date (can be customized)
if warranty.get("status") == "Open":
# Calculate priority based on complaint date
if warranty.get("complaint_date"):
complaint_date = warranty.get("complaint_date")
if isinstance(complaint_date, str):
complaint_date = datetime.strptime(complaint_date, "%Y-%m-%d").date()
elif isinstance(complaint_date, datetime):
complaint_date = complaint_date.date()
days_old = (date.today() - complaint_date).days
if days_old > 7:
tableRow["priority"] = "High"
elif days_old > 3:
tableRow["priority"] = "Medium"
else:
tableRow["priority"] = "Low"
else:
tableRow["priority"] = "Medium"
else:
tableRow["priority"] = "Low"
tableRows.append(tableRow)
tableDataDict = build_datatable_dict(data=tableRows, count=count, page=page, page_size=page_size)
return build_success_response(tableDataDict)
except frappe.ValidationError as ve:
return build_error_response(str(ve), 400)
except Exception as e:
return build_error_response(str(e), 500)
@frappe.whitelist()

View File

@ -6,6 +6,7 @@ const FRAPPE_UPSERT_ESTIMATE_METHOD = "custom_ui.api.db.estimates.upsert_estimat
const FRAPPE_GET_JOBS_METHOD = "custom_ui.api.db.get_jobs";
const FRAPPE_UPSERT_JOB_METHOD = "custom_ui.api.db.jobs.upsert_job";
const FRAPPE_UPSERT_INVOICE_METHOD = "custom_ui.api.db.invoices.upsert_invoice";
const FRAPPE_GET_WARRANTY_CLAIMS_METHOD = "custom_ui.api.db.warranties.get_warranty_claims";
const FRAPPE_UPSERT_CLIENT_METHOD = "custom_ui.api.db.clients.upsert_client";
const FRAPPE_GET_CLIENT_STATUS_COUNTS_METHOD = "custom_ui.api.db.clients.get_client_status_counts";
const FRAPPE_GET_CLIENT_TABLE_DATA_METHOD = "custom_ui.api.db.clients.get_clients_table_data";
@ -73,7 +74,7 @@ class Api {
}
static async getWarrantyData() {
const data = DataUtils.dummyWarrantyData;
const data = await this.request(FRAPPE_GET_WARRANTY_CLAIMS_METHOD);
console.log("DEBUG: API - getWarrantyData result: ", data);
return data;
}
@ -180,7 +181,7 @@ class Api {
console.log("DEBUG: API - Sending warranty options to backend:", options);
const result = await this.request("custom_ui.api.db.get_warranty_claims", { options });
const result = await this.request(FRAPPE_GET_WARRANTY_CLAIMS_METHOD, { options });
return result;
}

View File

@ -1,272 +0,0 @@
import { defineStore } from "pinia";
// Global toast instance - will be set during app initialization
let toastInstance = null;
export const useNotificationStore = defineStore("notifications", {
state: () => ({
// Configuration for PrimeVue Toast
defaultLife: 4000,
position: "top-right",
}),
getters: {
// Helper to check if toast is available
isToastAvailable: () => !!toastInstance,
},
actions: {
// Add a new notification
addNotification(notification) {
const newNotification = {
id: this.nextId++,
type: notification.type || "info", // info, success, warning, error
title: notification.title || "",
message: notification.message || "",
duration: notification.duration ?? this.defaultDuration,
persistent: notification.persistent || false, // If true, won't auto-dismiss
actions: notification.actions || [], // Array of action buttons
data: notification.data || null, // Any additional data
timestamp: new Date().toISOString(),
dismissed: false,
seen: false,
};
// Add to beginning of array (newest first)
this.notifications.unshift(newNotification);
// Trim notifications if we exceed max count
if (this.notifications.length > this.maxNotifications * 2) {
// Keep twice the max to maintain some history
this.notifications = this.notifications.slice(0, this.maxNotifications * 2);
}
// Auto-dismiss if not persistent
if (!newNotification.persistent && newNotification.duration > 0) {
setTimeout(() => {
this.dismissNotification(newNotification.id);
}, newNotification.duration);
}
return newNotification.id;
},
// Convenience methods for different types of notifications
addSuccess(message, title = "Success", options = {}) {
return this.addNotification({
type: "success",
title,
message,
...options,
});
},
addError(message, title = "Error", options = {}) {
return this.addNotification({
type: "error",
title,
message,
duration: options.duration ?? 6000, // Errors stay longer by default
...options,
});
},
addWarning(message, title = "Warning", options = {}) {
return this.addNotification({
type: "warning",
title,
message,
...options,
});
},
addInfo(message, title = "Info", options = {}) {
return this.addNotification({
type: "info",
title,
message,
...options,
});
},
// Dismiss a specific notification
dismissNotification(id) {
const notification = this.notifications.find((n) => n.id === id);
if (notification) {
notification.dismissed = true;
}
},
// Remove a notification completely
removeNotification(id) {
const index = this.notifications.findIndex((n) => n.id === id);
if (index !== -1) {
this.notifications.splice(index, 1);
}
},
// Mark notification as seen
markAsSeen(id) {
const notification = this.notifications.find((n) => n.id === id);
if (notification) {
notification.seen = true;
}
},
// Clear all notifications of a specific type
clearType(type) {
this.notifications = this.notifications.filter((n) => n.type !== type);
},
// Clear all notifications
clearAll() {
this.notifications = [];
},
// Clear all dismissed notifications
clearDismissed() {
this.notifications = this.notifications.filter((n) => !n.dismissed);
},
// Update notification content
updateNotification(id, updates) {
const notification = this.notifications.find((n) => n.id === id);
if (notification) {
Object.assign(notification, updates);
}
},
// Show a loading notification that can be updated
showLoadingNotification(message, title = "Loading...") {
return this.addNotification({
type: "info",
title,
message,
persistent: true,
actions: [],
});
},
// Update a loading notification to success
updateToSuccess(id, message, title = "Success") {
this.updateNotification(id, {
type: "success",
title,
message,
persistent: false,
duration: this.defaultDuration,
});
// Auto-dismiss after updating
setTimeout(() => {
this.dismissNotification(id);
}, this.defaultDuration);
},
// Update a loading notification to error
updateToError(id, message, title = "Error") {
this.updateNotification(id, {
type: "error",
title,
message,
persistent: false,
duration: 6000,
});
// Auto-dismiss after updating
setTimeout(() => {
this.dismissNotification(id);
}, 6000);
},
// Show API operation notifications
showApiSuccess(operation, message = null) {
const defaultMessages = {
create: "Item created successfully",
update: "Item updated successfully",
delete: "Item deleted successfully",
fetch: "Data loaded successfully",
};
return this.addSuccess(
message || defaultMessages[operation] || "Operation completed successfully",
"Success",
);
},
showApiError(operation, error, message = null) {
const defaultMessages = {
create: "Failed to create item",
update: "Failed to update item",
delete: "Failed to delete item",
fetch: "Failed to load data",
};
let errorMessage = message;
if (!errorMessage) {
if (typeof error === "string") {
errorMessage = error;
} else if (error?.response?.data?.message) {
errorMessage = error.response.data.message;
} else if (error?.message) {
errorMessage = error.message;
} else {
errorMessage = defaultMessages[operation] || "Operation failed";
}
}
return this.addError(errorMessage, "Operation Failed");
},
// Configuration methods
setPosition(position) {
this.position = position;
},
setDefaultDuration(duration) {
this.defaultDuration = duration;
},
setMaxNotifications(max) {
this.maxNotifications = max;
},
// Utility method for handling async operations with notifications
async withNotifications(operation, asyncFunction, options = {}) {
const {
loadingMessage = "Processing...",
successMessage = null,
errorMessage = null,
showLoading = true,
} = options;
let loadingId = null;
try {
if (showLoading) {
loadingId = this.showLoadingNotification(loadingMessage);
}
const result = await asyncFunction();
if (loadingId) {
this.updateToSuccess(
loadingId,
successMessage || `${operation} completed successfully`,
);
} else if (successMessage !== false) {
this.showApiSuccess(operation, successMessage);
}
return result;
} catch (error) {
if (loadingId) {
this.updateToError(loadingId, errorMessage || error.message);
} else {
this.showApiError(operation, error, errorMessage);
}
throw error;
}
},
},
});