diff --git a/custom_ui/api/db/jobs.py b/custom_ui/api/db/jobs.py index e806371..13d8110 100644 --- a/custom_ui/api/db/jobs.py +++ b/custom_ui/api/db/jobs.py @@ -46,5 +46,64 @@ def get_jobs_table_data(filters={}, sortings=[], page=1, page_size=10): @frappe.whitelist() def upsert_job(data): """Create or update a job (project).""" - # TODO: Implement job creation/update logic - pass + try: + if isinstance(data, str): + data = json.loads(data) + + project_id = data.get("id") + if not project_id: + return {"status": "error", "message": "Project ID is required"} + + project = frappe.get_doc("Project", project_id) + + if "scheduledDate" in data: + project.expected_start_date = data["scheduledDate"] + + if "foreman" in data: + project.custom_install_crew = data["foreman"] + + project.save() + return {"status": "success", "data": project.as_dict()} + except Exception as e: + return {"status": "error", "message": str(e)} + +@frappe.whitelist() +def get_install_projects(start_date=None, end_date=None): + """Get install projects for the calendar.""" + try: + filters = {"project_template": "SNW Install"} + # If date range provided, we could filter, but for now let's fetch all open/active ones + # or maybe filter by status not Closed/Completed if we want active ones. + # The user said "unscheduled" are those with status "Open" (and no date). + + projects = frappe.get_all("Project", fields=["*"], filters=filters) + + calendar_events = [] + for project in projects: + # Determine status + status = "unscheduled" + if project.get("expected_start_date"): + status = "scheduled" + + # Map to calendar event format + event = { + "id": project.name, + "serviceType": project.project_name, # Using project name as service type/title + "customer": project.customer, + "status": status, + "scheduledDate": project.expected_start_date, + "scheduledTime": "08:00", # Default time if not specified? Project doesn't seem to have time. + "duration": 480, # Default 8 hours? + "foreman": project.get("custom_install_crew"), + "crew": [], # Need to map crew + "estimatedCost": project.estimated_costing, + "priority": project.priority.lower() if project.priority else "medium", + "notes": project.notes, + "address": project.custom_installation_address + } + + calendar_events.append(event) + + return {"status": "success", "data": calendar_events} + except Exception as e: + return {"status": "error", "message": str(e)} \ No newline at end of file diff --git a/frontend/src/api.js b/frontend/src/api.js index dbc277f..78e7aee 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -16,6 +16,7 @@ const FRAPPE_ESTIMATE_UPDATE_RESPONSE_METHOD = "custom_ui.api.db.estimates.manua const FRAPPE_GET_JOBS_METHOD = "custom_ui.api.db.jobs.get_jobs_table_data"; const FRAPPE_UPSERT_JOB_METHOD = "custom_ui.api.db.jobs.upsert_job"; const FRAPPE_GET_JOB_TASK_LIST_METHOD = "custom_ui.api.db.get_job_task_list"; +const FRAPPE_GET_INSTALL_PROJECTS_METHOD = "custom_ui.api.db.jobs.get_install_projects"; // Invoice methods const FRAPPE_GET_INVOICES_METHOD = "custom_ui.api.db.invoices.get_invoice_table_data"; const FRAPPE_UPSERT_INVOICE_METHOD = "custom_ui.api.db.invoices.upsert_invoice"; @@ -98,6 +99,17 @@ class Api { return await this.request(FRAPPE_GET_CLIENT_NAMES_METHOD, { type }); } + static async getInstallProjects(startDate, endDate) { + return await this.request(FRAPPE_GET_INSTALL_PROJECTS_METHOD, { + start_date: startDate, + end_date: endDate, + }); + } + + static async upsertJob(data) { + return await this.request(FRAPPE_UPSERT_JOB_METHOD, { data }); + } + static async getClientNames(clientName) { return await this.request(FRAPPE_GET_CLIENT_NAMES_METHOD, { searchTerm: clientName }); } diff --git a/frontend/src/components/calendar/CalendarNavigation.vue b/frontend/src/components/calendar/CalendarNavigation.vue index e341596..6d8993a 100644 --- a/frontend/src/components/calendar/CalendarNavigation.vue +++ b/frontend/src/components/calendar/CalendarNavigation.vue @@ -15,7 +15,7 @@ - +
@@ -56,6 +56,7 @@ import TabPanel from 'primevue/tabpanel'; import TabPanels from 'primevue/tabpanels'; import ScheduleBid from '../calendar/bids/ScheduleBid.vue'; import JobsCalendar from '../calendar/jobs/JobsCalendar.vue'; +import InstallsCalendar from '../calendar/jobs/InstallsCalendar.vue'; import { useNotificationStore } from '../../stores/notifications-primevue'; const notifications = useNotificationStore(); diff --git a/frontend/src/components/calendar/jobs/InstallsCalendar.vue b/frontend/src/components/calendar/jobs/InstallsCalendar.vue new file mode 100644 index 0000000..d173b2e --- /dev/null +++ b/frontend/src/components/calendar/jobs/InstallsCalendar.vue @@ -0,0 +1,1200 @@ + + + + + \ No newline at end of file