'required|array|min:1|max:100', // 각 품목별 필수 입력 'items.*.finished_goods_code' => 'required|string|max:50', // React 필드명 (camelCase) - 우선 적용 'items.*.openWidth' => 'nullable|numeric|min:100|max:20000', 'items.*.openHeight' => 'nullable|numeric|min:100|max:20000', 'items.*.quantity' => 'nullable|integer|min:1', 'items.*.productCategory' => 'nullable|string|in:SCREEN,STEEL', 'items.*.guideRailType' => 'nullable|string|in:wall,ceiling,floor,mixed', 'items.*.motorPower' => 'nullable|string|in:single,three', 'items.*.controller' => 'nullable|string|in:basic,smart,premium', 'items.*.wingSize' => 'nullable|numeric|min:0|max:500', 'items.*.inspectionFee' => 'nullable|numeric|min:0', // API 변수명 (약어) - React 필드명이 없을 때 사용 'items.*.W0' => 'nullable|numeric|min:100|max:20000', 'items.*.H0' => 'nullable|numeric|min:100|max:20000', 'items.*.QTY' => 'nullable|integer|min:1', 'items.*.PC' => 'nullable|string|in:SCREEN,STEEL', 'items.*.GT' => 'nullable|string|in:wall,ceiling,floor,mixed', 'items.*.MP' => 'nullable|string|in:single,three', 'items.*.CT' => 'nullable|string|in:basic,smart,premium', 'items.*.WS' => 'nullable|numeric|min:0|max:500', 'items.*.INSP' => 'nullable|numeric|min:0', // 디버그 모드 (개발용) 'debug' => 'nullable|boolean', ]; } public function attributes(): array { return [ 'items' => __('validation.attributes.items'), 'items.*.finished_goods_code' => __('validation.attributes.finished_goods_code'), 'items.*.openWidth' => __('validation.attributes.open_width'), 'items.*.openHeight' => __('validation.attributes.open_height'), 'items.*.quantity' => __('validation.attributes.quantity'), 'items.*.productCategory' => __('validation.attributes.product_category'), 'items.*.guideRailType' => __('validation.attributes.guide_rail_type'), 'items.*.motorPower' => __('validation.attributes.motor_power'), 'items.*.controller' => __('validation.attributes.controller'), 'items.*.wingSize' => __('validation.attributes.wing_size'), 'items.*.inspectionFee' => __('validation.attributes.inspection_fee'), ]; } public function messages(): array { return [ 'items.required' => __('error.items_required'), 'items.min' => __('error.items_min'), 'items.max' => __('error.items_max'), 'items.*.finished_goods_code.required' => __('error.finished_goods_code_required'), ]; } /** * 입력 품목 배열 반환 (FormulaEvaluatorService용) * * React 필드명(camelCase)과 API 변수명(약어) 모두 지원 * React 필드명 우선, 없으면 API 변수명 사용 * * @return array */ public function getInputItems(): array { $validated = $this->validated(); $result = []; foreach ($validated['items'] as $index => $item) { $result[] = [ 'index' => $index, 'finished_goods_code' => $item['finished_goods_code'], 'inputs' => $this->normalizeInputVariables($item), ]; } return $result; } /** * 단일 품목의 입력 변수 정규화 * * React 필드명 → API 변수명 매핑 * - openWidth → W0 * - openHeight → H0 * - quantity → QTY * - productCategory → PC * - guideRailType → GT * - motorPower → MP * - controller → CT * - wingSize → WS * - inspectionFee → INSP */ private function normalizeInputVariables(array $item): array { return [ 'W0' => (float) ($item['openWidth'] ?? $item['W0'] ?? 0), 'H0' => (float) ($item['openHeight'] ?? $item['H0'] ?? 0), 'QTY' => (int) ($item['quantity'] ?? $item['QTY'] ?? 1), 'PC' => $item['productCategory'] ?? $item['PC'] ?? 'SCREEN', 'GT' => $item['guideRailType'] ?? $item['GT'] ?? 'wall', 'MP' => $item['motorPower'] ?? $item['MP'] ?? 'single', 'CT' => $item['controller'] ?? $item['CT'] ?? 'basic', 'WS' => (float) ($item['wingSize'] ?? $item['WS'] ?? 50), 'INSP' => (float) ($item['inspectionFee'] ?? $item['INSP'] ?? 50000), ]; } /** * 필수 입력 검증 (W0, H0) * * React 필드명이든 API 변수명이든 둘 중 하나는 있어야 함 */ public function withValidator($validator): void { $validator->after(function ($validator) { $items = $this->input('items', []); foreach ($items as $index => $item) { // W0 (openWidth 또는 W0) 검증 $w0 = $item['openWidth'] ?? $item['W0'] ?? null; if ($w0 === null || $w0 === '') { $validator->errors()->add( "items.{$index}.openWidth", __('error.open_width_required') ); } // H0 (openHeight 또는 H0) 검증 $h0 = $item['openHeight'] ?? $item['H0'] ?? null; if ($h0 === null || $h0 === '') { $validator->errors()->add( "items.{$index}.openHeight", __('error.open_height_required') ); } } }); } }