Files
MokoSuiteField/source/packages/com_mokosuitefield/admin/src/Model/WorkOrdersModel.php
T
Jonathan Miller efae025a2f fix: Address PR review findings — CSRF, status guards, SQL safety, transactions
- Add CSRF token validation on EstimateView approve/reject forms
- Add status guard on reject branch (prevent reversing approved estimates)
- Fix loc.* column collision in WorkOrdersModel::getWorkOrder (overwrote wo.id)
- Add (int) cast on all query parameter concatenations
- Wrap InvoiceHelper::generateFromWorkOrder in database transaction
- Replace magic 0.5 divisor with AVG_SPEED_MPH constant in RouteHelper
2026-06-18 09:03:19 -05:00

63 lines
2.9 KiB
PHP

<?php
namespace Moko\Component\MokoSuiteField\Administrator\Model;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
class WorkOrdersModel extends BaseDatabaseModel
{
public function getItems(string $status = '', string $trade = '', int $techId = 0, string $search = '', string $date = '', int $limit = 50, int $offset = 0): array
{
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('wo.*, cd.name AS customer_name, loc.address, loc.city, loc.state, loc.zip')
->select('t_cd.name AS tech_name')
->from($db->quoteName('#__mokosuitefield_work_orders', 'wo'))
->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = wo.contact_id')
->join('LEFT', $db->quoteName('#__mokosuitefield_locations', 'loc') . ' ON loc.id = wo.location_id')
->join('LEFT', $db->quoteName('#__mokosuitefield_technicians', 't') . ' ON t.id = wo.technician_id')
->join('LEFT', $db->quoteName('#__contact_details', 't_cd') . ' ON t_cd.id = t.contact_id')
->order('wo.scheduled_date DESC, wo.priority DESC');
if ($status) $query->where($db->quoteName('wo.status') . ' = ' . $db->quote($status));
if ($trade) $query->where($db->quoteName('wo.trade') . ' = ' . $db->quote($trade));
if ($techId) $query->where('wo.technician_id = ' . $techId);
if ($date) $query->where('wo.scheduled_date = ' . $db->quote($date));
if ($search) {
$query->where('(' . $db->quoteName('wo.wo_number') . ' LIKE ' . $db->quote('%' . $search . '%')
. ' OR ' . $db->quoteName('wo.description') . ' LIKE ' . $db->quote('%' . $search . '%')
. ' OR ' . $db->quoteName('cd.name') . ' LIKE ' . $db->quote('%' . $search . '%') . ')');
}
$db->setQuery($query, $offset, $limit);
return $db->loadObjectList() ?: [];
}
public function getWorkOrder(int $id): ?object
{
$db = $this->getDatabase();
$db->setQuery($db->getQuery(true)
->select('wo.*, cd.name AS customer_name, t_cd.name AS tech_name')
->select('loc.address, loc.city, loc.state, loc.zip, loc.latitude, loc.longitude, loc.name AS location_name')
->from($db->quoteName('#__mokosuitefield_work_orders', 'wo'))
->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = wo.contact_id')
->join('LEFT', $db->quoteName('#__mokosuitefield_locations', 'loc') . ' ON loc.id = wo.location_id')
->join('LEFT', $db->quoteName('#__mokosuitefield_technicians', 't') . ' ON t.id = wo.technician_id')
->join('LEFT', $db->quoteName('#__contact_details', 't_cd') . ' ON t_cd.id = t.contact_id')
->where('wo.id = ' . (int) $id));
return $db->loadObject();
}
public function getStatusCounts(): object
{
$db = $this->getDatabase();
$db->setQuery($db->getQuery(true)
->select('status, COUNT(*) AS cnt')
->from('#__mokosuitefield_work_orders')
->group('status'));
$rows = $db->loadObjectList('status') ?: [];
return (object) array_map(fn($r) => (int) $r->cnt, (array) $rows);
}
}