fix: [생산지시] 날짜포맷·개소수·자재투입 시 자동 상태전환

- ProductionOrderService: production_ordered_at를 Y-m-d 포맷으로 변환
- ProductionOrderService: withCount('nodes')로 개소수(node_count) 응답 추가
- WorkOrderService: autoStartWorkOrderOnMaterialInput() 신규 메서드
  - 자재투입 시 WO가 unassigned/pending/waiting이면 in_progress로 자동 전환
  - syncOrderStatus()로 Order도 IN_PRODUCTION 동기화
- Swagger: node_count 필드 문서화, 날짜 포맷 수정

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 17:27:35 +09:00
parent 2df8ecf765
commit 59d13eeb9f
3 changed files with 63 additions and 8 deletions

View File

@@ -29,7 +29,7 @@ public function index(array $params): LengthAwarePaginator
->where('tenant_id', $tenantId)
->whereIn('status_code', self::PRODUCTION_STATUSES)
->with(['client', 'workOrders.process', 'workOrders.assignees.user'])
->withCount('workOrders');
->withCount(['workOrders', 'nodes']);
// 검색어 필터
if (! empty($params['search'])) {
@@ -71,7 +71,13 @@ public function index(array $params): LengthAwarePaginator
// 가공 필드 추가
$result->getCollection()->transform(function (Order $order) {
$order->production_ordered_at = $order->workOrders->min('created_at');
$minCreatedAt = $order->workOrders->min('created_at');
$order->production_ordered_at = $minCreatedAt
? $minCreatedAt->format('Y-m-d')
: null;
// 개소수 (order_nodes 수)
$order->node_count = $order->nodes_count ?? 0;
$workOrders = $order->workOrders;
$order->work_order_progress = [
@@ -138,10 +144,14 @@ public function show(int $orderId): array
'workOrders.assignees.user',
'nodes',
])
->withCount('nodes')
->findOrFail($orderId);
// 생산지시일
$order->production_ordered_at = $order->workOrders->min('created_at');
// 생산지시일 (날짜만)
$minCreatedAt = $order->workOrders->min('created_at');
$order->production_ordered_at = $minCreatedAt
? $minCreatedAt->format('Y-m-d')
: null;
$order->production_status = $this->mapProductionStatus($order->status_code);
// WorkOrder 진행 현황
@@ -171,6 +181,7 @@ public function show(int $orderId): array
'order' => $order->makeHidden(['workOrders', 'nodes']),
'production_ordered_at' => $order->production_ordered_at,
'production_status' => $order->production_status,
'node_count' => $order->nodes_count ?? 0,
'work_order_progress' => $workOrderProgress,
'work_orders' => $workOrders,
'bom_process_groups' => $bomProcessGroups,

View File

@@ -850,6 +850,42 @@ private function syncOrderStatus(WorkOrder $workOrder, int $tenantId): void
);
}
/**
* 자재 투입 시 작업지시가 대기 상태이면 자동으로 진행중으로 전환
*
* pending/waiting 상태에서 첫 자재 투입이 발생하면
* 작업지시 → in_progress, 수주 → IN_PRODUCTION 으로 자동 전환
*/
private function autoStartWorkOrderOnMaterialInput(WorkOrder $workOrder, int $tenantId): void
{
// 아직 진행 전인 상태에서만 자동 전환 (자재투입 = 실질적 작업 시작)
if (! in_array($workOrder->status, [
WorkOrder::STATUS_UNASSIGNED,
WorkOrder::STATUS_PENDING,
WorkOrder::STATUS_WAITING,
])) {
return;
}
$oldStatus = $workOrder->status;
$workOrder->status = WorkOrder::STATUS_IN_PROGRESS;
$workOrder->updated_by = $this->apiUserId();
$workOrder->save();
// 감사 로그
$this->auditLogger->log(
$tenantId,
self::AUDIT_TARGET,
$workOrder->id,
'status_auto_changed_on_material_input',
['status' => $oldStatus],
['status' => WorkOrder::STATUS_IN_PROGRESS]
);
// 연결된 수주(Order) 상태 동기화 (IN_PROGRESS → IN_PRODUCTION)
$this->syncOrderStatus($workOrder, $tenantId);
}
/**
* 작업지시 품목에 결과 데이터 저장
*/
@@ -1458,6 +1494,9 @@ public function registerMaterialInput(int $workOrderId, array $inputs): array
$totalCount = array_sum(array_column($delegatedResults, 'material_count'));
$allResults = array_merge(...array_map(fn ($r) => $r['input_results'], $delegatedResults));
// 자재 투입 시 작업지시가 대기 상태면 자동으로 진행중으로 전환
$this->autoStartWorkOrderOnMaterialInput($workOrder, $tenantId);
return [
'work_order_id' => $workOrderId,
'material_count' => $totalCount,
@@ -1536,6 +1575,9 @@ public function registerMaterialInput(int $workOrderId, array $inputs): array
$allResults = array_merge($allResults, $dr['input_results']);
}
// 자재 투입 시 작업지시가 대기 상태면 자동으로 진행중으로 전환
$this->autoStartWorkOrderOnMaterialInput($workOrder, $tenantId);
return [
'work_order_id' => $workOrderId,
'material_count' => count($allResults),

View File

@@ -14,11 +14,12 @@
* @OA\Property(property="order_no", type="string", example="ORD-20260301-0001", description="수주번호 (= 생산지시번호)"),
* @OA\Property(property="site_name", type="string", example="서울현장", nullable=true, description="현장명"),
* @OA\Property(property="client_name", type="string", example="(주)고객사", nullable=true, description="거래처명"),
* @OA\Property(property="quantity", type="number", example=10, description="수량"),
* @OA\Property(property="quantity", type="number", example=232, description="부품수량 합계"),
* @OA\Property(property="node_count", type="integer", example=4, description="개소수 (order_nodes 수)"),
* @OA\Property(property="delivery_date", type="string", format="date", example="2026-03-15", nullable=true, description="납기일"),
* @OA\Property(property="production_ordered_at", type="string", format="date-time", nullable=true, description="생산지시일 (첫 WorkOrder 생성일)"),
* @OA\Property(property="production_ordered_at", type="string", format="date", example="2026-02-21", nullable=true, description="생산지시일 (첫 WorkOrder 생성일, Y-m-d)"),
* @OA\Property(property="production_status", type="string", enum={"waiting","in_production","completed"}, example="waiting", description="생산 상태"),
* @OA\Property(property="work_orders_count", type="integer", example=3, description="작업지시 수"),
* @OA\Property(property="work_orders_count", type="integer", example=2, description="작업지시 수 (공정별 1건)"),
* @OA\Property(property="work_order_progress", type="object",
* @OA\Property(property="total", type="integer", example=3),
* @OA\Property(property="completed", type="integer", example=1),
@@ -47,8 +48,9 @@
* description="생산지시 상세",
*
* @OA\Property(property="order", ref="#/components/schemas/ProductionOrderListItem"),
* @OA\Property(property="production_ordered_at", type="string", format="date-time", nullable=true),
* @OA\Property(property="production_ordered_at", type="string", format="date", example="2026-02-21", nullable=true),
* @OA\Property(property="production_status", type="string", enum={"waiting","in_production","completed"}),
* @OA\Property(property="node_count", type="integer", example=4, description="개소수"),
* @OA\Property(property="work_order_progress", type="object",
* @OA\Property(property="total", type="integer"),
* @OA\Property(property="completed", type="integer"),