From 9b6d5bebc998fa4b545be70bcde4c407070e7218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 17 Mar 2026 20:25:59 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20[numbering]=20=EC=B1=84=EB=B2=88?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EB=AC=B8=EC=84=9C=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MNG 채번규칙 관리 가이드 추가 - 신규 규칙 생성 템플릿 5종 추가 (원자재로트, 견적, 수주, 작업지시, 매출) - ReceivingService 연동 내용 추가 - 현재 등록 현황 (프론트_테스트회사) --- rules/numbering-rules.md | 300 +++++++++++++++++++++++++++++++++------ 1 file changed, 256 insertions(+), 44 deletions(-) diff --git a/rules/numbering-rules.md b/rules/numbering-rules.md index ef5ad2a..8dfdc50 100644 --- a/rules/numbering-rules.md +++ b/rules/numbering-rules.md @@ -1,19 +1,24 @@ # 채번규칙 (Numbering Rules) -> **상태**: 내부 서비스 (직접 API 없음) -> **최종 갱신**: 2026-02-27 +> **작성일**: 2026-02-27 +> **상태**: 운영 중 +> **최종 갱신**: 2026-03-17 --- ## 1. 개요 -문서 번호(견적번호, 수주번호 등)를 규칙 기반으로 자동 생성하는 내부 서비스. 테넌트별로 채번 규칙을 설정하면, 해당 규칙에 따라 일련번호가 자동 생성된다. 규칙이 없는 경우 레거시 포맷으로 폴백. +### 1.1 목적 + +문서 번호(견적번호, 수주번호, 원자재 로트번호 등)를 규칙 기반으로 자동 생성하는 내부 서비스. 테넌트별로 채번 규칙을 설정하면, 해당 규칙에 따라 일련번호가 자동 생성된다. 규칙이 없는 경우 레거시 포맷으로 폴백. + +### 1.2 핵심 원칙 -**핵심 기능:** - 유연한 패턴 기반 번호 생성 (6가지 세그먼트 유형) - Atomic UPSERT로 동시성 안전 시퀀스 관리 - 기간별 자동 리셋 (일/월/년/무제한) - 미리보기 기능 (DB 업데이트 없음) +- 규칙 미설정 시 레거시 로직 자동 폴백 --- @@ -22,11 +27,26 @@ | 모델 | 테이블 | 설명 | |------|--------|------| | `NumberingRule` | `numbering_rules` | 채번 규칙 정의 (document_type, pattern, reset_period) | -| `NumberingSequence` | `numbering_sequences` | 일련번호 카운터 (tenant + type + scope + period) | +| — | `numbering_sequences` | 일련번호 카운터 (tenant + type + scope + period) | -**지원 문서 유형:** quote, order, sale, work_order, material_receipt +**지원 문서 유형:** -**리셋 주기:** daily, monthly, yearly, never +| 코드 | 한글 | 사용처 | +|------|------|--------| +| `quote` | 견적 | `QuoteNumberService` | +| `order` | 수주 | `OrderService` | +| `sale` | 매출 | (예정) | +| `work_order` | 작업지시 | (예정) | +| `material_receipt` | 원자재수입검사 | `ReceivingService` | + +**리셋 주기:** + +| 코드 | 설명 | period_key 예시 | +|------|------|----------------| +| `daily` | 매일 01부터 리셋 | `260317` | +| `monthly` | 매월 01부터 리셋 | `202603` | +| `yearly` | 매년 01부터 리셋 | `2026` | +| `never` | 리셋 없이 계속 증가 | `all` | --- @@ -34,67 +54,259 @@ 패턴은 JSON 배열로 세그먼트를 정의한다. +### 3.1 세그먼트 유형 + | 세그먼트 | 설명 | 예시 | |---------|------|------| -| `static` | 고정 문자열 | `{ "type": "static", "value": "QT" }` | -| `date` | 날짜 포맷 | `{ "type": "date", "format": "ymd" }` → `260227` | -| `sequence` | 일련번호 | `{ "type": "sequence", "padding": 4 }` → `0001` | +| `static` | 고정 문자열 | `{ "type": "static", "value": "KD" }` | | `separator` | 구분자 | `{ "type": "separator", "value": "-" }` | -| `param` | 외부 파라미터 | `{ "type": "param", "key": "product_category" }` | -| `mapping` | 값 매핑 | `{ "type": "mapping", "key": "category", "map": {"A": "PR"} }` | +| `date` | 날짜 포맷 (PHP date) | `{ "type": "date", "format": "ymd" }` → `260317` | +| `sequence` | 일련번호 (자릿수는 rule 레벨) | `{ "type": "sequence" }` | +| `param` | 외부 파라미터 | `{ "type": "param", "key": "pair_code", "default": "SS" }` | +| `mapping` | 값 → 코드 매핑 | `{ "type": "mapping", "key": "category", "map": {"SCREEN": "SC"}, "default": "XX" }` | + +### 3.2 date 포맷 예시 + +| format | 결과 | 설명 | +|--------|------|------| +| `ymd` | `260317` | 2자리 연+월+일 | +| `Ymd` | `20260317` | 4자리 연+월+일 | +| `ym` | `2603` | 2자리 연+월 | +| `Y` | `2026` | 4자리 연도 | + +--- + +## 4. MNG 채번규칙 관리 + +### 4.1 접근 경로 + +``` +MNG > 권한 관리 > 채번 규칙 관리 +URL: admin.codebridge-x.com/numbering-rules +``` + +### 4.2 관리 화면 기능 + +| 기능 | 설명 | +|------|------| +| 목록 조회 | 문서유형, 상태 필터 + 규칙명 검색 | +| 새 규칙 | 패턴 세그먼트 편집기 + 실시간 미리보기 | +| 수정 | 기존 규칙의 패턴, 리셋주기, 자릿수 변경 | +| 삭제 | 규칙 삭제 (Hard Delete) | + +### 4.3 신규 규칙 생성 절차 + +1. **+ 새 규칙** 버튼 클릭 +2. **기본 정보** 입력: + - 규칙명: 관리용 이름 (예: `원자재 로트번호`) + - 문서유형: 드롭다운 선택 (테넌트당 유형별 1개만 가능) + - 리셋 주기: 일별 / 월별 / 연별 / 무제한 + - 시퀀스 자릿수: 2 → `01,02` / 3 → `001,002` / 4 → `0001,0002` +3. **패턴 세그먼트** 구성: + - `+ 세그먼트 추가` 버튼으로 세그먼트를 순서대로 추가 + - 각 세그먼트의 타입과 값 설정 +4. **미리보기** 확인: 세그먼트 변경 시 실시간 미리보기 표시 +5. **생성** 버튼으로 저장 + +> **주의**: 테넌트당 문서유형별 1개 규칙만 가능하다. 이미 등록된 유형은 선택 불가. + +--- + +## 5. 신규 규칙 생성 템플릿 + +아래는 자주 사용하는 채번 패턴 템플릿이다. MNG에서 새 규칙 생성 시 참고한다. + +### 5.1 원자재 로트번호 (YYMMDD-NN) + +> 5130 레거시 방식 차용. 일별 시퀀스 리셋. + +| 항목 | 값 | +|------|------| +| 규칙명 | `원자재 로트번호` | +| 문서유형 | `material_receipt` (원자재수입검사) | +| 리셋 주기 | `daily` (일별) | +| 시퀀스 자릿수 | `2` | + +**패턴:** + +```json +[ + { "type": "date", "format": "ymd" }, + { "type": "separator", "value": "-" }, + { "type": "sequence" } +] +``` + +**결과:** `260317-01`, `260317-02`, ... → 다음날 `260318-01` + +### 5.2 견적번호 (KD-PR-YYMMDD-NN) + +| 항목 | 값 | +|------|------| +| 규칙명 | `5130 견적번호` | +| 문서유형 | `quote` (견적) | +| 리셋 주기 | `daily` | +| 시퀀스 자릿수 | `2` | + +**패턴:** -**패턴 예시:** ```json [ { "type": "static", "value": "KD" }, { "type": "separator", "value": "-" }, - { "type": "mapping", "key": "category", "map": { "A": "PR" }, "default": "XX" }, + { "type": "static", "value": "PR" }, { "type": "separator", "value": "-" }, { "type": "date", "format": "ymd" }, { "type": "separator", "value": "-" }, - { "type": "sequence", "padding": 2 } + { "type": "sequence" } ] ``` -→ `KD-PR-260227-01` ---- +**결과:** `KD-PR-260317-01` -## 4. 서비스 +### 5.3 수주 로트번호 (KD-{pairCode}-YYMMDD-NN) -| 서비스 | 메서드 | 설명 | -|--------|--------|------| -| `NumberingService` | `generate($documentType, $params)` | 번호 생성 (규칙 없으면 null → 레거시 폴백) | -| | `preview($documentType, $params)` | 미리보기 (시퀀스 증가 없음) | -| `QuoteNumberService` | `generate()` | 견적번호 생성 (NumberingService → 레거시 폴백) | -| | `validate()` | 형식 검증 | -| | `isUnique()` | 중복 체크 | +| 항목 | 값 | +|------|------| +| 규칙명 | `5130 수주 로트번호` | +| 문서유형 | `order` (수주) | +| 리셋 주기 | `daily` | +| 시퀀스 자릿수 | `2` | -**레거시 포맷:** `QT{YYYYMMDD}{NNNN}` (예: `QT202602270001`) +**패턴:** ---- - -## 5. 동시성 처리 - -```sql -INSERT INTO numbering_sequences (tenant_id, document_type, scope_key, period_key, last_sequence) -VALUES (?, ?, ?, ?, 1) -ON DUPLICATE KEY UPDATE last_sequence = last_sequence + 1 +```json +[ + { "type": "static", "value": "KD" }, + { "type": "separator", "value": "-" }, + { "type": "param", "key": "pair_code", "default": "SS" }, + { "type": "separator", "value": "-" }, + { "type": "date", "format": "ymd" }, + { "type": "separator", "value": "-" }, + { "type": "sequence" } +] ``` -MySQL의 `UPSERT`를 사용하여 동시 요청에서도 시퀀스 충돌 없이 안전하게 번호 생성. +**결과:** `KD-SS-260317-01`, `KD-TE-260317-01` (pairCode별 독립 시퀀스) + +### 5.4 작업지시번호 (WO-YYYYMMDD-NNNN) + +| 항목 | 값 | +|------|------| +| 규칙명 | `작업지시번호` | +| 문서유형 | `work_order` (작업지시) | +| 리셋 주기 | `daily` | +| 시퀀스 자릿수 | `4` | + +**패턴:** + +```json +[ + { "type": "static", "value": "WO" }, + { "type": "separator", "value": "-" }, + { "type": "date", "format": "Ymd" }, + { "type": "separator", "value": "-" }, + { "type": "sequence" } +] +``` + +**결과:** `WO-20260317-0001` + +### 5.5 월별 리셋 예시 (INV-YYYYMM-NNN) + +| 항목 | 값 | +|------|------| +| 규칙명 | `매출번호` | +| 문서유형 | `sale` (매출) | +| 리셋 주기 | `monthly` | +| 시퀀스 자릿수 | `3` | + +**패턴:** + +```json +[ + { "type": "static", "value": "INV" }, + { "type": "separator", "value": "-" }, + { "type": "date", "format": "Ym" }, + { "type": "separator", "value": "-" }, + { "type": "sequence" } +] +``` + +**결과:** `INV-202603-001` → 4월 리셋 → `INV-202604-001` --- -## 6. 호출 위치 +## 6. 서비스 연동 -직접 API 엔드포인트는 없으며, 아래 서비스에서 내부 호출된다: +### 6.1 호출 구조 -| 서비스 | 용도 | -|--------|------| -| `QuoteNumberService` | 견적번호 생성 | -| `OrderService` | 수주번호 생성 | -| `WorkOrderService` | 작업지시번호 생성 | -| `MaterialReceiptService` | 입고번호 생성 | +``` +호출 서비스 → NumberingService.generate(documentType, params) + ├─ 규칙 있음 → 패턴 기반 생성 (Atomic UPSERT) + └─ 규칙 없음 → null 반환 → 호출 서비스가 레거시 로직 사용 +``` + +### 6.2 현재 연동 서비스 + +| 서비스 | 문서유형 | 레거시 폴백 | +|--------|---------|------------| +| `QuoteNumberService` | `quote` | `QT{YYYYMMDD}{NNNN}` | +| `OrderService` | `order` | `ORD{YYYYMMDD}{NNNN}` | +| `ReceivingService` | `material_receipt` | `YYMMDD-NN` (DB 시퀀스) | + +### 6.3 연동 코드 패턴 + +새로운 서비스에서 채번규칙을 사용하려면 아래 패턴을 따른다: + +```php +// 1. NumberingService 호출 +$numberingService = app(NumberingService::class); +$numberingService->setContext($this->tenantId(), $this->apiUserId()); + +// 2. 규칙 기반 생성 시도 +$number = $numberingService->generate('document_type', [ + 'param_key' => 'value', // param/mapping 세그먼트용 +]); + +// 3. 규칙 없으면 레거시 폴백 +if ($number !== null) { + return $number; +} +return $this->generateLegacy(); +``` + +--- + +## 7. 동시성 처리 + +```sql +INSERT INTO numbering_sequences + (tenant_id, document_type, scope_key, period_key, last_sequence, created_at, updated_at) +VALUES (?, ?, ?, ?, 1, NOW(), NOW()) +ON DUPLICATE KEY UPDATE + last_sequence = last_sequence + 1, updated_at = NOW() +``` + +MySQL의 `ON DUPLICATE KEY UPDATE`로 Atomic 연산을 보장한다. 동시 요청에서도 시퀀스 충돌 없이 안전하게 번호를 생성한다. + +**scope_key 분리**: `param`, `mapping` 세그먼트 값이 scope_key로 저장되어, 파라미터별 독립 시퀀스를 유지한다. + +예: 수주 `KD-SS-260317-01`과 `KD-TE-260317-01`은 각각 독립 시퀀스. + +--- + +## 8. 현재 등록 현황 + +### 프론트_테스트회사 (tenant_id: 287) + +| # | 규칙명 | 문서유형 | 패턴 미리보기 | 리셋 | 자릿수 | +|---|--------|---------|-------------|------|:------:| +| 1 | 5130 견적번호 | quote | `KD-PR-260317-01` | 일별 | 2 | +| 2 | 5130 수주 로트번호 | order | `KD-SS-260317-01` | 일별 | 2 | +| 3 | 원자재 로트번호 | material_receipt | `260317-01` | 일별 | 2 | + +> 신규 테넌트에 규칙이 없으면 각 서비스의 레거시 로직이 자동 적용된다. --- @@ -105,4 +317,4 @@ MySQL의 `UPSERT`를 사용하여 동시 요청에서도 시퀀스 충돌 없이 --- -**최종 업데이트**: 2026-02-27 +**최종 업데이트**: 2026-03-17