280 lines
8.1 KiB
PHP
280 lines
8.1 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Services;
|
||
|
|
|
||
|
|
use App\Models\Production\WorkOrder;
|
||
|
|
use App\Models\Production\WorkResult;
|
||
|
|
use Illuminate\Support\Facades\DB;
|
||
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||
|
|
|
||
|
|
class WorkResultService extends Service
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* 목록 조회 (검색/필터링/페이징)
|
||
|
|
*/
|
||
|
|
public function index(array $params)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
|
||
|
|
$page = (int) ($params['page'] ?? 1);
|
||
|
|
$size = (int) ($params['size'] ?? 20);
|
||
|
|
$q = trim((string) ($params['q'] ?? ''));
|
||
|
|
$processType = $params['process_type'] ?? null;
|
||
|
|
$workOrderId = $params['work_order_id'] ?? null;
|
||
|
|
$workerId = $params['worker_id'] ?? null;
|
||
|
|
$workDateFrom = $params['work_date_from'] ?? null;
|
||
|
|
$workDateTo = $params['work_date_to'] ?? null;
|
||
|
|
$isInspected = isset($params['is_inspected']) ? filter_var($params['is_inspected'], FILTER_VALIDATE_BOOLEAN) : null;
|
||
|
|
$isPackaged = isset($params['is_packaged']) ? filter_var($params['is_packaged'], FILTER_VALIDATE_BOOLEAN) : null;
|
||
|
|
|
||
|
|
$query = WorkResult::query()
|
||
|
|
->where('tenant_id', $tenantId)
|
||
|
|
->with([
|
||
|
|
'workOrder:id,work_order_no',
|
||
|
|
'worker:id,name',
|
||
|
|
]);
|
||
|
|
|
||
|
|
// 검색어
|
||
|
|
if ($q !== '') {
|
||
|
|
$query->where(function ($qq) use ($q) {
|
||
|
|
$qq->where('lot_no', 'like', "%{$q}%")
|
||
|
|
->orWhere('product_name', 'like', "%{$q}%")
|
||
|
|
->orWhereHas('workOrder', fn ($wo) => $wo->where('work_order_no', 'like', "%{$q}%"));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 공정유형 필터
|
||
|
|
if ($processType !== null) {
|
||
|
|
$query->where('process_type', $processType);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 작업지시 필터
|
||
|
|
if ($workOrderId !== null) {
|
||
|
|
$query->where('work_order_id', $workOrderId);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 작업자 필터
|
||
|
|
if ($workerId !== null) {
|
||
|
|
$query->where('worker_id', $workerId);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 작업일 범위
|
||
|
|
if ($workDateFrom !== null) {
|
||
|
|
$query->where('work_date', '>=', $workDateFrom);
|
||
|
|
}
|
||
|
|
if ($workDateTo !== null) {
|
||
|
|
$query->where('work_date', '<=', $workDateTo);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 검사 완료 필터
|
||
|
|
if ($isInspected !== null) {
|
||
|
|
$query->where('is_inspected', $isInspected);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 포장 완료 필터
|
||
|
|
if ($isPackaged !== null) {
|
||
|
|
$query->where('is_packaged', $isPackaged);
|
||
|
|
}
|
||
|
|
|
||
|
|
$query->orderByDesc('work_date')->orderByDesc('created_at');
|
||
|
|
|
||
|
|
return $query->paginate($size, ['*'], 'page', $page);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 통계 조회
|
||
|
|
*/
|
||
|
|
public function stats(array $params = []): array
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
|
||
|
|
$workDateFrom = $params['work_date_from'] ?? null;
|
||
|
|
$workDateTo = $params['work_date_to'] ?? null;
|
||
|
|
$processType = $params['process_type'] ?? null;
|
||
|
|
|
||
|
|
$query = WorkResult::where('tenant_id', $tenantId);
|
||
|
|
|
||
|
|
// 작업일 범위
|
||
|
|
if ($workDateFrom !== null) {
|
||
|
|
$query->where('work_date', '>=', $workDateFrom);
|
||
|
|
}
|
||
|
|
if ($workDateTo !== null) {
|
||
|
|
$query->where('work_date', '<=', $workDateTo);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 공정유형 필터
|
||
|
|
if ($processType !== null) {
|
||
|
|
$query->where('process_type', $processType);
|
||
|
|
}
|
||
|
|
|
||
|
|
$totals = $query->select([
|
||
|
|
DB::raw('SUM(production_qty) as total_production'),
|
||
|
|
DB::raw('SUM(good_qty) as total_good'),
|
||
|
|
DB::raw('SUM(defect_qty) as total_defect'),
|
||
|
|
])->first();
|
||
|
|
|
||
|
|
$totalProduction = (int) ($totals->total_production ?? 0);
|
||
|
|
$totalGood = (int) ($totals->total_good ?? 0);
|
||
|
|
$totalDefect = (int) ($totals->total_defect ?? 0);
|
||
|
|
|
||
|
|
$defectRate = $totalProduction > 0
|
||
|
|
? round(($totalDefect / $totalProduction) * 100, 1)
|
||
|
|
: 0;
|
||
|
|
|
||
|
|
return [
|
||
|
|
'total_production' => $totalProduction,
|
||
|
|
'total_good' => $totalGood,
|
||
|
|
'total_defect' => $totalDefect,
|
||
|
|
'defect_rate' => $defectRate,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 단건 조회
|
||
|
|
*/
|
||
|
|
public function show(int $id)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
|
||
|
|
$workResult = WorkResult::where('tenant_id', $tenantId)
|
||
|
|
->with([
|
||
|
|
'workOrder:id,work_order_no,project_name,status',
|
||
|
|
'workOrderItem:id,item_name,specification,quantity',
|
||
|
|
'worker:id,name',
|
||
|
|
])
|
||
|
|
->find($id);
|
||
|
|
|
||
|
|
if (! $workResult) {
|
||
|
|
throw new NotFoundHttpException(__('error.not_found'));
|
||
|
|
}
|
||
|
|
|
||
|
|
return $workResult;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 생성
|
||
|
|
*/
|
||
|
|
public function store(array $data)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
$userId = $this->apiUserId();
|
||
|
|
|
||
|
|
return DB::transaction(function () use ($data, $tenantId, $userId) {
|
||
|
|
$data['tenant_id'] = $tenantId;
|
||
|
|
$data['created_by'] = $userId;
|
||
|
|
$data['updated_by'] = $userId;
|
||
|
|
|
||
|
|
// 양품수량 자동 계산 (입력 안 된 경우)
|
||
|
|
if (! isset($data['good_qty'])) {
|
||
|
|
$data['good_qty'] = max(0, ($data['production_qty'] ?? 0) - ($data['defect_qty'] ?? 0));
|
||
|
|
}
|
||
|
|
|
||
|
|
// 작업지시 정보로 자동 채움
|
||
|
|
if (! empty($data['work_order_id'])) {
|
||
|
|
$workOrder = WorkOrder::find($data['work_order_id']);
|
||
|
|
if ($workOrder) {
|
||
|
|
$data['process_type'] = $data['process_type'] ?? $workOrder->process_type;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return WorkResult::create($data);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 수정
|
||
|
|
*/
|
||
|
|
public function update(int $id, array $data)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
$userId = $this->apiUserId();
|
||
|
|
|
||
|
|
$workResult = WorkResult::where('tenant_id', $tenantId)->find($id);
|
||
|
|
|
||
|
|
if (! $workResult) {
|
||
|
|
throw new NotFoundHttpException(__('error.not_found'));
|
||
|
|
}
|
||
|
|
|
||
|
|
return DB::transaction(function () use ($workResult, $data, $userId) {
|
||
|
|
$data['updated_by'] = $userId;
|
||
|
|
|
||
|
|
// 양품수량 재계산 (생산수량 또는 불량수량 변경 시)
|
||
|
|
if (isset($data['production_qty']) || isset($data['defect_qty'])) {
|
||
|
|
$productionQty = $data['production_qty'] ?? $workResult->production_qty;
|
||
|
|
$defectQty = $data['defect_qty'] ?? $workResult->defect_qty;
|
||
|
|
|
||
|
|
if (! isset($data['good_qty'])) {
|
||
|
|
$data['good_qty'] = max(0, $productionQty - $defectQty);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$workResult->update($data);
|
||
|
|
|
||
|
|
return $workResult->fresh([
|
||
|
|
'workOrder:id,work_order_no',
|
||
|
|
'worker:id,name',
|
||
|
|
]);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 삭제
|
||
|
|
*/
|
||
|
|
public function destroy(int $id): void
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
|
||
|
|
$workResult = WorkResult::where('tenant_id', $tenantId)->find($id);
|
||
|
|
|
||
|
|
if (! $workResult) {
|
||
|
|
throw new NotFoundHttpException(__('error.not_found'));
|
||
|
|
}
|
||
|
|
|
||
|
|
$workResult->delete();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 검사 상태 토글
|
||
|
|
*/
|
||
|
|
public function toggleInspection(int $id)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
$userId = $this->apiUserId();
|
||
|
|
|
||
|
|
$workResult = WorkResult::where('tenant_id', $tenantId)->find($id);
|
||
|
|
|
||
|
|
if (! $workResult) {
|
||
|
|
throw new NotFoundHttpException(__('error.not_found'));
|
||
|
|
}
|
||
|
|
|
||
|
|
$workResult->update([
|
||
|
|
'is_inspected' => ! $workResult->is_inspected,
|
||
|
|
'updated_by' => $userId,
|
||
|
|
]);
|
||
|
|
|
||
|
|
return $workResult->fresh();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 포장 상태 토글
|
||
|
|
*/
|
||
|
|
public function togglePackaging(int $id)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
$userId = $this->apiUserId();
|
||
|
|
|
||
|
|
$workResult = WorkResult::where('tenant_id', $tenantId)->find($id);
|
||
|
|
|
||
|
|
if (! $workResult) {
|
||
|
|
throw new NotFoundHttpException(__('error.not_found'));
|
||
|
|
}
|
||
|
|
|
||
|
|
$workResult->update([
|
||
|
|
'is_packaged' => ! $workResult->is_packaged,
|
||
|
|
'updated_by' => $userId,
|
||
|
|
]);
|
||
|
|
|
||
|
|
return $workResult->fresh();
|
||
|
|
}
|
||
|
|
}
|