From 0863afc8d00ea5151f5c073a0059a5b171f3882f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Tue, 17 Mar 2026 13:55:44 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[items]=20=ED=92=88=EB=AA=A9=20=EA=B7=9C?= =?UTF-8?q?=EA=B2=A9=20accessor=20+=20=EA=B0=90=EC=82=AC=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=20+=20bom=5Fcategory=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Item 모델에 specification accessor 추가 (attributes.spec 조회) - ItemService.update()에 AuditLogger 감사 로그 추가 - items.options에 bom_category 추가 마이그레이션 --- .../Controllers/Api/V1/ItemsController.php | 1 + app/Models/Items/Item.php | 18 ++++ app/Services/ItemService.php | 23 ++++- ...5842_add_bom_category_to_items_options.php | 90 +++++++++++++++++++ 4 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2026_03_16_185842_add_bom_category_to_items_options.php diff --git a/app/Http/Controllers/Api/V1/ItemsController.php b/app/Http/Controllers/Api/V1/ItemsController.php index e9931c63..40703f3e 100644 --- a/app/Http/Controllers/Api/V1/ItemsController.php +++ b/app/Http/Controllers/Api/V1/ItemsController.php @@ -28,6 +28,7 @@ public function index(Request $request) 'category_id' => $request->input('category_id'), 'item_type' => $request->input('type') ?? $request->input('item_type'), 'item_category' => $request->input('item_category'), + 'bom_category' => $request->input('bom_category'), 'group_id' => $request->input('group_id'), 'active' => $request->input('is_active') ?? $request->input('active'), 'has_bom' => $request->input('has_bom'), diff --git a/app/Models/Items/Item.php b/app/Models/Items/Item.php index 5a4e7b48..6e1fcb2b 100644 --- a/app/Models/Items/Item.php +++ b/app/Models/Items/Item.php @@ -51,6 +51,24 @@ class Item extends Model 'deleted_at', ]; + protected $appends = [ + 'specification', + ]; + + /** + * 규격 accessor — attributes JSON 내 spec/specification 값을 최상위 필드로 노출 + */ + public function getSpecificationAttribute(): ?string + { + $attrs = $this->getAttributeValue('attributes'); + + if (is_array($attrs)) { + return $attrs['spec'] ?? $attrs['specification'] ?? null; + } + + return null; + } + /** * item_type 상수 */ diff --git a/app/Services/ItemService.php b/app/Services/ItemService.php index 4e746191..28dfd1de 100644 --- a/app/Services/ItemService.php +++ b/app/Services/ItemService.php @@ -357,6 +357,7 @@ public function index(array $params): LengthAwarePaginator $categoryId = $params['category_id'] ?? null; $itemType = $params['item_type'] ?? null; $itemCategory = $params['item_category'] ?? null; + $bomCategory = $params['bom_category'] ?? null; $groupId = $params['group_id'] ?? null; $active = $params['active'] ?? null; $hasBom = $params['has_bom'] ?? null; @@ -410,6 +411,11 @@ public function index(array $params): LengthAwarePaginator $query->where('item_category', $itemCategory); } + // BOM 카테고리 (options->bom_category) + if ($bomCategory) { + $query->where('options->bom_category', $bomCategory); + } + // 활성 상태 if ($active !== null && $active !== '') { $query->where('is_active', (bool) $active); @@ -743,6 +749,9 @@ public function update(int $id, array $data): Model $data['attributes'] = array_merge($existingAttributes, $data['attributes']); } + // 변경 전 스냅샷 (감사 로그용) + $before = $item->toArray(); + // 테이블 업데이트 $itemData = array_intersect_key($data, array_flip([ 'item_type', 'code', 'name', 'unit', 'category_id', @@ -768,7 +777,19 @@ public function update(int $id, array $data): Model $item->load('details'); } - return $item->refresh(); + $item->refresh(); + + // 감사 로그 + app(\App\Services\Audit\AuditLogger::class)->log( + tenantId: $tenantId, + targetType: 'item', + targetId: $item->id, + action: 'updated', + before: $before, + after: $item->toArray() + ); + + return $item; } /** diff --git a/database/migrations/2026_03_16_185842_add_bom_category_to_items_options.php b/database/migrations/2026_03_16_185842_add_bom_category_to_items_options.php new file mode 100644 index 00000000..caf3dcf1 --- /dev/null +++ b/database/migrations/2026_03_16_185842_add_bom_category_to_items_options.php @@ -0,0 +1,90 @@ +where('code', 'like', 'EST-RAW-%') + ->update([ + 'options' => DB::raw("JSON_SET(COALESCE(options, '{}'), '$.bom_category', 'material')"), + ]); + + // 2. material: RM (원자재) 타입 + DB::table('items') + ->where('item_type', 'RM') + ->update([ + 'options' => DB::raw("JSON_SET(COALESCE(options, '{}'), '$.bom_category', 'material')"), + ]); + + // 3. motor: EST-MOTOR-* 코드 + DB::table('items') + ->where('code', 'like', 'EST-MOTOR-%') + ->update([ + 'options' => DB::raw("JSON_SET(COALESCE(options, '{}'), '$.bom_category', 'motor')"), + ]); + + // 4. controller: EST-CTRL-* 코드 + DB::table('items') + ->where('code', 'like', 'EST-CTRL-%') + ->update([ + 'options' => DB::raw("JSON_SET(COALESCE(options, '{}'), '$.bom_category', 'controller')"), + ]); + + // 5. steel: item_category=BENDING (BD-* 코드) + DB::table('items') + ->where('item_category', 'BENDING') + ->update([ + 'options' => DB::raw("JSON_SET(COALESCE(options, '{}'), '$.bom_category', 'steel')"), + ]); + + // 6. inspection: EST-INSPECTION 코드 + DB::table('items') + ->where('code', 'EST-INSPECTION') + ->update([ + 'options' => DB::raw("JSON_SET(COALESCE(options, '{}'), '$.bom_category', 'inspection')"), + ]); + + // 7. inspection: CS (소모품) 타입 + DB::table('items') + ->where('item_type', 'CS') + ->update([ + 'options' => DB::raw("JSON_SET(COALESCE(options, '{}'), '$.bom_category', 'inspection')"), + ]); + + // 8. parts: 나머지 PT/SM (아직 bom_category가 없는 것) + DB::table('items') + ->whereIn('item_type', ['PT', 'SM']) + ->whereRaw("(options IS NULL OR JSON_EXTRACT(options, '$.bom_category') IS NULL)") + ->update([ + 'options' => DB::raw("JSON_SET(COALESCE(options, '{}'), '$.bom_category', 'parts')"), + ]); + } + + public function down(): void + { + DB::table('items') + ->whereRaw("JSON_EXTRACT(options, '$.bom_category') IS NOT NULL") + ->update([ + 'options' => DB::raw("JSON_REMOVE(options, '$.bom_category')"), + ]); + } +};