refactor: [shipment] 배차 정보를 shipment_vehicle_dispatches로 일원화

- shipments 테이블에서 배차 관련 컬럼 8개 삭제 (vehicle_no, driver_name 등)
- shipping 전환 시 배차 정보를 vehicle_dispatches에 저장
- delivery_method ENUM → VARCHAR 변경 (common_codes 기반)
- VehicleDispatchService에 수주/작성자 관계 로딩 추가
- Swagger delivery_method enum 제약 제거

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-18 23:15:47 +09:00
parent 5e52293454
commit 6563d977ee
8 changed files with 87 additions and 73 deletions

View File

@@ -41,17 +41,6 @@ public function rules(): array
'loading_manager' => 'nullable|string|max:50',
'loading_time' => 'nullable|date',
// 물류/배차 정보
'logistics_company' => 'nullable|string|max:50',
'vehicle_tonnage' => 'nullable|string|max:20',
'shipping_cost' => 'nullable|numeric|min:0',
// 차량/운전자 정보
'vehicle_no' => 'nullable|string|max:20',
'driver_name' => 'nullable|string|max:50',
'driver_contact' => 'nullable|string|max:50',
'expected_arrival' => 'nullable|date',
// 기타
'remarks' => 'nullable|string',

View File

@@ -39,17 +39,6 @@ public function rules(): array
'loading_manager' => 'nullable|string|max:50',
'loading_time' => 'nullable|date',
// 물류/배차 정보
'logistics_company' => 'nullable|string|max:50',
'vehicle_tonnage' => 'nullable|string|max:20',
'shipping_cost' => 'nullable|numeric|min:0',
// 차량/운전자 정보
'vehicle_no' => 'nullable|string|max:20',
'driver_name' => 'nullable|string|max:50',
'driver_contact' => 'nullable|string|max:50',
'expected_arrival' => 'nullable|date',
// 기타
'remarks' => 'nullable|string',

View File

@@ -42,16 +42,6 @@ class Shipment extends Model
'loading_manager',
'loading_completed_at',
'loading_time',
// 물류/배차 정보
'logistics_company',
'vehicle_tonnage',
'shipping_cost',
// 차량/운전자 정보
'vehicle_no',
'driver_name',
'driver_contact',
'expected_arrival',
'confirmed_arrival',
// 기타
'remarks',
'created_by',
@@ -66,9 +56,6 @@ class Shipment extends Model
'invoice_issued' => 'boolean',
'loading_completed_at' => 'datetime',
'loading_time' => 'datetime',
'expected_arrival' => 'datetime',
'confirmed_arrival' => 'datetime',
'shipping_cost' => 'decimal:0',
'order_id' => 'integer',
'work_order_id' => 'integer',
'client_id' => 'integer',

View File

@@ -239,15 +239,6 @@ public function store(array $data): Shipment
// 상차 정보
'loading_manager' => $data['loading_manager'] ?? null,
'loading_time' => $data['loading_time'] ?? null,
// 물류/배차 정보
'logistics_company' => $data['logistics_company'] ?? null,
'vehicle_tonnage' => $data['vehicle_tonnage'] ?? null,
'shipping_cost' => $data['shipping_cost'] ?? null,
// 차량/운전자 정보
'vehicle_no' => $data['vehicle_no'] ?? null,
'driver_name' => $data['driver_name'] ?? null,
'driver_contact' => $data['driver_contact'] ?? null,
'expected_arrival' => $data['expected_arrival'] ?? null,
// 기타
'remarks' => $data['remarks'] ?? null,
'created_by' => $userId,
@@ -299,15 +290,6 @@ public function update(int $id, array $data): Shipment
// 상차 정보
'loading_manager' => $data['loading_manager'] ?? $shipment->loading_manager,
'loading_time' => $data['loading_time'] ?? $shipment->loading_time,
// 물류/배차 정보
'logistics_company' => $data['logistics_company'] ?? $shipment->logistics_company,
'vehicle_tonnage' => $data['vehicle_tonnage'] ?? $shipment->vehicle_tonnage,
'shipping_cost' => $data['shipping_cost'] ?? $shipment->shipping_cost,
// 차량/운전자 정보
'vehicle_no' => $data['vehicle_no'] ?? $shipment->vehicle_no,
'driver_name' => $data['driver_name'] ?? $shipment->driver_name,
'driver_contact' => $data['driver_contact'] ?? $shipment->driver_contact,
'expected_arrival' => $data['expected_arrival'] ?? $shipment->expected_arrival,
// 기타
'remarks' => $data['remarks'] ?? $shipment->remarks,
'updated_by' => $userId,
@@ -360,25 +342,26 @@ public function updateStatus(int $id, string $status, ?array $additionalData = n
} else {
$updateData['loading_completed_at'] = now();
}
if (isset($additionalData['vehicle_no'])) {
$updateData['vehicle_no'] = $additionalData['vehicle_no'];
}
if (isset($additionalData['driver_name'])) {
$updateData['driver_name'] = $additionalData['driver_name'];
}
if (isset($additionalData['driver_contact'])) {
$updateData['driver_contact'] = $additionalData['driver_contact'];
}
}
if ($status === 'completed' && isset($additionalData['confirmed_arrival'])) {
$updateData['confirmed_arrival'] = $additionalData['confirmed_arrival'];
}
$previousStatus = $shipment->status;
$shipment->update($updateData);
// shipping 전환 시 배차 정보를 shipment_vehicle_dispatches에 저장
if ($status === 'shipping' && $additionalData) {
$nextSeq = $shipment->vehicleDispatches()->max('seq') ?? 0;
ShipmentVehicleDispatch::create([
'tenant_id' => $tenantId,
'shipment_id' => $shipment->id,
'seq' => $nextSeq + 1,
'vehicle_no' => $additionalData['vehicle_no'] ?? null,
'driver_contact' => $additionalData['driver_contact'] ?? null,
'logistics_company' => $additionalData['logistics_company'] ?? null,
'tonnage' => $additionalData['vehicle_tonnage'] ?? null,
'arrival_datetime' => $additionalData['confirmed_arrival'] ?? null,
]);
}
// 재고 차감 비활성화: 수주생산은 재고 미경유, 선생산 완성품은 자재 투입 시 차감됨
// TODO: 선생산 로직 검증 후 재검토 (decreaseStockForShipment)

View File

@@ -16,7 +16,7 @@ public function index(array $params): LengthAwarePaginator
$query = ShipmentVehicleDispatch::query()
->where('tenant_id', $tenantId)
->with('shipment');
->with(['shipment.order.client', 'shipment.order.writer', 'shipment.creator']);
// 검색어 필터
if (! empty($params['search'])) {
@@ -98,7 +98,7 @@ public function show(int $id): ShipmentVehicleDispatch
return ShipmentVehicleDispatch::query()
->where('tenant_id', $tenantId)
->with('shipment')
->with(['shipment.order.client', 'shipment.order.writer', 'shipment.creator'])
->findOrFail($id);
}
@@ -114,7 +114,7 @@ public function update(int $id, array $data): ShipmentVehicleDispatch
->findOrFail($id);
// options에 저장할 필드 분리
$optionFields = ['freight_cost_type', 'supply_amount', 'vat', 'total_amount', 'status'];
$optionFields = ['freight_cost_type', 'supply_amount', 'vat', 'total_amount'];
$directFields = ['logistics_company', 'arrival_datetime', 'tonnage', 'vehicle_no', 'driver_contact', 'remarks'];
// 기존 options 유지하면서 업데이트

View File

@@ -20,7 +20,7 @@
* @OA\Property(property="status_label", type="string", example="출고예정", description="상태 라벨"),
* @OA\Property(property="priority", type="string", enum={"urgent","normal","low"}, example="normal", description="우선순위"),
* @OA\Property(property="priority_label", type="string", example="보통", description="우선순위 라벨"),
* @OA\Property(property="delivery_method", type="string", enum={"pickup","direct","logistics"}, example="pickup", description="배송방식"),
* @OA\Property(property="delivery_method", type="string", example="pickup", description="배송방식 (common_codes delivery_method 참조)"),
* @OA\Property(property="delivery_method_label", type="string", example="상차", description="배송방식 라벨"),
* @OA\Property(property="client_id", type="integer", nullable=true, description="거래처 ID"),
* @OA\Property(property="customer_name", type="string", example="(주)고객사", nullable=true, description="발주처명"),
@@ -147,7 +147,7 @@
* @OA\Property(property="scheduled_date", type="string", format="date", example="2025-12-26", description="출고예정일"),
* @OA\Property(property="status", type="string", enum={"scheduled","ready","shipping","completed"}, example="scheduled", description="상태"),
* @OA\Property(property="priority", type="string", enum={"urgent","normal","low"}, example="normal", description="우선순위"),
* @OA\Property(property="delivery_method", type="string", enum={"pickup","direct","logistics"}, example="pickup", description="배송방식"),
* @OA\Property(property="delivery_method", type="string", example="pickup", description="배송방식 (common_codes delivery_method 참조)"),
* @OA\Property(property="client_id", type="integer", description="거래처 ID"),
* @OA\Property(property="customer_name", type="string", example="(주)고객사", description="발주처명"),
* @OA\Property(property="site_name", type="string", example="서울현장", description="현장명"),
@@ -232,7 +232,7 @@ class ShipmentApi
* @OA\Parameter(name="search", in="query", description="검색어 (출하번호, LOT번호, 발주처명, 현장명)", @OA\Schema(type="string")),
* @OA\Parameter(name="status", in="query", description="상태", @OA\Schema(type="string", enum={"scheduled","ready","shipping","completed"})),
* @OA\Parameter(name="priority", in="query", description="우선순위", @OA\Schema(type="string", enum={"urgent","normal","low"})),
* @OA\Parameter(name="delivery_method", in="query", description="배송방식", @OA\Schema(type="string", enum={"pickup","direct","logistics"})),
* @OA\Parameter(name="delivery_method", in="query", description="배송방식 (common_codes delivery_method 참조)", @OA\Schema(type="string")),
* @OA\Parameter(name="scheduled_from", in="query", description="예정일 시작", @OA\Schema(type="string", format="date")),
* @OA\Parameter(name="scheduled_to", in="query", description="예정일 종료", @OA\Schema(type="string", format="date")),
* @OA\Parameter(name="can_ship", in="query", description="출하가능 여부", @OA\Schema(type="boolean")),

View File

@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* 배차 정보를 shipment_vehicle_dispatches 테이블로 일원화
* shipments 테이블의 배차 관련 컬럼 삭제
*/
public function up(): void
{
Schema::table('shipments', function (Blueprint $table) {
$table->dropColumn([
'logistics_company',
'vehicle_tonnage',
'shipping_cost',
'vehicle_no',
'driver_name',
'driver_contact',
'expected_arrival',
'confirmed_arrival',
]);
});
}
public function down(): void
{
Schema::table('shipments', function (Blueprint $table) {
$table->string('logistics_company', 50)->nullable()->after('loading_time');
$table->string('vehicle_tonnage', 20)->nullable()->after('logistics_company');
$table->decimal('shipping_cost', 10, 0)->nullable()->after('vehicle_tonnage');
$table->string('vehicle_no', 20)->nullable()->after('shipping_cost');
$table->string('driver_name', 50)->nullable()->after('vehicle_no');
$table->string('driver_contact', 50)->nullable()->after('driver_name');
$table->datetime('expected_arrival')->nullable()->after('driver_contact');
$table->datetime('confirmed_arrival')->nullable()->after('expected_arrival');
});
}
};

View File

@@ -0,0 +1,24 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
/**
* shipments.delivery_method: ENUM → VARCHAR 변경
*
* ENUM은 값 추가 시마다 마이그레이션이 필요하므로,
* common_codes 기반 VARCHAR로 변경하여 유연하게 관리
*/
return new class extends Migration
{
public function up(): void
{
DB::statement("ALTER TABLE `shipments` MODIFY `delivery_method` VARCHAR(30) NOT NULL DEFAULT 'pickup' COMMENT '배송방식 (common_codes delivery_method 참조)'");
}
public function down(): void
{
DB::statement("UPDATE `shipments` SET `delivery_method` = 'pickup' WHERE `delivery_method` NOT IN ('pickup', 'direct', 'logistics')");
DB::statement("ALTER TABLE `shipments` MODIFY `delivery_method` ENUM('pickup', 'direct', 'logistics') NOT NULL DEFAULT 'pickup' COMMENT '배송방식: pickup=상차, direct=직접배차, logistics=물류사'");
}
};