diff --git a/app/Http/Requests/Shipment/ShipmentStoreRequest.php b/app/Http/Requests/Shipment/ShipmentStoreRequest.php index a00fbc7..c384e41 100644 --- a/app/Http/Requests/Shipment/ShipmentStoreRequest.php +++ b/app/Http/Requests/Shipment/ShipmentStoreRequest.php @@ -21,7 +21,7 @@ public function rules(): array 'scheduled_date' => 'required|date', 'status' => 'nullable|in:scheduled,ready,shipping,completed', 'priority' => 'nullable|in:urgent,normal,low', - 'delivery_method' => 'nullable|in:pickup,direct,logistics', + 'delivery_method' => 'nullable|in:pickup,direct,logistics,direct_dispatch,loading,kyungdong_delivery,daesin_delivery,kyungdong_freight,daesin_freight,self_pickup', // 발주처/배송 정보 'client_id' => 'nullable|integer|exists:clients,id', @@ -55,6 +55,16 @@ public function rules(): array // 기타 'remarks' => 'nullable|string', + // 배차정보 + 'vehicle_dispatches' => 'nullable|array', + 'vehicle_dispatches.*.seq' => 'nullable|integer|min:1', + 'vehicle_dispatches.*.logistics_company' => 'nullable|string|max:100', + 'vehicle_dispatches.*.arrival_datetime' => 'nullable|date', + 'vehicle_dispatches.*.tonnage' => 'nullable|string|max:20', + 'vehicle_dispatches.*.vehicle_no' => 'nullable|string|max:20', + 'vehicle_dispatches.*.driver_contact' => 'nullable|string|max:50', + 'vehicle_dispatches.*.remarks' => 'nullable|string', + // 출하 품목 'items' => 'nullable|array', 'items.*.seq' => 'nullable|integer|min:1', diff --git a/app/Http/Requests/Shipment/ShipmentUpdateRequest.php b/app/Http/Requests/Shipment/ShipmentUpdateRequest.php index f86c4a2..43b4b14 100644 --- a/app/Http/Requests/Shipment/ShipmentUpdateRequest.php +++ b/app/Http/Requests/Shipment/ShipmentUpdateRequest.php @@ -19,7 +19,7 @@ public function rules(): array 'order_id' => 'nullable|integer|exists:orders,id', 'scheduled_date' => 'nullable|date', 'priority' => 'nullable|in:urgent,normal,low', - 'delivery_method' => 'nullable|in:pickup,direct,logistics', + 'delivery_method' => 'nullable|in:pickup,direct,logistics,direct_dispatch,loading,kyungdong_delivery,daesin_delivery,kyungdong_freight,daesin_freight,self_pickup', // 발주처/배송 정보 'client_id' => 'nullable|integer|exists:clients,id', @@ -53,6 +53,16 @@ public function rules(): array // 기타 'remarks' => 'nullable|string', + // 배차정보 + 'vehicle_dispatches' => 'nullable|array', + 'vehicle_dispatches.*.seq' => 'nullable|integer|min:1', + 'vehicle_dispatches.*.logistics_company' => 'nullable|string|max:100', + 'vehicle_dispatches.*.arrival_datetime' => 'nullable|date', + 'vehicle_dispatches.*.tonnage' => 'nullable|string|max:20', + 'vehicle_dispatches.*.vehicle_no' => 'nullable|string|max:20', + 'vehicle_dispatches.*.driver_contact' => 'nullable|string|max:50', + 'vehicle_dispatches.*.remarks' => 'nullable|string', + // 출하 품목 'items' => 'nullable|array', 'items.*.seq' => 'nullable|integer|min:1', diff --git a/app/Models/Tenants/Shipment.php b/app/Models/Tenants/Shipment.php index a02db8c..df96ff5 100644 --- a/app/Models/Tenants/Shipment.php +++ b/app/Models/Tenants/Shipment.php @@ -134,6 +134,14 @@ public function items(): HasMany return $this->hasMany(ShipmentItem::class)->orderBy('seq'); } + /** + * 배차정보 관계 + */ + public function vehicleDispatches(): HasMany + { + return $this->hasMany(ShipmentVehicleDispatch::class)->orderBy('seq'); + } + /** * 거래처 관계 */ diff --git a/app/Models/Tenants/ShipmentVehicleDispatch.php b/app/Models/Tenants/ShipmentVehicleDispatch.php new file mode 100644 index 0000000..7db88a0 --- /dev/null +++ b/app/Models/Tenants/ShipmentVehicleDispatch.php @@ -0,0 +1,50 @@ + 'integer', + 'shipment_id' => 'integer', + 'arrival_datetime' => 'datetime', + ]; + + /** + * 출하 관계 + */ + public function shipment(): BelongsTo + { + return $this->belongsTo(Shipment::class); + } + + /** + * 다음 순번 가져오기 + */ + public static function getNextSeq(int $shipmentId): int + { + $maxSeq = static::where('shipment_id', $shipmentId)->max('seq'); + + return ($maxSeq ?? 0) + 1; + } +} diff --git a/app/Services/ShipmentService.php b/app/Services/ShipmentService.php index 109b45d..8d42603 100644 --- a/app/Services/ShipmentService.php +++ b/app/Services/ShipmentService.php @@ -5,6 +5,7 @@ use App\Models\Orders\Order; use App\Models\Tenants\Shipment; use App\Models\Tenants\ShipmentItem; +use App\Models\Tenants\ShipmentVehicleDispatch; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Support\Facades\DB; @@ -19,7 +20,7 @@ public function index(array $params): LengthAwarePaginator $query = Shipment::query() ->where('tenant_id', $tenantId) - ->with(['items', 'order.client', 'order.writer', 'workOrder']); + ->with(['items', 'vehicleDispatches', 'order.client', 'order.writer', 'workOrder']); // 검색어 필터 if (! empty($params['search'])) { @@ -164,6 +165,7 @@ public function show(int $id): Shipment 'items' => function ($query) { $query->orderBy('seq'); }, + 'vehicleDispatches', 'order.client', 'order.writer', 'workOrder', @@ -228,7 +230,12 @@ public function store(array $data): Shipment $this->syncItems($shipment, $data['items'], $tenantId); } - return $shipment->load('items'); + // 배차정보 추가 + if (! empty($data['vehicle_dispatches'])) { + $this->syncDispatches($shipment, $data['vehicle_dispatches'], $tenantId); + } + + return $shipment->load(['items', 'vehicleDispatches']); }); } @@ -283,7 +290,12 @@ public function update(int $id, array $data): Shipment $this->syncItems($shipment, $data['items'], $tenantId); } - return $shipment->load('items'); + // 배차정보 동기화 + if (isset($data['vehicle_dispatches'])) { + $this->syncDispatches($shipment, $data['vehicle_dispatches'], $tenantId); + } + + return $shipment->load(['items', 'vehicleDispatches']); }); } @@ -340,7 +352,7 @@ public function updateStatus(int $id, string $status, ?array $additionalData = n // 연결된 수주(Order) 상태 동기화 $this->syncOrderStatus($shipment, $tenantId); - return $shipment->load('items'); + return $shipment->load(['items', 'vehicleDispatches']); } /** @@ -439,6 +451,9 @@ public function delete(int $id): bool // 품목 삭제 $shipment->items()->delete(); + // 배차정보 삭제 + $shipment->vehicleDispatches()->delete(); + // 출하 삭제 $shipment->update(['deleted_by' => $userId]); $shipment->delete(); @@ -477,6 +492,32 @@ protected function syncItems(Shipment $shipment, array $items, int $tenantId): v } } + /** + * 배차정보 동기화 + */ + protected function syncDispatches(Shipment $shipment, array $dispatches, int $tenantId): void + { + // 기존 배차정보 삭제 + $shipment->vehicleDispatches()->forceDelete(); + + // 새 배차정보 생성 + $seq = 1; + foreach ($dispatches as $dispatch) { + ShipmentVehicleDispatch::create([ + 'tenant_id' => $tenantId, + 'shipment_id' => $shipment->id, + 'seq' => $dispatch['seq'] ?? $seq, + 'logistics_company' => $dispatch['logistics_company'] ?? null, + 'arrival_datetime' => $dispatch['arrival_datetime'] ?? null, + 'tonnage' => $dispatch['tonnage'] ?? null, + 'vehicle_no' => $dispatch['vehicle_no'] ?? null, + 'driver_contact' => $dispatch['driver_contact'] ?? null, + 'remarks' => $dispatch['remarks'] ?? null, + ]); + $seq++; + } + } + /** * LOT 옵션 조회 (출고 가능한 LOT 목록) */ diff --git a/database/migrations/2026_03_04_100000_create_shipment_vehicle_dispatches_table.php b/database/migrations/2026_03_04_100000_create_shipment_vehicle_dispatches_table.php new file mode 100644 index 0000000..e1393b2 --- /dev/null +++ b/database/migrations/2026_03_04_100000_create_shipment_vehicle_dispatches_table.php @@ -0,0 +1,43 @@ +id(); + $table->foreignId('tenant_id')->comment('테넌트 ID'); + $table->foreignId('shipment_id')->comment('출하 ID'); + $table->integer('seq')->default(1)->comment('순번'); + $table->string('logistics_company', 100)->nullable()->comment('물류사'); + $table->datetime('arrival_datetime')->nullable()->comment('도착일시'); + $table->string('tonnage', 20)->nullable()->comment('톤수'); + $table->string('vehicle_no', 20)->nullable()->comment('차량번호'); + $table->string('driver_contact', 50)->nullable()->comment('운전자 연락처'); + $table->text('remarks')->nullable()->comment('비고'); + $table->timestamps(); + $table->softDeletes(); + + // 인덱스 + $table->index(['shipment_id', 'seq']); + + // 외래키 + $table->foreign('shipment_id')->references('id')->on('shipments')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('shipment_vehicle_dispatches'); + } +};