getIdentity(); if (!$user || $user->guest || (!$user->authorise('core.admin') && !$user->authorise($action, 'com_mokosuitefield'))) { http_response_code(403); echo json_encode(['error' => 'Access denied.']); Factory::getApplication()->close(); } } public function listEstimates(): void { $this->requireAuth('core.manage'); $db = Factory::getContainer()->get(DatabaseInterface::class); $input = Factory::getApplication()->getInput(); $query = $db->getQuery(true) ->select('e.*, cd.name AS customer_name, loc.address') ->from($db->quoteName('#__mokosuitefield_estimates', 'e')) ->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = e.contact_id') ->join('LEFT', $db->quoteName('#__mokosuitefield_locations', 'loc') . ' ON loc.id = e.location_id') ->order('e.created DESC'); $status = $input->getString('status', ''); if ($status) $query->where($db->quoteName('e.status') . ' = ' . $db->quote($status)); $techId = $input->getInt('technician_id', 0); if ($techId) $query->where('e.technician_id = ' . $techId); $db->setQuery($query, 0, 100); $this->sendJson($db->loadObjectList() ?: []); } public function createEstimate(): void { $this->requireAuth('core.create'); $input = Factory::getApplication()->getInput(); $estimateId = \Moko\Plugin\System\MokoSuiteField\Helper\EstimateHelper::createEstimate( $input->getInt('contact_id', 0), $input->getInt('location_id', 0), $input->getString('trade', 'general'), $input->getString('description', ''), json_decode($input->getString('line_items', '[]'), true) ?: [] ); $this->sendJson(['success' => true, 'estimate_id' => $estimateId]); } public function updateStatus(): void { $this->requireAuth('core.edit'); $input = Factory::getApplication()->getInput(); $id = $input->getInt('id', 0); $status = $input->getString('status', ''); if (!in_array($status, ['sent', 'approved', 'rejected', 'expired'])) { http_response_code(400); $this->sendJson(['error' => 'Invalid status']); return; } $db = Factory::getContainer()->get(DatabaseInterface::class); $update = (object) [ 'id' => $id, 'status' => $status, ]; if ($status === 'approved') { $update->approved_at = Factory::getDate()->toSql(); } $db->updateObject('#__mokosuitefield_estimates', $update, 'id'); $this->sendJson(['success' => true]); } public function convertToWorkOrder(): void { $this->requireAuth('core.create'); $id = Factory::getApplication()->getInput()->getInt('id', 0); $woId = \Moko\Plugin\System\MokoSuiteField\Helper\EstimateHelper::convertToWorkOrder($id); if (!$woId) { http_response_code(400); $this->sendJson(['error' => 'Could not convert estimate']); return; } $this->sendJson(['success' => true, 'work_order_id' => $woId]); } public function getRoute(): void { $this->requireAuth('core.manage'); $techId = Factory::getApplication()->getInput()->getInt('tech_id', 0); $date = Factory::getApplication()->getInput()->getString('date', date('Y-m-d')); $route = \Moko\Plugin\System\MokoSuiteField\Helper\RouteHelper::getTechRoute($techId, $date); $metrics = \Moko\Plugin\System\MokoSuiteField\Helper\RouteHelper::estimateRouteMetrics($techId, $date); $this->sendJson([ 'route' => $route, 'metrics' => $metrics, ]); } public function optimizeRoute(): void { $this->requireAuth('core.manage'); $techId = Factory::getApplication()->getInput()->getInt('tech_id', 0); $date = Factory::getApplication()->getInput()->getString('date', date('Y-m-d')); $optimized = \Moko\Plugin\System\MokoSuiteField\Helper\RouteHelper::optimizeRoute($techId, $date); $metrics = \Moko\Plugin\System\MokoSuiteField\Helper\RouteHelper::estimateRouteMetrics($techId, $date); $this->sendJson([ 'route' => $optimized, 'metrics' => $metrics, ]); } private function sendJson(mixed $data): void { header('Content-Type: application/json; charset=utf-8'); echo json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); Factory::getApplication()->close(); } }