@simplysm/sd-claude 14.0.78 → 14.0.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude/references/sd-simplysm14/manuals/logging.md +1 -1
- package/claude/rules/sd-base-rules.md +293 -377
- package/claude/rules/sd-design-rules.md +44 -0
- package/claude/{sd-check-forbidden-files.py → sd-check-edit.py} +2 -1
- package/claude/{sd-check-bash.py → sd-check-shell.py} +2 -2
- package/claude/settings.json +3 -4
- package/claude/skills/sd-demo/SKILL.md +6 -0
- package/claude/skills/sd-dev/SKILL.md +1 -1
- package/claude/skills/sd-impl/SKILL.md +15 -14
- package/claude/skills/sd-impl/references/spec-cross-check.md +2 -2
- package/claude/skills/sd-skill/SKILL.md +209 -29
- package/claude/skills/sd-spec/SKILL.md +820 -192
- package/claude/skills/sd-spec/references/example-spec.md +129 -80
- package/claude/skills/sd-unpack/SKILL.md +39 -14
- package/claude/skills/sd-unpack/scripts/handlers/__pycache__/_common.cpython-314.pyc +0 -0
- package/claude/skills/sd-unpack/scripts/handlers/__pycache__/eml_handler.cpython-314.pyc +0 -0
- package/claude/skills/sd-unpack/scripts/handlers/__pycache__/office_com.cpython-314.pyc +0 -0
- package/claude/skills/sd-unpack/scripts/handlers/__pycache__/pdf_handler.cpython-314.pyc +0 -0
- package/claude/skills/sd-unpack/scripts/handlers/_common.py +59 -0
- package/claude/skills/sd-unpack/scripts/handlers/eml_handler.py +7 -0
- package/claude/skills/sd-unpack/scripts/handlers/msg_handler.py +11 -0
- package/claude/skills/sd-unpack/scripts/handlers/office_com.py +288 -79
- package/claude/skills/sd-unpack/scripts/handlers/office_worker.py +3 -2
- package/claude/skills/sd-unpack/scripts/handlers/pdf_handler.py +78 -10
- package/package.json +1 -1
- package/claude/output-styles/sd-tone.md +0 -128
- package/claude/skills/sd-skill/evals/fixtures/empty/.gitkeep +0 -0
- package/claude/skills/sd-skill/evals/fixtures/with-existing-review/.claude/skills/review/SKILL.md +0 -14
- package/claude/skills/sd-skill/evals/golden.jsonl +0 -5
- package/claude/skills/sd-skill/references/eval-authoring.md +0 -81
- package/claude/skills/sd-skill/references/eval-run.md +0 -32
- package/claude/skills/sd-skill/references/skill-authoring.md +0 -70
- package/claude/skills/sd-spec/references/spec-authoring.md +0 -298
- package/claude/skills/sd-spec/references/spec-md-template.md +0 -29
- package/claude/skills/sd-wip/SKILL.md +0 -38
- package/claude/skills/sd-wip/evals/fixtures/empty/.gitkeep +0 -0
- package/claude/skills/sd-wip/evals/fixtures/with-artifact/projects/acct/_wip.md +0 -3
- package/claude/skills/sd-wip/evals/fixtures/with-artifact/projects/acct/spec.md +0 -15
- package/claude/skills/sd-wip/evals/fixtures/with-existing-wip/.wips/260101120000_acct.md +0 -6
- package/claude/skills/sd-wip/evals/fixtures/with-existing-wip-for-compact/.wips/260101120000_acct.md +0 -14
- package/claude/skills/sd-wip/evals/golden.jsonl +0 -4
- package/claude/skills/sd-wip/references/compact.md +0 -79
|
@@ -75,8 +75,7 @@ flowchart LR
|
|
|
75
75
|
|
|
76
76
|
### 2.2 출고 [OPEN: 2026-04-01]
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
- 대화 발췌: 사용자 — "출고 부분은 입고 끝나고 다음 단계에서 보자"
|
|
78
|
+
분석 방법: 회의록.md L40-50 재독 (입고 분석 끝난 후). 사용자 보류 발언 — "출고 부분은 입고 끝나고 다음 단계에서 보자".
|
|
80
79
|
|
|
81
80
|
## 3. 기타 요구사항
|
|
82
81
|
|
|
@@ -86,6 +85,12 @@ flowchart LR
|
|
|
86
85
|
|
|
87
86
|
관련 섹션: [화면.재고 확인], [자동 처리.재고 스냅샷]
|
|
88
87
|
|
|
88
|
+
### 3.2 마스터 데이터 변경 이력 [확정: 2026-04-01]
|
|
89
|
+
|
|
90
|
+
모든 마스터 데이터의 변경 (등록·편집·활성/비활성 전환) 이력을 자동 기록해야 함. 변경 추적·감사 목적.
|
|
91
|
+
|
|
92
|
+
관련 섹션: [횡단 처리.마스터 데이터 변경 이력]
|
|
93
|
+
|
|
89
94
|
## 4. 화면
|
|
90
95
|
|
|
91
96
|
| § | 분류 | 화면 | 유형 | 장치 |
|
|
@@ -106,7 +111,7 @@ Actor: 창고 관리자
|
|
|
106
111
|
|
|
107
112
|
- 품목 마스터 CRUD (등록 / 편집 / 삭제·복구 / 엑셀 업·다운로드)
|
|
108
113
|
|
|
109
|
-
|
|
114
|
+
#### 와이어프레임
|
|
110
115
|
|
|
111
116
|
```
|
|
112
117
|
┌──────────────────────────────────────────────────────────────────────────┐
|
|
@@ -120,7 +125,7 @@ Actor: 창고 관리자
|
|
|
120
125
|
└──────────────────────────────────────────────────────────────────────────┘
|
|
121
126
|
```
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
#### 항목
|
|
124
129
|
|
|
125
130
|
**필터**
|
|
126
131
|
|
|
@@ -137,7 +142,7 @@ Actor: 창고 관리자
|
|
|
137
142
|
| 코드 | 문자 | - | [모델.품목.코드] | |
|
|
138
143
|
| 명칭 | 문자 | - | [모델.품목.명칭] | |
|
|
139
144
|
|
|
140
|
-
|
|
145
|
+
#### 동작
|
|
141
146
|
|
|
142
147
|
- `[조회]` 클릭: 검색 필터 조건으로 목록 갱신
|
|
143
148
|
- `[등록]` 클릭: [화면.품목 등록·편집] 을 빈 폼으로 모달 띄움
|
|
@@ -146,15 +151,15 @@ Actor: 창고 관리자
|
|
|
146
151
|
- `[선택 복구]` 클릭: 선택된 행들의 활성 여부 true 전환
|
|
147
152
|
- `[엑셀 업로드]` 클릭: 엑셀 파일 선택 → 코드 기준 일괄 등록/수정
|
|
148
153
|
- `[엑셀 다운로드]` 클릭: 현재 검색 필터 적용된 목록을 엑셀로 내보내기
|
|
149
|
-
- `[화주 자료 업로드]` 클릭: [공통 정의.화주 품목 자료] 파일 선택 → 아래 양식
|
|
154
|
+
- `[화주 자료 업로드]` 클릭: [공통 정의.화주 품목 자료] 파일 선택 → 아래 업로드 양식 적용 후 코드 기준 일괄 등록/수정
|
|
150
155
|
|
|
151
|
-
시각
|
|
156
|
+
#### 시각 규칙
|
|
152
157
|
|
|
153
158
|
- 비활성 행: 전체 취소선 + 회색
|
|
154
159
|
|
|
155
|
-
양식
|
|
160
|
+
#### 업로드 양식
|
|
156
161
|
|
|
157
|
-
|
|
162
|
+
[공통 정의.화주 품목 자료] → [모델.품목]
|
|
158
163
|
|
|
159
164
|
| 파일 컬럼 | 도메인 모델 | 변환·규칙 |
|
|
160
165
|
| ------------------ | ---------------- | -------------------------------- |
|
|
@@ -170,7 +175,7 @@ Actor: 창고 관리자
|
|
|
170
175
|
|
|
171
176
|
- 품목 신규 등록 또는 기존 품목 편집 (호출하는 쪽에서 빈 폼/기존 데이터로 모달 띄움)
|
|
172
177
|
|
|
173
|
-
|
|
178
|
+
#### 와이어프레임
|
|
174
179
|
|
|
175
180
|
```
|
|
176
181
|
┌──────────────────────────────────────┐
|
|
@@ -182,7 +187,7 @@ Actor: 창고 관리자
|
|
|
182
187
|
└──────────────────────────────────────┘
|
|
183
188
|
```
|
|
184
189
|
|
|
185
|
-
|
|
190
|
+
#### 항목
|
|
186
191
|
|
|
187
192
|
**입력 폼**
|
|
188
193
|
|
|
@@ -191,7 +196,7 @@ Actor: 창고 관리자
|
|
|
191
196
|
| 코드 | 텍스트 입력 | O | [모델.품목.코드] | |
|
|
192
197
|
| 명칭 | 텍스트 입력 | O | [모델.품목.명칭] | |
|
|
193
198
|
|
|
194
|
-
|
|
199
|
+
#### 동작
|
|
195
200
|
|
|
196
201
|
- `[저장]` 클릭: 활성 품목 내 코드·명칭 유일성 검증 → 저장 → 모달 닫음
|
|
197
202
|
- `[취소]` 클릭: 변경 사항 폐기 → 모달 닫음
|
|
@@ -206,7 +211,7 @@ Actor: 창고 관리자
|
|
|
206
211
|
- Location 마스터 CRUD (등록 / 편집 / 삭제·복구 / 엑셀 업·다운로드)
|
|
207
212
|
- 두 가지 바코드 출력: A4 다중 + 라벨프린터 1매씩
|
|
208
213
|
|
|
209
|
-
|
|
214
|
+
#### 와이어프레임
|
|
210
215
|
|
|
211
216
|
```
|
|
212
217
|
┌────────────────────────────────────────────────────────────────────────────────────┐
|
|
@@ -219,7 +224,7 @@ Actor: 창고 관리자
|
|
|
219
224
|
└────────────────────────────────────────────────────────────────────────────────────┘
|
|
220
225
|
```
|
|
221
226
|
|
|
222
|
-
|
|
227
|
+
#### 항목
|
|
223
228
|
|
|
224
229
|
**필터**
|
|
225
230
|
|
|
@@ -235,7 +240,7 @@ Actor: 창고 관리자
|
|
|
235
240
|
| ID | 숫자 + 편집 버튼 | - | [모델.Location.ID] | 클릭 시 편집 모달 |
|
|
236
241
|
| 코드 | 문자 | - | [모델.Location.코드] | |
|
|
237
242
|
|
|
238
|
-
|
|
243
|
+
#### 동작
|
|
239
244
|
|
|
240
245
|
- `[조회]` 클릭: 검색 필터 조건으로 목록 갱신
|
|
241
246
|
- `[등록]` 클릭: [화면.Location 등록·편집] 을 빈 폼으로 모달 띄움
|
|
@@ -255,7 +260,7 @@ Actor: 창고 관리자
|
|
|
255
260
|
1. 선택된 Location 을 [공통 정의.Location 라벨] 규격으로 라벨프린터 1매씩 출력
|
|
256
261
|
2. 다중 선택 시 선택 수만큼 연속 출력
|
|
257
262
|
|
|
258
|
-
시각
|
|
263
|
+
#### 시각 규칙
|
|
259
264
|
|
|
260
265
|
- 비활성 행: 전체 취소선 + 회색
|
|
261
266
|
|
|
@@ -268,7 +273,7 @@ Actor: 창고 관리자
|
|
|
268
273
|
|
|
269
274
|
- Location 신규 등록 또는 기존 Location 편집 (호출하는 쪽에서 빈 폼/기존 데이터로 모달 띄움)
|
|
270
275
|
|
|
271
|
-
|
|
276
|
+
#### 와이어프레임
|
|
272
277
|
|
|
273
278
|
```
|
|
274
279
|
┌──────────────────────────────────────┐
|
|
@@ -280,7 +285,7 @@ Actor: 창고 관리자
|
|
|
280
285
|
└──────────────────────────────────────┘
|
|
281
286
|
```
|
|
282
287
|
|
|
283
|
-
|
|
288
|
+
#### 항목
|
|
284
289
|
|
|
285
290
|
**입력 폼**
|
|
286
291
|
|
|
@@ -288,7 +293,7 @@ Actor: 창고 관리자
|
|
|
288
293
|
| ---- | ----------- | ---- | -------------------- | ----------------------- |
|
|
289
294
|
| 코드 | 텍스트 입력 | O | [모델.Location.코드] | 영문 대문자/숫자/하이픈 |
|
|
290
295
|
|
|
291
|
-
|
|
296
|
+
#### 동작
|
|
292
297
|
|
|
293
298
|
- `[저장]` 클릭: 활성 Location 내 코드 유일성 검증 → 저장 → 모달 닫음
|
|
294
299
|
- `[취소]` 클릭: 변경 사항 폐기 → 모달 닫음
|
|
@@ -302,7 +307,7 @@ Actor: 창고 작업자
|
|
|
302
307
|
|
|
303
308
|
- 박스 → Location 순서로 스캔하여 재고 등록
|
|
304
309
|
|
|
305
|
-
|
|
310
|
+
#### 와이어프레임
|
|
306
311
|
|
|
307
312
|
```
|
|
308
313
|
┌────────────────────────┐
|
|
@@ -315,7 +320,7 @@ Actor: 창고 작업자
|
|
|
315
320
|
(360 × 568)
|
|
316
321
|
```
|
|
317
322
|
|
|
318
|
-
|
|
323
|
+
#### 항목
|
|
319
324
|
|
|
320
325
|
**스캔 입력 폼**
|
|
321
326
|
|
|
@@ -324,7 +329,7 @@ Actor: 창고 작업자
|
|
|
324
329
|
| 박스 바코드 | 텍스트 입력 | O | - | PDA 스캐너 입력. 파싱 = 품목코드 + 수량 + 박스번호 |
|
|
325
330
|
| Location | 텍스트 입력 | O | [모델.Location.코드] | PDA 스캐너 입력 |
|
|
326
331
|
|
|
327
|
-
|
|
332
|
+
#### 동작
|
|
328
333
|
|
|
329
334
|
- `[등록]` 클릭:
|
|
330
335
|
- 절차:
|
|
@@ -343,7 +348,7 @@ Actor: 창고 관리자
|
|
|
343
348
|
- 현재 재고: Location 별 적치된 박스 + 품목·수량 조회
|
|
344
349
|
- 과거 시점 재고: 특정 날짜의 스냅샷 조회
|
|
345
350
|
|
|
346
|
-
|
|
351
|
+
#### 와이어프레임
|
|
347
352
|
|
|
348
353
|
```
|
|
349
354
|
┌──────────────────────────────────────────────────────────────────────────┐
|
|
@@ -356,7 +361,7 @@ Actor: 창고 관리자
|
|
|
356
361
|
└──────────────────────────────────────────────────────────────────────────┘
|
|
357
362
|
```
|
|
358
363
|
|
|
359
|
-
|
|
364
|
+
#### 항목
|
|
360
365
|
|
|
361
366
|
**필터**
|
|
362
367
|
|
|
@@ -375,7 +380,7 @@ Actor: 창고 관리자
|
|
|
375
380
|
| 품목 | 문자 | - | [모델.재고.박스.품목.코드] | 품목 코드 표시 |
|
|
376
381
|
| 수량 | 숫자 | - | [모델.재고.박스.수량] | |
|
|
377
382
|
|
|
378
|
-
|
|
383
|
+
#### 동작
|
|
379
384
|
|
|
380
385
|
- `[조회]` 클릭:
|
|
381
386
|
- 기준일 비어있음: 현재 [모델.재고] 조회
|
|
@@ -387,49 +392,73 @@ Actor: 창고 관리자
|
|
|
387
392
|
|
|
388
393
|
### 5.1 재고 스냅샷 [확정: 2026-04-01]
|
|
389
394
|
|
|
390
|
-
|
|
391
|
-
flowchart LR
|
|
392
|
-
S([매일 0시]) --> T1[현재 재고 전체 조회]
|
|
393
|
-
T1 --> T2[스냅샷 테이블에 날짜별 저장]
|
|
394
|
-
T2 --> E1([완료])
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
목적:
|
|
398
|
-
|
|
399
|
-
- 과거 시점 재고 조회 지원
|
|
395
|
+
목적: 과거 시점 재고 조회 지원
|
|
400
396
|
|
|
401
|
-
트리거:
|
|
397
|
+
트리거: 매일 0시 정각
|
|
402
398
|
|
|
403
|
-
|
|
399
|
+
관련 섹션: [기타.과거 재고 조회], [화면.재고 확인]
|
|
404
400
|
|
|
405
|
-
|
|
401
|
+
#### 처리
|
|
406
402
|
|
|
407
403
|
- [모델.재고] 전체를 [모델.재고 스냅샷] 에 날짜별로 저장
|
|
408
404
|
|
|
409
|
-
예외
|
|
405
|
+
#### 예외 처리
|
|
410
406
|
|
|
411
407
|
- 스냅샷 생성 실패 시:
|
|
412
408
|
- 위험: 당일 분 누락 시 해당 날짜의 과거 시점 조회 불가
|
|
413
409
|
- 대처: 관리자 알림. 동일 일자 재실행 시 (날짜, 박스) 유일성 검증으로 중복 생성 방지
|
|
414
410
|
- 재시도 한계: 별도 재시도 없음 (다음 날 0시 정상 동작)
|
|
415
411
|
|
|
416
|
-
|
|
412
|
+
## 6. 횡단 처리
|
|
413
|
+
|
|
414
|
+
### 6.1 마스터 데이터 변경 이력 [확정: 2026-04-01]
|
|
415
|
+
|
|
416
|
+
목적: 마스터 변경 추적·감사 (누가 언제 무엇을 어떻게 바꿨는지)
|
|
417
|
+
|
|
418
|
+
트리거 (부수효과 발동 조건):
|
|
419
|
+
- 마스터 엔티티의 insert / update / delete 트랜잭션 commit 시
|
|
420
|
+
- 적용 범위: [모델.품목], [모델.Location]
|
|
421
|
+
- 적용 제외: 트랜잭션 모델 ([모델.재고], [모델.재고 스냅샷])
|
|
422
|
+
|
|
423
|
+
관련 섹션: [기타.마스터 데이터 변경 이력], [모델.품목], [모델.Location], [모델.DataLog]
|
|
424
|
+
|
|
425
|
+
#### 처리
|
|
426
|
+
|
|
427
|
+
- 변경 전 값과 변경 후 값 캡처 (insert 시 변경 전 = null, delete 시 변경 후 = null)
|
|
428
|
+
- [모델.DataLog] 에 한 행 적재 (테이블·키·전·후·작업자·시각)
|
|
429
|
+
|
|
430
|
+
#### 예외 처리
|
|
431
|
+
|
|
432
|
+
- DataLog 적재 실패 시:
|
|
433
|
+
- 위험: 변경 추적 누락
|
|
434
|
+
- 대처: 본 트랜잭션은 정상 commit 유지, DataLog 적재 실패만 별도 큐로 적재 후 관리자 알림
|
|
435
|
+
- 재시도 한계: 큐에서 5회 지수 백오프 후 운영팀 알림
|
|
436
|
+
|
|
437
|
+
#### 모델 매핑
|
|
438
|
+
|
|
439
|
+
| 필드 ([모델.DataLog]) | 용도 |
|
|
440
|
+
| --------------------- | ------------------------------- |
|
|
441
|
+
| [모델.DataLog.테이블명] | 변경 대상 모델명 (예: `품목`) |
|
|
442
|
+
| [모델.DataLog.키] | 변경 대상 ID |
|
|
443
|
+
| [모델.DataLog.변경 전] | 컬럼별 변경 전 값 (JSON) |
|
|
444
|
+
| [모델.DataLog.변경 후] | 컬럼별 변경 후 값 (JSON) |
|
|
445
|
+
| [모델.DataLog.작업자] | 세션 사용자 |
|
|
446
|
+
| [모델.DataLog.시각] | 변경 commit 시각 |
|
|
417
447
|
|
|
418
|
-
##
|
|
448
|
+
## 7. 공통 정의
|
|
419
449
|
|
|
420
|
-
###
|
|
450
|
+
### 7.1 용어 사전 [확정: 2026-04-01]
|
|
421
451
|
|
|
422
452
|
- 박스: 외부에서 이미 바코드(품목+수량+박스번호)가 부착된 입고 단위
|
|
423
453
|
- Location: 창고 내 적치 위치 ([공통 정의.Location 라벨])
|
|
424
454
|
- ERP 통보: 입출고 발생 시 WMS → ERP 단방향 알림
|
|
425
455
|
- 활성 여부: 마스터 데이터의 소프트 삭제 플래그 (true = 정상, false = 삭제 처리됨). UI 의 "삭제/복구" 액션과 매핑. 마스터에 완전 삭제(DB 레코드 제거) 없음.
|
|
426
456
|
|
|
427
|
-
###
|
|
457
|
+
### 7.2 박스 바코드 형식 [OPEN: 2026-04-01]
|
|
428
458
|
|
|
429
|
-
|
|
430
|
-
- 자료 연결 메모: 박스 코드는 외부 규격이지만 [모델.박스] 식별 키 구조와 직결 — §7 박스 확정과 함께 검토
|
|
459
|
+
분석 방법: 첨부B.pdf p.5 "박스 코드 체계" (외부 송장 시스템 규격) 분석. [모델.박스] 식별 키 구조와 직결 — §8 박스 확정과 함께 검토.
|
|
431
460
|
|
|
432
|
-
###
|
|
461
|
+
### 7.3 Location 라벨 [확정: 2026-04-01]
|
|
433
462
|
|
|
434
463
|
```
|
|
435
464
|
┌────────────────────────────────┐
|
|
@@ -446,7 +475,7 @@ flowchart LR
|
|
|
446
475
|
- 라벨 크기: 100 × 50 mm
|
|
447
476
|
- 라벨 재질: 유포지, 흰색 무지
|
|
448
477
|
|
|
449
|
-
###
|
|
478
|
+
### 7.4 화주 품목 자료 [확정: 2026-04-01]
|
|
450
479
|
|
|
451
480
|
화주(품목을 본 WMS 에 보관 위탁한 거래처)가 자기 ERP 에서 수시로 다운로드해 화주별 별도 파일로 전달한다. 신규 화주 등록 또는 화주 측 품목 마스터 변경 시점에 본 WMS 에 1회 일괄 업로드한다. 식별 키는 `화주코드 + SEQ` 2컬럼 합성.
|
|
452
481
|
|
|
@@ -457,18 +486,18 @@ flowchart LR
|
|
|
457
486
|
| 품목명 | O | 화주 측 명칭 |
|
|
458
487
|
| 단위 | X | `EA` / `BOX` 등 |
|
|
459
488
|
|
|
460
|
-
##
|
|
489
|
+
## 8. 도메인 모델
|
|
461
490
|
|
|
462
|
-
###
|
|
491
|
+
### 8.1 품목 [확정: 2026-04-01]
|
|
463
492
|
|
|
464
493
|
필드:
|
|
465
494
|
|
|
466
|
-
| 필드 | 타입 | 필수 | 비고
|
|
467
|
-
| --------- | ------- | ---- |
|
|
468
|
-
| ID | 숫자 | O | 자동 부여
|
|
495
|
+
| 필드 | 타입 | 필수 | 비고 |
|
|
496
|
+
| --------- | ------- | ---- | -------------------- |
|
|
497
|
+
| ID | 숫자 | O | 자동 부여 |
|
|
469
498
|
| 코드 | 문자 | O | 자유 형식, 수정 가능 |
|
|
470
|
-
| 명칭 | 문자 | O |
|
|
471
|
-
| 활성 여부 | boolean | O |
|
|
499
|
+
| 명칭 | 문자 | O | |
|
|
500
|
+
| 활성 여부 | boolean | O | |
|
|
472
501
|
|
|
473
502
|
키/제약:
|
|
474
503
|
|
|
@@ -476,7 +505,7 @@ flowchart LR
|
|
|
476
505
|
- 비즈니스 키: 코드 (사용자 부여, 수정 가능)
|
|
477
506
|
- 유일성: 활성 품목 내 코드, 명칭 각각 유일
|
|
478
507
|
|
|
479
|
-
###
|
|
508
|
+
### 8.2 박스 [확정: 2026-04-01]
|
|
480
509
|
|
|
481
510
|
필드:
|
|
482
511
|
|
|
@@ -494,7 +523,7 @@ flowchart LR
|
|
|
494
523
|
- 박스 1개 = 단일 품목
|
|
495
524
|
- 유일성: 박스번호 유일
|
|
496
525
|
|
|
497
|
-
###
|
|
526
|
+
### 8.3 Location [확정: 2026-04-01]
|
|
498
527
|
|
|
499
528
|
필드:
|
|
500
529
|
|
|
@@ -510,7 +539,7 @@ flowchart LR
|
|
|
510
539
|
- 비즈니스 키: 코드 (사용자 부여, 수정 가능)
|
|
511
540
|
- 유일성: 활성 Location 내 코드 유일
|
|
512
541
|
|
|
513
|
-
###
|
|
542
|
+
### 8.4 재고 [확정: 2026-04-01]
|
|
514
543
|
|
|
515
544
|
필드:
|
|
516
545
|
|
|
@@ -526,31 +555,52 @@ flowchart LR
|
|
|
526
555
|
- 박스 1개는 동시에 1개 Location 에만 존재 (= 박스-재고 1:1)
|
|
527
556
|
- Location 1개에 박스 다중 가능
|
|
528
557
|
|
|
529
|
-
###
|
|
558
|
+
### 8.5 재고 스냅샷 [확정: 2026-04-01]
|
|
530
559
|
|
|
531
560
|
필드:
|
|
532
561
|
|
|
533
|
-
| 필드 | 타입 | 필수 | 비고
|
|
534
|
-
| -------- | --------------- | ---- |
|
|
535
|
-
| ID | 숫자 | O | 자동 부여
|
|
562
|
+
| 필드 | 타입 | 필수 | 비고 |
|
|
563
|
+
| -------- | --------------- | ---- | ----------- |
|
|
564
|
+
| ID | 숫자 | O | 자동 부여 |
|
|
536
565
|
| 날짜 | 날짜 | O | 스냅샷 일자 |
|
|
537
|
-
| 박스 | 참조 - 박스 | O |
|
|
538
|
-
| Location | 참조 - Location | O |
|
|
566
|
+
| 박스 | 참조 - 박스 | O | |
|
|
567
|
+
| Location | 참조 - Location | O | |
|
|
539
568
|
|
|
540
569
|
키/제약:
|
|
541
570
|
|
|
542
571
|
- 식별 키: ID (자동 부여)
|
|
543
572
|
- 비즈니스 키: (날짜, 박스) 조합 유일
|
|
544
573
|
|
|
545
|
-
|
|
574
|
+
### 8.6 DataLog [확정: 2026-04-01]
|
|
546
575
|
|
|
547
|
-
|
|
576
|
+
필드:
|
|
577
|
+
|
|
578
|
+
| 필드 | 타입 | 필수 | 비고 |
|
|
579
|
+
| -------- | ---- | ---- | ------------------------------------- |
|
|
580
|
+
| ID | 숫자 | O | 자동 부여 |
|
|
581
|
+
| 테이블명 | 문자 | O | 변경 대상 모델명 (예: `품목`) |
|
|
582
|
+
| 키 | 숫자 | O | 변경 대상 ID |
|
|
583
|
+
| 변경 전 | JSON | X | 컬럼별 이전 값. insert 시 null |
|
|
584
|
+
| 변경 후 | JSON | X | 컬럼별 변경 후 값. delete 시 null |
|
|
585
|
+
| 작업자 | 문자 | O | 세션 사용자 |
|
|
586
|
+
| 시각 | 일시 | O | 변경 commit 시각 |
|
|
587
|
+
|
|
588
|
+
키/제약:
|
|
589
|
+
|
|
590
|
+
- 식별 키: ID (자동 부여)
|
|
591
|
+
- 인덱스: (테이블명, 키, 시각) — 특정 엔티티 변경 이력 조회용
|
|
592
|
+
|
|
593
|
+
## 9. 외부 인터페이스
|
|
594
|
+
|
|
595
|
+
### 9.1 ERP 입고 통보 [확정: 2026-04-01]
|
|
548
596
|
|
|
549
597
|
- 상대 시스템: ERP
|
|
550
598
|
- 방향: WMS → ERP
|
|
551
599
|
- 전송 방식: REST API (POST)
|
|
552
600
|
|
|
553
|
-
|
|
601
|
+
관련 섹션: [화면.입고 스캔]
|
|
602
|
+
|
|
603
|
+
#### 자료 매핑
|
|
554
604
|
|
|
555
605
|
| ERP 필드 | WMS 출처 |
|
|
556
606
|
| -------- | --------------------- |
|
|
@@ -559,7 +609,7 @@ flowchart LR
|
|
|
559
609
|
| 박스번호 | [모델.박스.박스번호] |
|
|
560
610
|
| 발생시각 | 입고 등록 시각 |
|
|
561
611
|
|
|
562
|
-
예외
|
|
612
|
+
#### 예외 처리
|
|
563
613
|
|
|
564
614
|
- 호출 실패 (네트워크 오류, ERP 서버 일시 다운 등):
|
|
565
615
|
- 위험: 단순 재시도 시 ERP 에 같은 박스가 2번 입고 등록될 수 있음 (첫 호출이 실제로는 ERP 에 도달했는데 응답만 못 받은 경우 등)
|
|
@@ -567,23 +617,22 @@ flowchart LR
|
|
|
567
617
|
- 대처: 위 전제 하에 지수 백오프로 재시도, 최대 5회
|
|
568
618
|
- 재시도 한계 초과: 관리자 알림 (이후 수동 확인 필요)
|
|
569
619
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
## 9. 본문 외 결정사항
|
|
620
|
+
## 10. 본문 외 결정사항
|
|
573
621
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
- 직원 관리 사원번호 컬럼 추가
|
|
622
|
+
- 2026-04-01 [제외]: 직원 관리 사원번호 컬럼 추가
|
|
623
|
+
- 근거: 본 spec 범위 밖 — 인사 마스터 영역
|
|
577
624
|
- 후속 처리: sd-dev
|
|
578
625
|
- 자료 위치: 회의록.md L42
|
|
579
|
-
|
|
626
|
+
|
|
627
|
+
- 2026-04-01 [제외]: 재고 확인 화면 정렬 버그
|
|
628
|
+
- 근거: 본 spec 범위 밖 — 운영 잔손
|
|
580
629
|
- 후속 처리: 운영 잔손
|
|
581
630
|
- 자료 위치: 이슈 #128
|
|
582
631
|
|
|
583
|
-
|
|
632
|
+
- 2026-04-01: 출고 분석은 입고 끝나고 다음 단계
|
|
633
|
+
- 근거: 사용자 결정 — 입고 검증 후 출고 진행이 안전
|
|
634
|
+
- 자료 위치: 회의록.md L40-50
|
|
635
|
+
- 영향: §2.2 출고 OPEN 상태 유지
|
|
584
636
|
|
|
585
|
-
- 2026-04-
|
|
586
|
-
-
|
|
587
|
-
- 2026-04-01: 과거 재고 조회 요구 식별 → 재고 스냅샷 모델·자동 처리 추가, 재고 확인 화면에 기준일 필터 반영.
|
|
588
|
-
- 2026-04-01: ERP 입고 통보 식별 → 입고 스캔 등록 후속 동작에서 호출.
|
|
589
|
-
- 2026-04-05: 품목 관리·품목 등록·편집 sd-impl 완료 (구현 마커 부착).
|
|
637
|
+
- 2026-04-05: 품목 마스터 sd-impl 완료 (구현 마커 부착)
|
|
638
|
+
- 근거: 시연 검증 통과 (§4.1·§4.2)
|
|
@@ -49,10 +49,10 @@ meeting_eml/
|
|
|
49
49
|
embedded_xlsx/
|
|
50
50
|
README.md
|
|
51
51
|
_source.xlsx
|
|
52
|
-
workbook.meta.json ← defined names
|
|
52
|
+
workbook.meta.json ← defined names·sheet_code_map (있을 때)
|
|
53
53
|
sheets/
|
|
54
|
-
01_Sheet1.png
|
|
55
|
-
01_Sheet1.jsonl
|
|
54
|
+
01_Sheet1.png ← 시각 (서식·바탕색·border 모두)
|
|
55
|
+
01_Sheet1.jsonl ← 분석 데이터 (값·number_format·수식·merges 등)
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
형식별 산출물 매트릭스:
|
|
@@ -61,7 +61,7 @@ meeting_eml/
|
|
|
61
61
|
|---|---|---|---|
|
|
62
62
|
| pptx/ppt | `slides/<idx>_<title>.png` | `slides/<idx>_<title>.jsonl` (슬라이드별 노드) | `charts/*.data.json`, `images/`, `attachments/`, `macros/` |
|
|
63
63
|
| docx/doc | `pages/<NNN>.png` (시각 검증용) | `content.jsonl` (단일 시퀀스), `pages.meta.json` (PNG↔노드 매핑) | `images/`, `attachments/`, `macros/` |
|
|
64
|
-
| xlsx/xlsb/xls | `sheets/<idx>_<name>.png` | `sheets/<idx>_<name>.jsonl` (값·수식·시트 메타
|
|
64
|
+
| xlsx/xlsb/xls | `sheets/<idx>_<name>.png` (일반 시트만) | `sheets/<idx>_<name>.jsonl` (값·수식·시트 메타), `workbook.meta.json` | `charts/sheet<idx>_chart*.data.json` (일반 시트 안 차트 + Chartsheet 의 차트), `images/<sheet>_<cell>`, `attachments/`, `macros/` |
|
|
65
65
|
| pdf | `pages/<NNN>.png` | `pages/<NNN>.jsonl` (블록 bbox + 표 셀 단위) | `images/p<NNN>_b<bid>.<ext>`, `attachments/` (PDF 임베드) |
|
|
66
66
|
| eml/msg | — | `body.md` (평문 본문), `headers.json`, `images.rels.json` | `body.html` (원본), `attachments/` (컨테이너면 재귀) |
|
|
67
67
|
|
|
@@ -78,11 +78,14 @@ meeting_eml/
|
|
|
78
78
|
|
|
79
79
|
## xlsx jsonl 규약
|
|
80
80
|
|
|
81
|
-
시트별 `.jsonl
|
|
81
|
+
시트별 `.jsonl` — 분석 핵심 (값·number_format·수식·merges·hyperlinks·comments). 시각 표시 (바탕색·border·폰트)·frozen·dims 는 미보존 (PNG 가 시각 보조, 필요 시 `_source.xlsx` 직접 추출).
|
|
82
82
|
|
|
83
|
-
- 첫 줄: `{"_meta":{"
|
|
84
|
-
-
|
|
85
|
-
- `number_formats`: General(기본) 외
|
|
83
|
+
- 첫 줄: `{"_meta":{"merges":["A1:C1",...], "number_formats":{"E1":"yyyy-mm-dd",...}, "hyperlinks":{"D5":"http://..."}, "comments":{"E3":"메모"}}}`
|
|
84
|
+
- `merges`: 머지된 셀 영역 (셀 좌표 해석에 필수 — 머지 영역 안 빈 셀 오해 차단)
|
|
85
|
+
- `number_formats`: General(기본) 외 셀 표시 형식 — Date·통화·% 등 셀 값 의미 단서
|
|
86
|
+
- `hyperlinks`: 셀 URL (URL 자체가 셀 정보)
|
|
87
|
+
- `comments`: 셀 메모
|
|
88
|
+
- 비어있는 메타 키는 생략 (모두 비면 `{"_meta":{}}`)
|
|
86
89
|
- 데이터 줄: `{"r":11, "A":"P001", "I":7800, "J":12.5, "_f":{"I":"=SUM(...)", "J":"=I11*1.5"}}`
|
|
87
90
|
- `r`: 1-based 행번호 (Excel 동일)
|
|
88
91
|
- 열문자 키 (`A`·`B`·...·`AA`·...): 셀 값. 빈 셀은 키 생략
|
|
@@ -90,8 +93,22 @@ meeting_eml/
|
|
|
90
93
|
- 빈 행도 `{"r":N}` 한 줄 유지 → Read offset = 행번호 (오프바이원 차단)
|
|
91
94
|
- 값 타입: JSON 네이티브 (`int`·`float`·`bool`·`str`), datetime 은 ISO 8601 문자열
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
### Chartsheet (시트 자체가 차트)
|
|
97
|
+
|
|
98
|
+
xlsx 안 시트는 일반 Worksheet 외에 **Chartsheet** (셀 없이 차트 1개) 도 있을 수 있음.
|
|
99
|
+
|
|
100
|
+
- Chartsheet 는 `sheets/<idx>_<name>.jsonl` 미생성 (셀 없음)
|
|
101
|
+
- Chartsheet 의 차트 데이터: `charts/sheet<idx>_chart.data.json`
|
|
102
|
+
- README sheet_summaries 에 `(chart sheet — "...")` 명시
|
|
103
|
+
- 일반 시트·Chartsheet 통합 시트 순서 (idx) 대로 보존
|
|
104
|
+
|
|
105
|
+
### 워크북 단위 `workbook.meta.json`
|
|
106
|
+
|
|
107
|
+
시트 외 워크북 공통 정보 (있을 때만 생성):
|
|
108
|
+
|
|
94
109
|
- `defined_names`: `{"이름":["'Sheet1'!$A$1:$C$10", ...]}` (다중 destination 시 list 다수 항목)
|
|
110
|
+
- `sheet_code_map`: `{"Sheet1":"BOA", ...}` (VBA codeName → raw 시트명. 매크로 모듈 파일명과 매칭용)
|
|
111
|
+
- `pivots`: pivot table 정의 list. 각 항목 `{name, source, location, rowFields, colFields, pageFields, dataFields}`. 결과 셀은 시트별 jsonl 에 일반 셀로 들어감
|
|
95
112
|
|
|
96
113
|
## pptx jsonl 규약
|
|
97
114
|
|
|
@@ -146,15 +163,17 @@ paragraph 안 hyperlink 가 있으면 `hyperlinks`: `[{"text":"...", "url":"..."
|
|
|
146
163
|
|
|
147
164
|
페이지별 `pages/<NNN>.jsonl`. PDF 페이지는 원본 단위.
|
|
148
165
|
|
|
149
|
-
- 첫 줄: `{"_meta":{"page":N, "size":[w,h], "blocks":B, "tables":T, "table_cells":C}}`
|
|
166
|
+
- 첫 줄: `{"_meta":{"page":N, "size":[w,h], "blocks":B, "tables":T, "table_cells":C, "form_fields":F, "annotations":A}}`
|
|
150
167
|
- 노드 줄:
|
|
151
168
|
- `text_block`: `{"page":N, "block":B, "type":"text_block", "bbox":[x0,y0,x1,y1], "text":"..."}`
|
|
152
169
|
- `image_block`: `{"page":N, "block":B, "type":"image_block", "bbox":[...], "ref":"images/p001_b03.png"}`
|
|
153
170
|
- `table_cell`: `{"page":N, "type":"table_cell", "table_idx":T, "table_bbox":[...], "row":R, "col":C, "text":"..."}`
|
|
171
|
+
- `form_field`: `{"page":N, "type":"form_field", "name":"...", "field_type":"text", "value":"...", "bbox":[...]}` (PDF 양식 입력란)
|
|
172
|
+
- `annotation`: `{"page":N, "type":"annotation", "subtype":"Highlight", "bbox":[...], "content":"...", "author":"..."}` (주석·highlight·sticky note)
|
|
154
173
|
- 모든 블록 보존 (표 영역과 겹쳐도 skip 안 함) — find_tables 정확도 100% 가정 시 정보 손실 위험 회피. text_block·image_block·table_cell 노드가 동일 영역에 중복 출력될 수 있음. Claude 가 양쪽 비교 판단
|
|
155
|
-
- bbox 는 PDF 기준 좌표 (left-top, pt 단위,
|
|
174
|
+
- bbox 는 PDF 기준 좌표 (left-top, pt 단위, raw float)
|
|
156
175
|
|
|
157
|
-
heading 추출은 미적용 (PDF 는 style 정보 없음).
|
|
176
|
+
heading 추출은 미적용 (PDF 는 style 정보 없음). OCR 미적용 (스캔 PDF 는 image_block 만 추출).
|
|
158
177
|
|
|
159
178
|
## 인라인 이미지 매핑 (eml/msg)
|
|
160
179
|
|
|
@@ -166,6 +185,12 @@ heading 추출은 미적용 (PDF 는 style 정보 없음). 필요 시 본문 gre
|
|
|
166
185
|
- text/plain·HTML 둘 다 있을 때 → `body.md` 는 plain (placeholder 없음), `body.from_html.md` 가 변환본 (placeholder 포함)
|
|
167
186
|
- 인라인 이미지 없으면 `images.rels.json` 미생성
|
|
168
187
|
|
|
188
|
+
## TNEF (winmail.dat) 풀이
|
|
189
|
+
|
|
190
|
+
Outlook RTF 메일이 첨부를 `winmail.dat` 단일 binary (TNEF 형식) 로 패키징한 경우, `tnefparse` 로 내부 첨부 추출하여 `attachments/` 에 같이 풀어 둠. 원본 `winmail.dat` 도 유지 (원본 보존).
|
|
191
|
+
|
|
192
|
+
내부 첨부도 컨테이너 (xlsx·pptx 등) 면 재귀 풀이 (다른 첨부와 동일).
|
|
193
|
+
|
|
169
194
|
## eml/msg 본문 규약
|
|
170
195
|
|
|
171
196
|
본문 흐름 정확성(text/plain 우선) + 인라인 이미지 위치 단서(HTML→평문 변환본) 둘 다 보존:
|
|
@@ -183,8 +208,8 @@ heading 추출은 미적용 (PDF 는 style 정보 없음). 필요 시 본문 gre
|
|
|
183
208
|
## xlsb 클린업
|
|
184
209
|
|
|
185
210
|
- legacy → xlsx 변환 시 `_converted.xlsx` 는 임시 폴더에서만 처리 (산출 폴더에 미잔존)
|
|
186
|
-
- VBA
|
|
187
|
-
|
|
211
|
+
- VBA 매크로는 원본 코드 그대로 `macros/<모듈명>.vba` 저장 (변형 X)
|
|
212
|
+
- VBA 시트 객체명↔raw 시트명 매핑은 `workbook.meta.json` 의 `sheet_code_map` 키 (예: `{"Sheet1":"BOA","Sheet3":"Mapping"}`)
|
|
188
213
|
|
|
189
214
|
## 산출물 사용
|
|
190
215
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -160,6 +160,65 @@ def temp_workdir():
|
|
|
160
160
|
shutil.rmtree(d, ignore_errors=True)
|
|
161
161
|
|
|
162
162
|
|
|
163
|
+
def is_tnef(path: Path) -> bool:
|
|
164
|
+
"""TNEF (winmail.dat) 형식인지 검사. filename 또는 magic bytes."""
|
|
165
|
+
if path.name.lower() in ("winmail.dat", "win.dat"):
|
|
166
|
+
return True
|
|
167
|
+
try:
|
|
168
|
+
with open(long_str(path), "rb") as f:
|
|
169
|
+
magic = f.read(4)
|
|
170
|
+
return magic == b"\x78\x9f\x3e\x22"
|
|
171
|
+
except Exception:
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def unpack_tnef(path: Path, attachments_dir: Path) -> list[Path]:
|
|
176
|
+
"""TNEF (winmail.dat) 내부 첨부 추출. 추출된 path list 반환.
|
|
177
|
+
|
|
178
|
+
TNEF 아니거나 실패 시 빈 list. 원본 winmail.dat 은 유지 (원본 보존).
|
|
179
|
+
"""
|
|
180
|
+
if not is_tnef(path):
|
|
181
|
+
return []
|
|
182
|
+
ensure_pip("tnefparse")
|
|
183
|
+
try:
|
|
184
|
+
from tnefparse import TNEF
|
|
185
|
+
with open(long_str(path), "rb") as f:
|
|
186
|
+
t = TNEF(f.read())
|
|
187
|
+
except Exception:
|
|
188
|
+
return []
|
|
189
|
+
|
|
190
|
+
saved: list[Path] = []
|
|
191
|
+
for att in getattr(t, "attachments", []):
|
|
192
|
+
try:
|
|
193
|
+
name = None
|
|
194
|
+
lf = getattr(att, "long_filename", None)
|
|
195
|
+
if callable(lf):
|
|
196
|
+
try:
|
|
197
|
+
name = lf()
|
|
198
|
+
except Exception:
|
|
199
|
+
name = None
|
|
200
|
+
if not name:
|
|
201
|
+
name = getattr(att, "name", None)
|
|
202
|
+
if isinstance(name, bytes):
|
|
203
|
+
try:
|
|
204
|
+
name = name.decode("utf-8")
|
|
205
|
+
except UnicodeDecodeError:
|
|
206
|
+
name = name.decode("cp949", errors="replace")
|
|
207
|
+
if not name:
|
|
208
|
+
name = "tnef_attachment.bin"
|
|
209
|
+
data = att.data
|
|
210
|
+
if not data:
|
|
211
|
+
continue
|
|
212
|
+
except Exception:
|
|
213
|
+
continue
|
|
214
|
+
if not isinstance(name, str):
|
|
215
|
+
name = "tnef_attachment.bin"
|
|
216
|
+
dst = unique_path(attachments_dir, name)
|
|
217
|
+
write_bytes(dst, data)
|
|
218
|
+
saved.append(dst)
|
|
219
|
+
return saved
|
|
220
|
+
|
|
221
|
+
|
|
163
222
|
def save_source(input_path: Path, out_dir: Path) -> None:
|
|
164
223
|
ext = input_path.suffix.lstrip(".")
|
|
165
224
|
dst = out_dir / f"_source.{ext}"
|
|
@@ -136,6 +136,13 @@ def run(input_path: Path, out_dir: Path) -> None:
|
|
|
136
136
|
json.dumps(rels, ensure_ascii=False, indent=2),
|
|
137
137
|
)
|
|
138
138
|
|
|
139
|
+
# TNEF (winmail.dat) 풀이 — Outlook RTF 메일의 첨부 패키지 안 내부 첨부 추출
|
|
140
|
+
tnef_saved: list[Path] = []
|
|
141
|
+
for ap in saved_attachments:
|
|
142
|
+
extra = _common.unpack_tnef(ap, attachments_dir)
|
|
143
|
+
tnef_saved.extend(extra)
|
|
144
|
+
saved_attachments.extend(tnef_saved)
|
|
145
|
+
|
|
139
146
|
attachment_links: list[str] = []
|
|
140
147
|
for ap in saved_attachments:
|
|
141
148
|
size = ap.stat().st_size
|