|
|
|
@@ -0,0 +1,147 @@
|
|
|
|
|
<?php
|
|
|
|
|
namespace Moko\Plugin\System\MokoSuiteField\Helper;
|
|
|
|
|
|
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
|
|
|
|
|
|
use Joomla\CMS\Factory;
|
|
|
|
|
use Joomla\Database\DatabaseInterface;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Safety checklists — pre-job safety checks, compliance tracking, incident prevention.
|
|
|
|
|
*/
|
|
|
|
|
class SafetyChecklistHelper
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Create a safety checklist for a work order.
|
|
|
|
|
*/
|
|
|
|
|
public static function createChecklist(int $woId, string $trade): int
|
|
|
|
|
{
|
|
|
|
|
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
|
|
|
|
$now = Factory::getDate()->toSql();
|
|
|
|
|
|
|
|
|
|
$items = self::getDefaultItems($trade);
|
|
|
|
|
|
|
|
|
|
$checklist = (object) [
|
|
|
|
|
'wo_id' => $woId,
|
|
|
|
|
'trade' => $trade,
|
|
|
|
|
'status' => 'pending',
|
|
|
|
|
'created' => $now,
|
|
|
|
|
'created_by' => Factory::getApplication()->getIdentity()->id,
|
|
|
|
|
];
|
|
|
|
|
$db->insertObject('#__mokosuitefield_safety_checklists', $checklist, 'id');
|
|
|
|
|
$checklistId = (int) $checklist->id;
|
|
|
|
|
|
|
|
|
|
foreach ($items as $i => $item) {
|
|
|
|
|
$db->insertObject('#__mokosuitefield_safety_checklist_items', (object) [
|
|
|
|
|
'checklist_id' => $checklistId,
|
|
|
|
|
'item_text' => $item,
|
|
|
|
|
'checked' => 0,
|
|
|
|
|
'ordering' => $i + 1,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $checklistId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Complete a checklist item.
|
|
|
|
|
*/
|
|
|
|
|
public static function checkItem(int $itemId, bool $passed, string $notes = ''): bool
|
|
|
|
|
{
|
|
|
|
|
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
|
|
|
|
|
|
|
|
|
// Verify item belongs to a pending checklist and is not already checked
|
|
|
|
|
$db->setQuery($db->getQuery(true)
|
|
|
|
|
->select('sci.id, sci.checked, sc.status AS checklist_status')
|
|
|
|
|
->from($db->quoteName('#__mokosuitefield_safety_checklist_items', 'sci'))
|
|
|
|
|
->join('INNER', $db->quoteName('#__mokosuitefield_safety_checklists', 'sc') . ' ON sc.id = sci.checklist_id')
|
|
|
|
|
->where('sci.id = ' . (int) $itemId));
|
|
|
|
|
$existing = $db->loadObject();
|
|
|
|
|
|
|
|
|
|
if (!$existing || $existing->checklist_status !== 'pending' || (int) $existing->checked === 1) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$update = (object) [
|
|
|
|
|
'id' => $itemId,
|
|
|
|
|
'checked' => 1,
|
|
|
|
|
'passed' => $passed ? 1 : 0,
|
|
|
|
|
'notes' => $notes,
|
|
|
|
|
'checked_at' => Factory::getDate()->toSql(),
|
|
|
|
|
'checked_by' => Factory::getApplication()->getIdentity()->id,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$db->updateObject('#__mokosuitefield_safety_checklist_items', $update, 'id');
|
|
|
|
|
|
|
|
|
|
// Auto-complete checklist if all items are checked
|
|
|
|
|
$db->setQuery($db->getQuery(true)
|
|
|
|
|
->select('sc.id, COUNT(sci2.id) AS total, SUM(CASE WHEN sci2.checked = 1 THEN 1 ELSE 0 END) AS done')
|
|
|
|
|
->from($db->quoteName('#__mokosuitefield_safety_checklist_items', 'sci2'))
|
|
|
|
|
->join('INNER', $db->quoteName('#__mokosuitefield_safety_checklists', 'sc') . ' ON sc.id = sci2.checklist_id')
|
|
|
|
|
->where('sci2.id = ' . (int) $itemId)
|
|
|
|
|
->group('sc.id'));
|
|
|
|
|
$progress = $db->loadObject();
|
|
|
|
|
|
|
|
|
|
if ($progress && (int) $progress->done === (int) $progress->total) {
|
|
|
|
|
$db->setQuery($db->getQuery(true)
|
|
|
|
|
->update('#__mokosuitefield_safety_checklists')
|
|
|
|
|
->set($db->quoteName('status') . ' = ' . $db->quote('completed'))
|
|
|
|
|
->where('id = ' . (int) $progress->id));
|
|
|
|
|
$db->execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get checklist completion status for a work order.
|
|
|
|
|
*/
|
|
|
|
|
public static function getStatus(int $woId): ?object
|
|
|
|
|
{
|
|
|
|
|
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
|
|
|
|
|
|
|
|
|
$db->setQuery($db->getQuery(true)
|
|
|
|
|
->select('sc.id, sc.status')
|
|
|
|
|
->select('COUNT(sci.id) AS total_items')
|
|
|
|
|
->select('SUM(CASE WHEN sci.checked = 1 THEN 1 ELSE 0 END) AS checked_items')
|
|
|
|
|
->select('SUM(CASE WHEN sci.checked = 1 AND sci.passed = 0 THEN 1 ELSE 0 END) AS failed_items')
|
|
|
|
|
->from($db->quoteName('#__mokosuitefield_safety_checklists', 'sc'))
|
|
|
|
|
->join('LEFT', $db->quoteName('#__mokosuitefield_safety_checklist_items', 'sci') . ' ON sci.checklist_id = sc.id')
|
|
|
|
|
->where('sc.wo_id = ' . (int) $woId)
|
|
|
|
|
->group('sc.id')
|
|
|
|
|
->order('sc.created DESC'));
|
|
|
|
|
|
|
|
|
|
$status = $db->loadObject();
|
|
|
|
|
if (!$status) return null;
|
|
|
|
|
|
|
|
|
|
$status->complete = (int) $status->checked_items === (int) $status->total_items;
|
|
|
|
|
$status->all_passed = (int) $status->failed_items === 0;
|
|
|
|
|
$status->safe_to_proceed = $status->complete && $status->all_passed;
|
|
|
|
|
|
|
|
|
|
return $status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get default safety items by trade.
|
|
|
|
|
*/
|
|
|
|
|
private static function getDefaultItems(string $trade): array
|
|
|
|
|
{
|
|
|
|
|
$common = [
|
|
|
|
|
'PPE worn (gloves, safety glasses, boots)',
|
|
|
|
|
'Work area inspected for hazards',
|
|
|
|
|
'Tools and equipment in good condition',
|
|
|
|
|
'Fire extinguisher accessible',
|
|
|
|
|
'Emergency exits identified',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$tradeSpecific = match ($trade) {
|
|
|
|
|
'electrical' => ['Lockout/tagout verified', 'Voltage tester functional', 'Grounding confirmed', 'Arc flash boundaries marked'],
|
|
|
|
|
'plumbing' => ['Water supply shut off', 'Gas lines identified and marked', 'Asbestos check for older buildings', 'Drain protection in place'],
|
|
|
|
|
'hvac' => ['Refrigerant handling certification verified', 'Electrical isolation confirmed', 'Ductwork supports inspected', 'Ladder/scaffold secured'],
|
|
|
|
|
'general' => ['Work permit obtained if required', 'Material Safety Data Sheets reviewed'],
|
|
|
|
|
default => [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return array_merge($common, $tradeSpecific);
|
|
|
|
|
}
|
|
|
|
|
}
|