feat: field_key 시스템 필드 예약어 검증 추가
- SystemFields 상수 클래스 생성 (app/Constants/) - source_table 기반 테이블별 예약어 관리 - products/materials 테이블 고정 컬럼 정의 - 공통 시스템 컬럼 포함 - ItemFieldService 수정 - validateFieldKeyUnique에 시스템 필드 검증 추가 - source_table 미지정시 그룹 전체 예약어 체크 (안전 모드) - 에러 메시지 추가 (error.field_key_reserved) - 작업 문서 추가 (docs/specs/item-master-field-key-validation.md)
This commit is contained in:
144
app/Constants/SystemFields.php
Normal file
144
app/Constants/SystemFields.php
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 테이블별 시스템 필드(고정 컬럼) 예약어 관리
|
||||||
|
*
|
||||||
|
* field_key 중복 검증 시 해당 테이블의 고정 컬럼과 충돌을 방지합니다.
|
||||||
|
* source_table (products, materials) 기반으로 대상 테이블을 결정합니다.
|
||||||
|
*/
|
||||||
|
class SystemFields
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 소스 테이블 상수
|
||||||
|
*/
|
||||||
|
public const SOURCE_TABLE_PRODUCTS = 'products';
|
||||||
|
|
||||||
|
public const SOURCE_TABLE_MATERIALS = 'materials';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 그룹 ID 상수
|
||||||
|
*/
|
||||||
|
public const GROUP_ITEM_MASTER = 1; // 품목관리
|
||||||
|
|
||||||
|
/**
|
||||||
|
* products 테이블 고정 컬럼
|
||||||
|
*/
|
||||||
|
public const PRODUCTS = [
|
||||||
|
// 기본 정보
|
||||||
|
'code',
|
||||||
|
'name',
|
||||||
|
'unit',
|
||||||
|
'category_id',
|
||||||
|
'product_type',
|
||||||
|
'description',
|
||||||
|
// 플래그
|
||||||
|
'is_sellable',
|
||||||
|
'is_purchasable',
|
||||||
|
'is_producible',
|
||||||
|
'is_variable_size',
|
||||||
|
'is_active',
|
||||||
|
// 하이브리드 고정 필드
|
||||||
|
'safety_stock',
|
||||||
|
'lead_time',
|
||||||
|
'product_category',
|
||||||
|
'part_type',
|
||||||
|
// 파일 관련
|
||||||
|
'bending_diagram',
|
||||||
|
'bending_details',
|
||||||
|
'specification_file',
|
||||||
|
'specification_file_name',
|
||||||
|
'certification_file',
|
||||||
|
'certification_file_name',
|
||||||
|
'certification_number',
|
||||||
|
'certification_start_date',
|
||||||
|
'certification_end_date',
|
||||||
|
// JSON 필드
|
||||||
|
'attributes',
|
||||||
|
'attributes_archive',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* materials 테이블 고정 컬럼
|
||||||
|
*/
|
||||||
|
public const MATERIALS = [
|
||||||
|
// 기본 정보
|
||||||
|
'name',
|
||||||
|
'item_name',
|
||||||
|
'specification',
|
||||||
|
'material_code',
|
||||||
|
'material_type',
|
||||||
|
'unit',
|
||||||
|
'category_id',
|
||||||
|
// 플래그
|
||||||
|
'is_inspection',
|
||||||
|
'is_active',
|
||||||
|
// 기타
|
||||||
|
'search_tag',
|
||||||
|
'remarks',
|
||||||
|
// JSON 필드
|
||||||
|
'attributes',
|
||||||
|
'options',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 공통 시스템 컬럼 (모든 테이블에 적용)
|
||||||
|
*/
|
||||||
|
public const COMMON = [
|
||||||
|
'id',
|
||||||
|
'tenant_id',
|
||||||
|
'created_by',
|
||||||
|
'updated_by',
|
||||||
|
'deleted_by',
|
||||||
|
'created_at',
|
||||||
|
'updated_at',
|
||||||
|
'deleted_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* source_table 기반 예약어 목록 조회
|
||||||
|
*/
|
||||||
|
public static function getReservedKeys(string $sourceTable): array
|
||||||
|
{
|
||||||
|
$tableFields = match ($sourceTable) {
|
||||||
|
self::SOURCE_TABLE_PRODUCTS => self::PRODUCTS,
|
||||||
|
self::SOURCE_TABLE_MATERIALS => self::MATERIALS,
|
||||||
|
default => []
|
||||||
|
};
|
||||||
|
|
||||||
|
// 공통 시스템 컬럼 병합
|
||||||
|
return array_unique(array_merge(self::COMMON, $tableFields));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 특정 field_key가 시스템 필드인지 확인
|
||||||
|
*/
|
||||||
|
public static function isReserved(string $fieldKey, string $sourceTable): bool
|
||||||
|
{
|
||||||
|
return in_array($fieldKey, self::getReservedKeys($sourceTable), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* group_id에 속한 모든 테이블의 예약어 목록 조회
|
||||||
|
* (item_type을 모를 때 안전하게 모든 예약어 체크)
|
||||||
|
*/
|
||||||
|
public static function getAllReservedKeysForGroup(int $groupId): array
|
||||||
|
{
|
||||||
|
$allFields = self::COMMON;
|
||||||
|
|
||||||
|
if ($groupId === self::GROUP_ITEM_MASTER) {
|
||||||
|
$allFields = array_merge($allFields, self::PRODUCTS, self::MATERIALS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_unique($allFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 특정 field_key가 그룹 내 어떤 테이블에서든 시스템 필드인지 확인
|
||||||
|
*/
|
||||||
|
public static function isReservedInGroup(string $fieldKey, int $groupId): bool
|
||||||
|
{
|
||||||
|
return in_array($fieldKey, self::getAllReservedKeysForGroup($groupId), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
namespace App\Services\ItemMaster;
|
namespace App\Services\ItemMaster;
|
||||||
|
|
||||||
|
use App\Constants\SystemFields;
|
||||||
use App\Models\ItemMaster\EntityRelationship;
|
use App\Models\ItemMaster\EntityRelationship;
|
||||||
use App\Models\ItemMaster\ItemField;
|
use App\Models\ItemMaster\ItemField;
|
||||||
use App\Services\Service;
|
use App\Services\Service;
|
||||||
use App\Traits\LockCheckTrait;
|
use App\Traits\LockCheckTrait;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
|
||||||
class ItemFieldService extends Service
|
class ItemFieldService extends Service
|
||||||
@@ -61,14 +63,64 @@ public function storeIndependent(array $data): ItemField
|
|||||||
'created_by' => $userId,
|
'created_by' => $userId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 2. field_key 설정 ({ID}_{field_key} 형식)
|
// 2. field_key 설정 (시스템 필드 + 중복 검증 후 저장)
|
||||||
if (! empty($data['field_key'])) {
|
if (! empty($data['field_key'])) {
|
||||||
$field->update(['field_key' => $field->id.'_'.$data['field_key']]);
|
$sourceTable = $data['source_table'] ?? null;
|
||||||
|
$groupId = $data['group_id'] ?? 1;
|
||||||
|
$this->validateFieldKeyUnique($data['field_key'], $tenantId, $sourceTable, $groupId);
|
||||||
|
$field->update(['field_key' => $data['field_key']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $field;
|
return $field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* field_key 유효성 검증 (시스템 필드 + 중복 체크)
|
||||||
|
*
|
||||||
|
* @param string|null $sourceTable 소스 테이블 (products, materials), null이면 그룹 전체 예약어 체크
|
||||||
|
* @param int $groupId 그룹 ID (1=품목관리), sourceTable이 null일 때 사용
|
||||||
|
*
|
||||||
|
* @throws ValidationException
|
||||||
|
*/
|
||||||
|
private function validateFieldKeyUnique(
|
||||||
|
string $fieldKey,
|
||||||
|
int $tenantId,
|
||||||
|
?string $sourceTable = null,
|
||||||
|
int $groupId = 1,
|
||||||
|
?int $excludeId = null
|
||||||
|
): void {
|
||||||
|
// 1. 시스템 필드(예약어) 체크
|
||||||
|
if ($sourceTable) {
|
||||||
|
// source_table이 지정된 경우: 해당 테이블의 예약어만 체크
|
||||||
|
if (SystemFields::isReserved($fieldKey, $sourceTable)) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'field_key' => [__('error.field_key_reserved', ['field_key' => $fieldKey])],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// source_table이 없는 경우: 그룹 내 모든 테이블의 예약어 체크 (안전 모드)
|
||||||
|
if (SystemFields::isReservedInGroup($fieldKey, $groupId)) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'field_key' => [__('error.field_key_reserved', ['field_key' => $fieldKey])],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 기존 필드 중복 체크
|
||||||
|
$query = ItemField::where('tenant_id', $tenantId)
|
||||||
|
->where('field_key', $fieldKey);
|
||||||
|
|
||||||
|
if ($excludeId) {
|
||||||
|
$query->where('id', '!=', $excludeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($query->exists()) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'field_key' => [__('validation.unique', ['attribute' => 'field_key'])],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 필드 복제
|
* 필드 복제
|
||||||
*
|
*
|
||||||
@@ -109,11 +161,19 @@ public function clone(int $id): ItemField
|
|||||||
'created_by' => $userId,
|
'created_by' => $userId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 2. field_key 복제 ({새ID}_{원본key} 형식)
|
// 2. field_key 복제 (원본key_copy 형식, 중복 시 _copy2, _copy3...)
|
||||||
if ($original->field_key) {
|
if ($original->field_key) {
|
||||||
// 원본 field_key에서 키 부분 추출 (예: "81_itemNum" → "itemNum")
|
$baseKey = $original->field_key.'_copy';
|
||||||
$originalKey = preg_replace('/^\d+_/', '', $original->field_key);
|
$copyKey = $baseKey;
|
||||||
$cloned->update(['field_key' => $cloned->id.'_'.$originalKey.'_copy']);
|
$suffix = 1;
|
||||||
|
|
||||||
|
// 중복되지 않는 키 찾기
|
||||||
|
while (ItemField::where('tenant_id', $tenantId)->where('field_key', $copyKey)->exists()) {
|
||||||
|
$suffix++;
|
||||||
|
$copyKey = $baseKey.$suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cloned->update(['field_key' => $copyKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $cloned;
|
return $cloned;
|
||||||
@@ -183,9 +243,12 @@ public function store(int $sectionId, array $data): ItemField
|
|||||||
'created_by' => $userId,
|
'created_by' => $userId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 2. field_key 설정 ({ID}_{field_key} 형식)
|
// 2. field_key 설정 (시스템 필드 + 중복 검증 후 저장)
|
||||||
if (! empty($data['field_key'])) {
|
if (! empty($data['field_key'])) {
|
||||||
$field->update(['field_key' => $field->id.'_'.$data['field_key']]);
|
$sourceTable = $data['source_table'] ?? null;
|
||||||
|
$groupId = $data['group_id'] ?? 1;
|
||||||
|
$this->validateFieldKeyUnique($data['field_key'], $tenantId, $sourceTable, $groupId);
|
||||||
|
$field->update(['field_key' => $data['field_key']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 섹션-필드 관계 생성
|
// 3. 섹션-필드 관계 생성
|
||||||
@@ -237,9 +300,16 @@ public function update(int $id, array $data): ItemField
|
|||||||
'updated_by' => $userId,
|
'updated_by' => $userId,
|
||||||
];
|
];
|
||||||
|
|
||||||
// field_key 변경 시 {ID}_{field_key} 형식 유지
|
// field_key 변경 시 시스템 필드 + 중복 검증 후 저장
|
||||||
if (array_key_exists('field_key', $data)) {
|
if (array_key_exists('field_key', $data)) {
|
||||||
$updateData['field_key'] = $data['field_key'] ? $field->id.'_'.$data['field_key'] : null;
|
if ($data['field_key']) {
|
||||||
|
$sourceTable = $data['source_table'] ?? null;
|
||||||
|
$groupId = $field->group_id ?? 1;
|
||||||
|
$this->validateFieldKeyUnique($data['field_key'], $tenantId, $sourceTable, $groupId, $id);
|
||||||
|
$updateData['field_key'] = $data['field_key'];
|
||||||
|
} else {
|
||||||
|
$updateData['field_key'] = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// is_locked 변경 처리
|
// is_locked 변경 처리
|
||||||
|
|||||||
200
docs/specs/item-master-field-key-validation.md
Normal file
200
docs/specs/item-master-field-key-validation.md
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
# Item Master field_key 검증 정책
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
field_key 저장 및 검증 정책을 변경하여 시스템 필드(고정 컬럼)와의 충돌을 방지합니다.
|
||||||
|
|
||||||
|
## 변경 사항
|
||||||
|
|
||||||
|
### 1. field_key 저장 정책 변경
|
||||||
|
|
||||||
|
**변경 전:**
|
||||||
|
```
|
||||||
|
field_key = {id}_{입력값}
|
||||||
|
예: 98_code, 99_name
|
||||||
|
```
|
||||||
|
|
||||||
|
**변경 후:**
|
||||||
|
```
|
||||||
|
field_key = {입력값}
|
||||||
|
예: code, name (단, 시스템 예약어는 사용 불가)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 시스템 필드 예약어 검증 추가
|
||||||
|
|
||||||
|
#### 검증 흐름
|
||||||
|
```
|
||||||
|
field_key 입력
|
||||||
|
↓
|
||||||
|
source_table 확인 (products / materials)
|
||||||
|
↓
|
||||||
|
해당 테이블 예약어 체크
|
||||||
|
↓
|
||||||
|
기존 필드 중복 체크
|
||||||
|
↓
|
||||||
|
저장
|
||||||
|
```
|
||||||
|
|
||||||
|
#### source_table 기반 예약어 매핑
|
||||||
|
|
||||||
|
| source_table | 대상 테이블 | 예약어 목록 |
|
||||||
|
|--------------|-------------|-------------|
|
||||||
|
| `products` | products | code, name, unit, product_type, ... |
|
||||||
|
| `materials` | materials | name, material_code, material_type, ... |
|
||||||
|
| `null` | 전체 | products + materials 예약어 모두 체크 (안전 모드) |
|
||||||
|
|
||||||
|
## 구현 상세
|
||||||
|
|
||||||
|
### 파일 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
├── Constants/
|
||||||
|
│ └── SystemFields.php # 신규: 예약어 상수 클래스
|
||||||
|
└── Services/
|
||||||
|
└── ItemMaster/
|
||||||
|
└── ItemFieldService.php # 수정: 예약어 검증 추가
|
||||||
|
```
|
||||||
|
|
||||||
|
### SystemFields 상수 클래스
|
||||||
|
|
||||||
|
```php
|
||||||
|
// app/Constants/SystemFields.php
|
||||||
|
|
||||||
|
class SystemFields
|
||||||
|
{
|
||||||
|
// 소스 테이블 상수
|
||||||
|
public const SOURCE_TABLE_PRODUCTS = 'products';
|
||||||
|
public const SOURCE_TABLE_MATERIALS = 'materials';
|
||||||
|
|
||||||
|
// 그룹 ID 상수
|
||||||
|
public const GROUP_ITEM_MASTER = 1;
|
||||||
|
|
||||||
|
// products 테이블 고정 컬럼
|
||||||
|
public const PRODUCTS = [
|
||||||
|
'code', 'name', 'unit', 'category_id', 'product_type', 'description',
|
||||||
|
'is_sellable', 'is_purchasable', 'is_producible', 'is_variable_size', 'is_active',
|
||||||
|
'safety_stock', 'lead_time', 'product_category', 'part_type',
|
||||||
|
'bending_diagram', 'bending_details',
|
||||||
|
'specification_file', 'specification_file_name',
|
||||||
|
'certification_file', 'certification_file_name',
|
||||||
|
'certification_number', 'certification_start_date', 'certification_end_date',
|
||||||
|
'attributes', 'attributes_archive',
|
||||||
|
];
|
||||||
|
|
||||||
|
// materials 테이블 고정 컬럼
|
||||||
|
public const MATERIALS = [
|
||||||
|
'name', 'item_name', 'specification', 'material_code', 'material_type',
|
||||||
|
'unit', 'category_id', 'is_inspection', 'is_active',
|
||||||
|
'search_tag', 'remarks', 'attributes', 'options',
|
||||||
|
];
|
||||||
|
|
||||||
|
// 공통 시스템 컬럼
|
||||||
|
public const COMMON = [
|
||||||
|
'id', 'tenant_id', 'created_by', 'updated_by', 'deleted_by',
|
||||||
|
'created_at', 'updated_at', 'deleted_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
// source_table 기반 예약어 조회
|
||||||
|
public static function getReservedKeys(string $sourceTable): array;
|
||||||
|
|
||||||
|
// 예약어 여부 확인
|
||||||
|
public static function isReserved(string $fieldKey, string $sourceTable): bool;
|
||||||
|
|
||||||
|
// 그룹 내 전체 예약어 조회 (안전 모드)
|
||||||
|
public static function getAllReservedKeysForGroup(int $groupId): array;
|
||||||
|
|
||||||
|
// 그룹 내 예약어 여부 확인
|
||||||
|
public static function isReservedInGroup(string $fieldKey, int $groupId): bool;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ItemFieldService 검증 메서드
|
||||||
|
|
||||||
|
```php
|
||||||
|
private function validateFieldKeyUnique(
|
||||||
|
string $fieldKey,
|
||||||
|
int $tenantId,
|
||||||
|
?string $sourceTable = null,
|
||||||
|
int $groupId = 1,
|
||||||
|
?int $excludeId = null
|
||||||
|
): void {
|
||||||
|
// 1. 시스템 필드(예약어) 체크
|
||||||
|
if ($sourceTable) {
|
||||||
|
if (SystemFields::isReserved($fieldKey, $sourceTable)) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'field_key' => [__('error.field_key_reserved', ['field_key' => $fieldKey])],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 안전 모드: 그룹 내 모든 테이블 예약어 체크
|
||||||
|
if (SystemFields::isReservedInGroup($fieldKey, $groupId)) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'field_key' => [__('error.field_key_reserved', ['field_key' => $fieldKey])],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 기존 필드 중복 체크
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 호출 예시
|
||||||
|
|
||||||
|
```php
|
||||||
|
// 독립 필드 생성 시
|
||||||
|
$this->validateFieldKeyUnique(
|
||||||
|
$data['field_key'],
|
||||||
|
$tenantId,
|
||||||
|
$data['source_table'] ?? null, // 'products' 또는 'materials'
|
||||||
|
$data['group_id'] ?? 1
|
||||||
|
);
|
||||||
|
|
||||||
|
// 필드 수정 시
|
||||||
|
$this->validateFieldKeyUnique(
|
||||||
|
$data['field_key'],
|
||||||
|
$tenantId,
|
||||||
|
$data['source_table'] ?? null,
|
||||||
|
$field->group_id ?? 1,
|
||||||
|
$id // excludeId
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 에러 메시지
|
||||||
|
|
||||||
|
| 상황 | 메시지 키 | 메시지 |
|
||||||
|
|------|----------|--------|
|
||||||
|
| 시스템 예약어 충돌 | `error.field_key_reserved` | `"code"은(는) 시스템 예약어로 사용할 수 없습니다.` |
|
||||||
|
| 기존 필드 중복 | `validation.unique` | `field_key은(는) 이미 사용 중입니다.` |
|
||||||
|
|
||||||
|
```php
|
||||||
|
// lang/ko/error.php
|
||||||
|
'field_key_reserved' => '":field_key"은(는) 시스템 예약어로 사용할 수 없습니다.',
|
||||||
|
|
||||||
|
// lang/ko/validation.php (Laravel 기본)
|
||||||
|
'unique' => ':attribute은(는) 이미 사용 중입니다.',
|
||||||
|
```
|
||||||
|
|
||||||
|
## clone 메서드 field_key 복제 정책
|
||||||
|
|
||||||
|
```
|
||||||
|
원본 field_key: custom_field
|
||||||
|
복제본: custom_field_copy
|
||||||
|
|
||||||
|
중복 시: custom_field_copy2, custom_field_copy3, ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 관련 파일
|
||||||
|
|
||||||
|
| 파일 | 변경 유형 | 설명 |
|
||||||
|
|------|----------|------|
|
||||||
|
| `app/Constants/SystemFields.php` | 신규 | 예약어 상수 클래스 |
|
||||||
|
| `app/Services/ItemMaster/ItemFieldService.php` | 수정 | 검증 로직 추가 |
|
||||||
|
| `lang/ko/error.php` | 수정 | 에러 메시지 추가 |
|
||||||
|
|
||||||
|
## 참고
|
||||||
|
|
||||||
|
- ItemPage 테이블의 `source_table` 컬럼: 실제 저장 테이블명 (products, materials)
|
||||||
|
- ItemPage 테이블의 `item_type` 컬럼: FG, PT, SM, RM, CS (품목 유형 코드)
|
||||||
|
- `group_id`: 카테고리 격리용 (1 = 품목관리)
|
||||||
@@ -109,6 +109,7 @@
|
|||||||
'page_not_found' => '페이지를 찾을 수 없습니다.',
|
'page_not_found' => '페이지를 찾을 수 없습니다.',
|
||||||
'section_not_found' => '섹션을 찾을 수 없습니다.',
|
'section_not_found' => '섹션을 찾을 수 없습니다.',
|
||||||
'field_not_found' => '필드를 찾을 수 없습니다.',
|
'field_not_found' => '필드를 찾을 수 없습니다.',
|
||||||
|
'field_key_reserved' => '":field_key"은(는) 시스템 예약어로 사용할 수 없습니다.',
|
||||||
'bom_not_found' => 'BOM 항목을 찾을 수 없습니다.',
|
'bom_not_found' => 'BOM 항목을 찾을 수 없습니다.',
|
||||||
|
|
||||||
// 품목 관리 관련
|
// 품목 관리 관련
|
||||||
|
|||||||
Reference in New Issue
Block a user