다크모드
매입 P1(매입확정)/P2(지급충당) — FE 메인테이너 참고
⚠️ 2026-07-07 갱신:
payment콘솔은 P3/P6 슬라이스에서 확장됐다(선지급 현황·미식별출금 큐/식별 다이얼로그 신규,DialogOffsetArt의 초과 차단 UI 제거). 최신 구조는docs/handoff/frontend/purchasing-p3-p6.md참고 — 이 문서는 P1/P2 첫 슬라이스 as-built 기록으로 보존한다(§9 확장 포인트 중 "선지급" 항목은 P3/P6로 해소).정본 설계:
docs/superpowers/specs/2026-07-07-purchasing-p1-p2-design.md. BE 계약은docs/handoff/backend/purchasing-p1-p2.md참고. 이 문서는 "이 화면을 이어 개발하려면 무엇을 알아야 하나"에 집중한다.
1. 라우트
src/router/buildLineRoutes.js의 purchasing 라인이 소유(FRAMEWORK_LINES에 포함):
| 경로 | 뷰 |
|---|---|
/purchasing (landing) | src/views/purchasing/LandingView.vue |
/purchasing/console/transaction | src/views/purchasing/console/transaction/IndexView.vue |
/purchasing/console/payment | src/views/purchasing/console/payment/IndexView.vue |
/purchasing/console/vendor-ledger | src/views/purchasing/console/vendor-ledger/IndexView.vue |
/purchasing/console/tax-invoice | 기존 세금계산서 core(이번 슬라이스 범위 밖 — direction=received 메타만 부여) |
각 IndexView.vue는 얇은 셸이고 실제 화면 로직은 src/components/purchasing/console/{transaction,payment,vendor-ledger}/에 있다. 라우트 회귀 테스트: src/router/__tests__/buildLineRoutes.spec.js.
2. 컴포넌트 트리
src/components/purchasing/console/
├── transaction/
│ ├── HeaderOrg.vue
│ ├── MainOrg.vue — 목록 카드 + [매입 등록]/[세금계산서 가져오기(disabled)] + 회계 전기 footer
│ ├── blocks/DynamicTableOrg.vue — usePurchases 실배선 목록(행 클릭→상세 시트)
│ └── overlays/
│ ├── DialogCreatePurchaseArt.vue — P1 등록 폼(거래처·발생일·항목·계정·세금유형·증빙유형·공급가액)
│ └── SheetReadPurchaseTransactionArt.vue — 유형 A 상세 시트(매입내역·금액/미지급현황·분개미리보기)
├── payment/
│ ├── HeaderOrg.vue
│ ├── MainOrg.vue — 목록 카드 + [지급 등록] + 회계 전기 footer
│ ├── blocks/DynamicTableOrg.vue — usePayments 실배선 목록(행별 토글 상세 — 충당내역+분개미리보기)
│ └── overlays/
│ ├── DialogOffsetArt.vue — P2 1단계: 거래처→열린채무 FIFO 미리보기+초과/음수 가드→draft emit
│ └── DialogPaymentConfirmArt.vue — P2 2단계: draft prop 수신→recordPayment 커밋
└── vendor-ledger/
├── HeaderOrg.vue
├── MainOrg.vue — 거래처 Select + 요약 스트립(카드 규율 §8-3 비접힘 합계 행) + 테이블
└── blocks/DynamicTableOrg.vue — useVendorLedger 시계열 테이블(vendor-code prop)각 서브디렉터리 __tests__/에 대응 spec 있음(§9).
3. 소비 컴포저블
3-1. usePurchases (src/composables/usePurchases.js) — 매입 원장 진실원
모듈 reactive 싱글톤(buildSeed deep-clone 시드 — 회귀0 패턴, useForwarding/useAllocations 선례 동형). 노출 API:
js
const {
purchases,
recordPurchase,
payablesByVendor,
payableBalanceByVendor,
pay,
peekPaid,
unpay,
resetPurchases,
} = usePurchases();purchases: reactive 배열, 화면에서 직접 순회(테이블 sort는 컴포넌트가 담당).recordPurchase({vendorCode, purchaseDate, category, accountCode, supplyAmount, taxType, documentType, memo}):DialogCreatePurchaseArt가 호출. vatAmount·payable·payableAccountCode는 이 함수가 계산해 반환값(purchase 객체)에 포함.payablesByVendor()/payableBalanceByVendor(): 열린 채무 조회(화면은 이 파생 함수만 사용,paidByPurchase내부 오버레이를 직접 참조하지 않는다).pay/peekPaid/unpay: 콘솔 컴포넌트는 이 3개를 직접 호출하지 않는다 —usePayments가 소비. FE가 직접 호출할 일은 없다(테스트 제외).
3-2. usePayments (src/composables/usePayments.js) — 지급 원자 진실원
js
const { payments, recordPayment, cancelPayment, paidByVendor, resetPayments } = usePayments();recordPayment({vendorCode, amount, paymentDate, method, memo})→{ ok, id?, applied?, appliedBreakdown?, reason? }.ok:false를 무음으로 흘리지 않는다 —DialogPaymentConfirmArt.confirm()이result.ok를 체크해error.value에 표시(1단계 가드를 우회한 방어적 케이스만 도달하는 게 정상).cancelPayment(id): 컴포저블엔 있으나 화면 버튼 미배선(§10 확장 포인트).
3-3. usePurchaseJournal (src/composables/usePurchaseJournal.js) — GL 전표 뷰
js
const { vouchersPurchase, vouchersPayment, purchaseVouchers } = usePurchaseJournal();순수 파생(원자 소유 없음) — 매번 purchases/payments 최신 상태로 재계산. 콘솔은 이 함수들을 직접 호출해 미전기 카운트·전기 버튼·분개 미리보기 테이블을 만든다(재계산 캐싱 없음 — E-시리즈 useBillingJournal 선례 동형).
3-4. useVendorLedger (src/composables/useVendorLedger.js) — 거래처 원장 read-only VM
js
const { vendorLedger, ledgerFor, vendorCodes } = useVendorLedger();새 원자 없음 — usePurchases.purchases + usePayments.payments를 거래처별 시계열 병합. vendor-ledger/MainOrg.vue가 vendorLedger()로 Select 옵션을, ledgerFor(vendorCode)로 선택된 거래처의 entries/totalPayable/totalApplied/balance를 가져온다.
4. 데이터 흐름 요약
DialogCreatePurchaseArt.submit()
→ usePurchases().recordPurchase(...)
→ purchases[] 갱신(reactive)
→ transaction/DynamicTableOrg.vue가 자동 재렌더(rows computed)
→ transaction/MainOrg.vue의 unpostedCount(usePurchaseJournal().vouchersPurchase() 필터)도 자동 갱신
DialogOffsetArt(1단계, FIFO 미리보기·가드) → emit('draft', {...})
→ payment/MainOrg.vue: paymentDraft.value = draft
→ DialogPaymentConfirmArt(2단계, :draft="paymentDraft").confirm()
→ usePayments().recordPayment(draft)
→ { ok:true } → emit('registered') → paymentDraft = null
→ payments[] 갱신 → payment/DynamicTableOrg.vue·vendor-ledger 자동 재렌더두 다이얼로그 간 인계는 prop(:draft)+emit — 별도 store/composable 상태 없음. 등록 폼 검증(가드)은 1단계에서 전부 끝내고, 2단계는 커밋 전용(방어적 재검증만).
5. 회계 게이팅
useModuleSubscription().accountingEnabled — E-시리즈 선례 그대로:
- transaction/payment MainOrg:
v-if="accountingEnabled"로 "회계 전기" 버튼(footer) 숨김.unpostedCount/postAllPurchases/postAllPayments는 게이팅과 무관하게 항상 계산되지만 버튼만 숨긴다. - DynamicTableOrg(둘 다): "전기" 컬럼 자체를
v-if="accountingEnabled"로 숨김(colspan도 게이팅 반영 —:colspan="accountingEnabled ? N : N-1"). - SheetReadPurchaseTransactionArt / payment 행 확장: 분개 미리보기
<details>섹션을v-if="accountingEnabled"로 숨김. - vendor-ledger: 게이팅 없음(read-only 조망 — brief에서 "회계 게이팅 불요" 명시).
전기 액션 자체은 useGeneralLedger().post(voucherId)를 그대로 재사용(신규 상태머신 없음) — isPosted(id)도 동일 소스.
6. DS 패턴
- 콘솔 카드 구조: transaction/payment는 단일
card card-md+card-body(테이블) +card-footer(전기 버튼 + more_vert) — 유형 A/B 어느 쪽도 아닌 콘솔 전용 패턴(기존 sales/lease 콘솔 선례 동형). - vendor-ledger 요약 스트립: CLAUDE.md 카드 규율 §8-3("핵심 요약 수치는 카드 밖 — 비접힘 합계 스트립") 그대로 —
bg-neutral-minimal표면 위card card-sm card-inset-edged비접힘 행, 금액은title-lg(미지급 잔액만) / 나머지title-sm. - 행 클릭 상세 진입: transaction 테이블의 거래처 셀 =
font-medium cursor-pointer hover:underline+table-hover(CLAUDE.md §0-10 IA 컨벤션). - 상세 시트:
SheetReadPurchaseTransactionArt는 유형 A(다중 카드,bg-neutral-subtle+disclosure카드 3개 — 매입내역/금액·미지급현황/분개미리보기) —sheet-width-3xl(§0-5 집중폭 준수). - P2 2단계 다이얼로그 흐름:
DialogOffsetArt→DialogPaymentConfirmArt는 표준 sheet 패턴이 아니라 다이얼로그 릴레이(1단계 close+2단계 showModal을 코드에서 직접 호출) — CLAUDE.md §0 R/C/U 통합 규칙과는 별개 축(P2는 "등록 확정" 2스텝 위저드이지 CRUD read↔edit 토글이 아님). - 경고/에러:
alert alert-error-subtle(초과 채무)·alert alert-warning-subtle(금액 0 이하)·alert alert-info-subtle(안내) — E-시리즈 수납 콘솔 alert 어휘 그대로.
7. 회귀0 가드
seedDemoPurchases()/seedDemoPayments()는 main.js 부트스트랩에서만 호출(모듈 로드 시 자동 소비 안 함) —usePurchases/usePayments를 직접 import하는 회계/GL/재무제표 테스트가 매입 데모로 오염되지 않는다(seedDemoReceipts선례).resetPurchases()/resetPayments()는 각 스펙beforeEach에서 상태 초기화용 — 신규 스펙 작성 시 반드시 호출(다른 스펙의 잔여 상태가 섞이는 것 방지).
8. 테스트 위치
src/composables/__tests__/usePurchases.spec.js
src/composables/__tests__/usePayments.spec.js
src/composables/__tests__/usePurchaseJournal.spec.js
src/composables/__tests__/useVendorLedger.spec.js
src/components/purchasing/console/transaction/__tests__/MainOrg.spec.js
src/components/purchasing/console/transaction/blocks/__tests__/DynamicTableOrg.spec.js
src/components/purchasing/console/transaction/overlays/__tests__/DialogCreatePurchaseArt.spec.js
src/components/purchasing/console/transaction/overlays/__tests__/SheetReadPurchaseTransactionArt.spec.js
src/components/purchasing/console/payment/__tests__/MainOrg.spec.js
src/components/purchasing/console/payment/blocks/__tests__/DynamicTableOrg.spec.js
src/components/purchasing/console/payment/overlays/__tests__/DialogOffsetArt.spec.js
src/components/purchasing/console/payment/overlays/__tests__/DialogPaymentConfirmArt.spec.js
src/components/purchasing/console/vendor-ledger/__tests__/MainOrg.spec.js
src/components/purchasing/console/vendor-ledger/blocks/__tests__/DynamicTableOrg.spec.js
src/router/__tests__/buildLineRoutes.spec.js — purchasing 라인 경로 회귀9. 확장 포인트
- 거래처명 마스터 연결: 현재
vendorCode를 코드 그대로 표시(select 옵션도[...new Set(purchases.map(p=>p.vendorCode))]). 거래처 마스터(등록코드/이름 페어)가 생기면DialogCreatePurchaseArt의 vendorCode select·DialogOffsetArt/vendor-ledger Select를 이름 표시로 교체하고usePurchaseJournal.partnerOf()폴백을 제거. - drill-down: vendor-ledger 시계열 행에서 원 매입/지급 상세 시트로 이동하는 링크(현재는 read-only 텍스트 행만).
- cancelPayment UI:
usePayments().cancelPayment(id)는 구현돼 있으나 payment 콘솔에 취소 버튼이 없다. 추가 시payment/blocks/DynamicTableOrg.vue의 행 확장 영역에 취소 버튼 + 확인 다이얼로그, 취소 후vouchersPayment()가 자동으로 그 전표를 제외하는지 확인(이미 필터링돼 있음 —pay.status !== '취소'). - bulk-post 확인 다이얼로그: 현재
postAllPurchases/postAllPayments는 확인 없이 즉시 전기(E-시리즈 선례 동형 — 필요 시 확인 다이얼로그 추가는 두 콘솔 모두에 공통 적용). - 세금계산서 가져오기 연동: transaction/MainOrg의 "세금계산서 가져오기" 버튼은 현재
disabled(Phase 4 예정) — 세금계산서 허브 완성 시recordPurchase자동 호출 배선.