Files
My-KOPKB/be/Modules/Dashboard/Repositories/PKJDashboardRepository.php
T
ISMAIL MASSERAN 94ecbe5887 first init
2026-06-08 11:37:14 +08:00

496 lines
17 KiB
PHP

<?php
namespace Modules\Dashboard\Repositories;
use Modules\Dashboard\Repositories\Contracts\PKJDashboardRepositoryInterface;
use Modules\PKJAssetEntitlement\Entities\PKJAssetEntitlement;
use Modules\PKJAssetHolding\Entities\PKJAssetHolding;
use Modules\PKJRepair\Entities\PKJRepair;
use Modules\PKJReport\Entities\PKJReport;
use Modules\PKJVariant\Entities\PKJCategory;
use Illuminate\Support\Facades\DB;
use Modules\Unit\Entities\Unit;
use App\Services\VisibilityService;
use Carbon\Carbon;
class PKJDashboardRepository implements PKJDashboardRepositoryInterface
{
public function __construct(
protected VisibilityService $visibilityService
) {}
/**
* Get total PKJ asset entitlement
*/
public function getTotalEntitlement(array $filters = [], $user = null): int
{
$query = PKJAssetEntitlement::query();
// Apply visibility scoping if user is provided
if ($user) {
$query->visibleTo($user);
}
$this->applyFilters($query, $filters, excludeDateFilter: true);
return $query->sum('entitlement');
}
public function getTotalHoldings(array $filters = [], $user = null): int
{
$query = PKJAssetEntitlement::query();
// Apply visibility scoping if user is provided
if ($user) {
$query->visibleTo($user);
}
$this->applyFilters($query, $filters, excludeDateFilter: true);
return $query->sum('holding') ?? 0;
}
/**
* Get PKJ asset holdings grouped by status
*/
public function getHoldingsByStatus(array $filters = [], $user = null): array
{
$query = PKJAssetHolding::query();
// Apply visibility scoping if user is provided
if ($user) {
$query->visibleTo($user);
}
$this->applyFilters($query, $filters);
return $query->selectRaw('status, COUNT(*) as count')
->groupBy('status')
->orderBy('status')
->get()
->toArray();
}
/**
* Get PKJ asset holdings by specific status
*/
public function getHoldingsBySpecificStatus(string $status, array $filters = [], $user = null): array
{
$query = PKJAssetHolding::query();
// Apply visibility scoping if user is provided
if ($user) {
$query->visibleTo($user);
}
$this->applyFilters($query, $filters);
$holdings = $query->where('status', $status)->paginate(10);
return [
'data' => $holdings->items(),
'pagination' => [
'current_page' => $holdings->currentPage(),
'per_page' => $holdings->perPage(),
'total' => $holdings->total(),
'last_page' => $holdings->lastPage(),
'from' => $holdings->firstItem(),
'to' => $holdings->lastItem()
]
];
}
/**
* Get PKJ monthly trends
*/
public function getMonthlyTrends(array $filters = [], $user = null): array
{
$dateRange = $this->getDateRange($filters);
$query = PKJReport::query();
// Apply visibility scoping if user is provided
if ($user) {
$query->visibleTo($user);
}
$trends = $query->selectRaw('
TO_CHAR(created_at, \'YYYY-MM\') as month,
COUNT(*) as total_reports
')
->whereBetween('created_at', [$dateRange['from'], $dateRange['to']])
->groupBy('month')
->orderBy('month')
->get()
->toArray();
return $trends;
}
/**
* Get PKJ holdings by category
*/
public function getHoldingsByCategory(array $filters = [], $user = null): array
{
// Build a subquery for filtered entitlements
// This ensures all categories are included, but only matching entitlements are summed
$entitlementSubquery = PKJAssetEntitlement::query()
->select('pkj_category_id', DB::raw('SUM(holding) as total_holding'))
->groupBy('pkj_category_id');
// Apply visibility scoping if user is provided
if ($user) {
$entitlementSubquery->visibleTo($user);
}
// Apply filters to entitlement subquery
if (isset($filters['unit_id'])) {
$entitlementSubquery->where('unit_id', $filters['unit_id']);
}
// Apply government_id filter
if (isset($filters['government_id']) && $filters['government_id']) {
$unitIds = $this->getUnitsByGovernment($filters['government_id']);
if (!empty($unitIds)) {
$entitlementSubquery->whereIn('unit_id', $unitIds);
} else {
// If no units match, set to empty result but don't filter categories
$entitlementSubquery->whereRaw('1 = 0');
}
}
// Apply formation_id filter
if (isset($filters['formation_id']) && $filters['formation_id']) {
$unitIds = Unit::where('formation_id', $filters['formation_id'])
->pluck('id')
->toArray();
if (!empty($unitIds)) {
$entitlementSubquery->whereIn('unit_id', $unitIds);
} else {
// If no units match, set to empty result but don't filter categories
$entitlementSubquery->whereRaw('1 = 0');
}
}
// Main query: Get ALL categories and LEFT JOIN with filtered entitlements
// This ensures all categories are returned even if they have no matching entitlements
$query = PKJCategory::query()
->leftJoinSub($entitlementSubquery, 'filtered_entitlements', function ($join) {
$join->on('pkj_categories.id', '=', 'filtered_entitlements.pkj_category_id');
})
->selectRaw('
pkj_categories.id as category_id,
pkj_categories.name as category_name,
COALESCE(filtered_entitlements.total_holding, 0) as total_holding
')
->orderBy('pkj_categories.name');
return $query->get()->toArray();
}
/**
* Get PKJ category metrics (KEUPAYAAN, KESIAGAAN, SERVISIBILITI)
*/
public function getCategoryMetrics(int $categoryId, array $filters = [], $user = null): array
{
// Get category name
$category = PKJCategory::find($categoryId);
$categoryName = $category ? $category->name : 'Unknown Category';
// Get entitlement and holding data for the category
$entitlementQuery = PKJAssetEntitlement::query()
->where('pkj_category_id', $categoryId);
// Apply visibility scoping if user is provided
if ($user) {
$entitlementQuery->visibleTo($user);
}
// Apply filters
if (isset($filters['unit_id'])) {
$entitlementQuery->where('unit_id', $filters['unit_id']);
}
$entitlementData = $entitlementQuery->selectRaw('
SUM(entitlement) as total_entitlement,
SUM(holding) as total_holding
')->first();
// Get operational/usable assets count (BDG, BT, BP statuses) for the category
$operationalQuery = PKJAssetHolding::query()
->where('pkj_category_id', $categoryId)
->whereIn('status', ['BDG', 'BT', 'BP']);
// Apply visibility scoping if user is provided
if ($user) {
$operationalQuery->visibleTo($user);
}
// Apply filters
if (isset($filters['unit_id'])) {
$operationalQuery->where('unit_id', $filters['unit_id']);
}
$operationalCount = $operationalQuery->count();
// Get BDG, BT, BG count specifically for servisibiliti calculation
$bdgQuery = PKJAssetHolding::query()
->where('pkj_category_id', $categoryId)
->whereIn('status', ['BDG', 'BT', 'BG']);
// Apply visibility scoping if user is provided
if ($user) {
$bdgQuery->visibleTo($user);
}
// Apply filters
if (isset($filters['unit_id'])) {
$bdgQuery->where('unit_id', $filters['unit_id']);
}
$bdgCount = $bdgQuery->count();
$totalEntitlement = $entitlementData ? $entitlementData->total_entitlement : 0;
$totalHolding = $entitlementData ? $entitlementData->total_holding : 0;
// Calculate metrics
$keupayaan = $totalEntitlement > 0 ? ($totalHolding / $totalEntitlement) * 100 : 0;
$kesiagaan = $totalEntitlement > 0 ? ($operationalCount / $totalEntitlement) * 100 : 0;
$servisibiliti = $totalHolding > 0 ? ($bdgCount / $totalHolding) * 100 : 0;
return [
'category_id' => $categoryId,
'category_name' => $categoryName,
'keupayaan' => round($keupayaan, 2),
'kesiagaan' => round($kesiagaan, 2),
'servisibiliti' => round($servisibiliti, 2),
'total_entitlement' => $totalEntitlement,
'total_holding' => $totalHolding,
'operational_count' => $operationalCount,
'bdg_count' => $bdgCount
];
}
/**
* Get PKJ category status breakdown
*/
public function getCategoryStatusBreakdown(int $categoryId, array $filters = [], $user = null): array
{
// Get category name
$category = PKJCategory::find($categoryId);
$categoryName = $category ? $category->name : 'Unknown Category';
$query = PKJAssetHolding::query()
->where('pkj_category_id', $categoryId)
->selectRaw('status, COUNT(*) as count')
->groupBy('status')
->orderBy('status');
// Apply visibility scoping if user is provided
if ($user) {
$query->visibleTo($user);
}
// Apply filters
if (isset($filters['unit_id'])) {
$query->where('unit_id', $filters['unit_id']);
}
$statusCounts = $query->get()->toArray();
return [
'category_id' => $categoryId,
'category_name' => $categoryName,
'status_breakdown' => $statusCounts
];
}
/**
* Get PKJ asset details by category and status.
*
* When the asset has no status yet, the status column in the database is NULL.
* Callers pass status as '' or the string 'null' to request these "no status" assets.
*/
public function getAssetDetailsByCategoryAndStatus(int $categoryId, string $status, array $filters = [], $user = null): array
{
// Handle no-status: API passes '' or 'null'; DB column is NULL for assets not yet assigned a status
$status = trim($status ?? '');
$query = PKJAssetHolding::query()
->with(['unit', 'pkjCategory', 'pkjSubcategory', 'pkjModel'])
->where('pkj_category_id', $categoryId);
if ($status === '' || $status === 'null') {
// Assets with status column NULL (not yet assigned a status)
$query->whereNull('status');
} else {
// For specific status, must match exactly and NOT be null
$query->whereNotNull('status')
->where('status', '=', $status);
}
$query->select([
'id',
'uuid',
'registration_number',
'kewpa_registration_number',
'status',
'unit_id',
'pkj_category_id',
'pkj_subcategory_id',
'pkj_model_id',
'purchase_date',
'receipt_date',
'purchase_price',
'contract_reference',
'country_of_manufacture',
'economic_year',
'economic_distance',
'note',
'engine_number',
'chassis_number',
'created_at',
'updated_at'
]);
// Apply visibility scoping if user is provided
if ($user) {
$query->visibleTo($user);
}
// Apply filters
if (isset($filters['unit_id'])) {
$query->where('unit_id', $filters['unit_id']);
}
$perPage = isset($filters['per_page']) ? (int) $filters['per_page'] : 10;
$page = isset($filters['page']) ? (int) $filters['page'] : null;
$assets = $query->paginate($perPage, ['*'], 'page', $page);
return [
'data' => $assets->items(),
'pagination' => [
'current_page' => $assets->currentPage(),
'per_page' => $assets->perPage(),
'total' => $assets->total(),
'last_page' => $assets->lastPage(),
'from' => $assets->firstItem(),
'to' => $assets->lastItem(),
'has_more_pages' => $assets->hasMorePages()
]
];
}
/**
* Apply common filters to query
*/
private function applyFilters($query, array $filters = [], bool $excludeDateFilter = false): void
{
// Only apply created_at filter if the model has created_at column
$model = $query->getModel();
if ($model && method_exists($model, 'getTable')) {
$schema = $model->getConnection()->getSchemaBuilder();
$columns = $schema->getColumnListing($model->getTable());
if (!$excludeDateFilter && in_array('created_at', $columns)) {
$dateRange = $this->getDateRange($filters);
$query->whereBetween('created_at', [$dateRange['from'], $dateRange['to']]);
}
}
// Apply unit_id filter if model has unit_id column
if (isset($filters['unit_id']) && $filters['unit_id']) {
if ($model && method_exists($model, 'getTable')) {
$columns = $schema->getColumnListing($model->getTable());
if (in_array('unit_id', $columns)) {
$query->where('unit_id', $filters['unit_id']);
}
}
}
// Apply government_id filter if provided
if (isset($filters['government_id']) && $filters['government_id']) {
// Get units under the government
$unitIds = $this->getUnitsByGovernment($filters['government_id']);
if ($model && method_exists($model, 'getTable')) {
$columns = $schema->getColumnListing($model->getTable());
if (in_array('unit_id', $columns)) {
if (!empty($unitIds)) {
$query->whereIn('unit_id', $unitIds);
} else {
// No units found for this government - return empty result
$query->whereNull('id');
}
}
}
}
// Apply formation_id filter if provided
if (isset($filters['formation_id']) && $filters['formation_id']) {
if ($model && method_exists($model, 'getTable')) {
$columns = $schema->getColumnListing($model->getTable());
if (in_array('unit_id', $columns)) {
// Get units under this formation
$unitIds = Unit::where('formation_id', $filters['formation_id'])
->pluck('id')
->toArray();
if (!empty($unitIds)) {
$query->whereIn('unit_id', $unitIds);
} else {
// No units found for this formation - return empty result
$query->whereNull('id');
}
}
}
}
// Apply camp_id filter if provided
if (isset($filters['camp_id']) && $filters['camp_id']) {
if ($model && method_exists($model, 'getTable')) {
$columns = $schema->getColumnListing($model->getTable());
if (in_array('camp_id', $columns)) {
$query->where('camp_id', $filters['camp_id']);
}
}
}
}
/**
* Get units by government ID
* Uses VisibilityService for consistent logic
*/
private function getUnitsByGovernment(int $governmentId): array
{
$governmentUnit = Unit::where('government_id', $governmentId)->first();
if (!$governmentUnit) {
// If no direct unit found, try to get from formation
$formation = \Modules\Formation\Entities\Formation::where('government_id', $governmentId)->first();
if ($formation) {
$governmentUnit = Unit::where('formation_id', $formation->id)->first();
}
}
if (!$governmentUnit) {
return [];
}
return $this->visibilityService->getUnitsUnderGovernment($governmentUnit);
}
/**
* Get date range for filtering
*/
private function getDateRange(array $filters = []): array
{
$dateFrom = $filters['date_from'] ?? Carbon::now()->startOfYear();
$dateTo = $filters['date_to'] ?? Carbon::now()->endOfYear();
return [
'from' => Carbon::parse($dateFrom)->startOfDay(),
'to' => Carbon::parse($dateTo)->endOfDay()
];
}
}