Files
portfolio-hub/docs/requirements/req-001/req-001_manual-cash-dividend.md

26 KiB

schema, id, title, revision, status, depends_on, applies_to, language
schema id title revision status depends_on applies_to language
proofline.requirement/v1 REQ-001 사용자 입력 현금 배당 원장 기록 1.0 approved
portfolio-hub
manual-ledger
ko

REQ-001 — 사용자 입력 현금 배당 원장 기록

1. 목적

사용자는 Broker에서 자동으로 수집하기 어려운 현금 배당의 실제 실수령액을 portfolio-hub의 manual ledger에 직접 기록할 수 있어야 한다. 기록된 배당은 전략 주문 가능 현금과 분리하여 profit_buffer에 반영하고, 입력 사실과 변경 후 portfolio 상태를 원장에서 조회하고 정정할 수 있어야 한다.

2. 배경

초기 범위에서는 Broker별 배당 조회 기능의 가용성과 형식 차이에 의존하지 않고 사용자가 실제 입금 내역을 직접 기록한다. 배당 실수령액은 투자 원금이나 주문 가능 현금이 아니므로 cash_balance에 합산하지 않는다. 잘못 입력한 기록은 기존 append-only REVERSAL 흐름으로 상쇄하며, 원본 원장을 수정하거나 삭제하지 않는다.

이 문서는 GitHub 이슈 #31의 확정 계약과 PR #33의 구현 증거를 요구사항으로 정리한 초안이다. 구현 코드와 테스트는 의도의 근거이지만, 이 문서의 approved 전환은 별도의 명시적인 사용자 승인을 필요로 한다.

3. 범위

포함

  • manual ledger action DIVIDEND
  • 배당 실수령액, 선택적 세금 metadata, 입금일과 메모 입력
  • 입력값 검증과 OpenAPI 계약
  • PostgreSQL 원장 저장과 portfolio snapshot 갱신
  • 중복 후보 경고와 사용자 확인 후 재제출
  • manual ledger 및 unified ledger 조회
  • 기존 REVERSAL을 이용한 배당 정정
  • Decimal 정밀도와 transaction 원자성
  • 기존 manual ledger action 하위호환
  • API·저장소·실제 PostgreSQL 검증

제외

  • Broker 배당 자동 수집 또는 동기화
  • 외화 환산과 다통화 손익
  • 배당 총액(gross amount) 입력
  • 세금 자동 계산·역산과 세율 정책
  • 배당수익 별도 집계
  • 실현손익 또는 기간 수익률 breakdown 변경
  • 배당금을 cash_balance나 전략 주문 가능 현금으로 이동하는 기능
  • portfolio-hub CLI의 배당 입력·표시 UX
  • trex-web의 현금 배당 label, 입력 form과 중복 확인 UX
  • reversal UI

4. 용어와 기준

  • 실수령액(net cash dividend): 세금 등을 제외하고 사용자 계좌에 실제 입금된 배당금. API에서는 amount로 표현한다.
  • 세금 metadata: 사용자가 참고 목적으로 입력하는 tax. 자동 계산하지 않으며 portfolio 상태에 영향을 주지 않는다.
  • 입금일: 사용자가 date-only 형식으로 입력하는 배당 입금 날짜. 기존 executed_at에 UTC 자정으로 저장한다.
  • 기록 시각: 서버가 원장 항목을 append한 시각인 recorded_at.
  • 중복 후보: 동일 portfolio, 동일 UTC 입금일, 동일 Decimal amount, 동일 note를 가진 reversal되지 않은 기존 DIVIDEND.
  • 정본 기준: 배당 사실과 변경 후 상태는 PostgreSQL portfolio_ledger의 append-only 기록 및 그 기록과 같은 transaction에서 갱신된 portfolio snapshot을 기준으로 한다.

5. 요구사항

REQ-001은 사용자 입력 현금 배당 기능 묶음의 identifier다. 고객 요구사항명세리스트와 구현 추적에서는 독립적으로 구현·검증할 수 있는 다음 항목을 사용한다.

REQ-001.01 — 현금 배당 입력 계약

시스템은 portfolio 소유 사용자가 manual ledger API를 통해 실제 배당 실수령액, 입금일, 선택적 세금 metadata와 메모를 입력할 수 있게 해야 한다.

Impact

  • Interfaces: POST /v1/portfolios/{portfolio_id}/ledger/manual, OpenAPI
  • Persistence: portfolio_ledger
  • Consumers: portfolio-hub API client, 후속 trex-web 연계
  • Migration: 신규 입금일 column 없이 기존 원장 metadata 구조 사용
  • Compatibility: 기존 manual action의 유효 입력 계약 유지
  • Deployment prerequisites: 기존 portfolio-hub PostgreSQL migration head 적용

REQ-001.02 — 배당에 따른 portfolio 상태 변경

시스템은 유효한 DIVIDEND의 amount만큼 profit_buffer를 증가시키고, cash_balance와 asset_quantity는 변경하지 않아야 한다. total_value_after는 증가한 buffer를 반영해야 하며 tax는 모든 상태 계산에서 제외해야 한다.

Impact

  • Interfaces: manual ledger 생성 응답, ledger 조회 응답
  • Persistence: portfolio snapshot, portfolio_ledger after-snapshot
  • Consumers: portfolio valuation과 unified ledger 소비자
  • Migration: 없음
  • Compatibility: 기존 action의 상태 계산식 유지
  • Deployment prerequisites: 없음

REQ-001.03 — 입금일과 기록 시각 분리

시스템은 date-only 입금일을 기존 executed_at에 UTC 자정(00:00:00Z)으로 저장하고, recorded_at은 서버 append 시각으로 독립적으로 유지해야 한다.

Impact

  • Interfaces: manual ledger 요청·응답, unified ledger 응답
  • Persistence: 기존 portfolio_ledger.executed_at, recorded_at
  • Consumers: 입금일 표시와 중복 판정 소비자
  • Migration: 입금일 전용 DB column 추가 없음
  • Compatibility: 기존 원장의 recorded_at 의미 유지
  • Deployment prerequisites: 없음

REQ-001.04 — 배당 원장 저장과 조회

시스템은 배당을 source=manual, canonical action=DIVIDEND로 append하고, unified ledger에서 배당 금액과 metadata 및 변경 후 snapshot을 직접 조회할 수 있게 해야 한다.

Impact

  • Interfaces: manual ledger 응답, GET /v1/portfolios/{portfolio_id}/ledger, OpenAPI
  • Persistence: portfolio_ledger
  • Consumers: portfolio-hub API client, trex-web 원장 화면
  • Migration: 기존 manual ledger metadata column 사용
  • Compatibility: broker 원장과 기존 manual action의 unified ledger 표현 유지
  • Deployment prerequisites: 없음

REQ-001.05 — 중복 후보 경고와 확인 재제출

시스템은 중복 후보를 자동 기록하지 않고 HTTP 409 경고와 일치한 원장 ID를 반환해야 한다. 사용자가 confirm_duplicate=true로 확인한 재요청은 실제 예외 기록으로 허용해야 한다.

Impact

  • Interfaces: manual ledger 요청의 confirm_duplicate, HTTP 409 오류 응답
  • Persistence: 중복 후보 조회와 조건부 append
  • Consumers: 중복 확인 UX를 제공하는 후속 trex-web
  • Migration: 없음
  • Compatibility: 기존 호출자가 새 배당 전용 field를 생략한 경우의 기존 계약 유지
  • Deployment prerequisites: 없음

REQ-001.06 — DIVIDEND reversal

시스템은 기존 append-only REVERSAL로 원본 배당의 실수령액만큼 profit_buffer를 감소시켜 배당 효과를 상쇄해야 한다. 원본 원장을 수정하거나 삭제해서는 안 된다.

Impact

  • Interfaces: 기존 manual REVERSAL 요청·응답
  • Persistence: 원본 DIVIDEND, 새 REVERSAL, portfolio snapshot
  • Consumers: manual ledger와 unified ledger 소비자
  • Migration: 없음
  • Compatibility: 기존 잔액 부족·대상 없음·중복 reversal 정책 유지
  • Deployment prerequisites: 없음

REQ-001.07 — Decimal 정밀도와 원자적 정합성

시스템은 수용한 Decimal 금액을 입력, 중복 비교, 원장 저장, snapshot 계산과 reversal에서 같은 의미로 유지해야 한다. 원장과 portfolio snapshot이 같은 값을 표현할 수 없는 요청은 어떤 상태도 기록하기 전에 원자적으로 거부해야 한다.

Impact

  • Interfaces: Decimal 입력 검증과 충돌 응답
  • Persistence: Decimal 원장 값과 portfolio snapshot
  • Consumers: 잔액·원장·reversal 정합성에 의존하는 모든 소비자
  • Migration: column type 변경 없음
  • Compatibility: 수용된 값의 정합성을 보장하면서 기존 manual action 계약 유지
  • Deployment prerequisites: 실제 PostgreSQL 경계값 검증

REQ-001.08 — 기존 manual ledger 하위호환

시스템은 DIVIDEND 도입으로 기존 manual ledger action의 입력, 상태 변경, 충돌과 reversal 계약을 변경해서는 안 된다.

Impact

  • Interfaces: 기존 manual ledger API 전체
  • Persistence: 기존 action의 원장 및 snapshot 갱신
  • Consumers: 기존 portfolio-hub API client
  • Migration: 없음
  • Compatibility: INVEST, BUFFER_OUT, BUFFER_IN, BUY, SELL, WITHDRAW, PROFIT, REVERSAL 유지
  • Deployment prerequisites: 기존 action 회귀 검증

REQ-001.09 — 권한과 민감정보 비노출

시스템은 기존 portfolio 소유권과 manual-write 권한 정책을 유지하고, 배당 기능으로 인해 API·로그·오류 응답에 secret 또는 account 식별정보를 추가로 노출해서는 안 된다.

Impact

  • Interfaces: 인증된 manual ledger API와 오류 응답
  • Persistence: actor와 기존 감사 metadata
  • Consumers: 사용자 및 운영 로그 소비자
  • Migration: 없음
  • Compatibility: 기존 인증·권한·daemon-managed portfolio 확인 정책 유지
  • Deployment prerequisites: 없음

6. 제약과 불변조건

  • amount는 0보다 큰 Decimal이어야 한다.
  • tax는 선택 입력이며 기본값은 0이고 음수일 수 없다.
  • amount, tax와 관련 수치는 원장의 NUMERIC(20,8) 범위와 scale을 준수해야 한다.
  • tax는 metadata이며 profit_buffer, cash_balance, asset_quantity, total_value_after 계산에 영향을 주지 않는다.
  • quantity, 거래 price, fee, 주문·체결 ID와 reversal 대상 ID는 DIVIDEND 입력 대상이 아니다.
  • date-only 입금일은 서버 timezone과 무관하게 UTC 자정으로 변환한다.
  • 원장 append와 portfolio snapshot 갱신은 원자적으로 함께 성공하거나 실패해야 한다.
  • 실패하거나 거부된 요청은 원장과 portfolio snapshot을 모두 변경하지 않는다.
  • 원장은 append-only이며 원본 배당을 직접 수정하거나 삭제하지 않는다.
  • 중복 비교는 binary float 근사가 아니라 저장 계약과 동일한 Decimal 의미를 사용한다.
  • profit_buffer가 부족한 DIVIDEND reversal은 기존 잔액 부족 정책에 따라 거부한다.

7. Acceptance Criteria

REQ-001.01-AC01 — 유효한 배당 입력 수용

Criterion

portfolio 소유 사용자가 action=DIVIDEND, 유효한 amount, date-only executed_at, 선택적 tax와 note를 제출하면 시스템은 manual ledger 항목을 생성하고 HTTP 201을 반환한다. tax 생략 시 저장 의미는 0이다.

Verification

  • amount>0, tax>=0, executed_at=YYYY-MM-DD인 정상 요청이 HTTP 201인지 확인한다.
  • tax를 생략한 요청의 저장·응답 값이 0인지 확인한다.
  • 생성 응답의 action, source, amount, tax, executed_at과 after-snapshot을 확인한다.

Verification Classification

  • Level: API | integration
  • Backend: PostgreSQL
  • Cases: positive | boundary

State Invariants

  • 성공 시 원장 항목이 정확히 하나 추가되어야 한다.
  • 성공 시 tax는 portfolio 상태를 변경하지 않아야 한다.
  • 실패한 입력은 원장과 snapshot을 변경하지 않아야 한다.

REQ-001.01-AC02 — 잘못된 배당 입력 거부

Criterion

시스템은 amount 또는 입금일이 없거나, 허용 범위를 벗어난 수치 또는 DIVIDEND와 무관한 field가 포함된 요청을 원장 기록 전에 거부한다.

Verification

  • 누락·0·음수·8자리 초과 소수·저장 범위 초과 amount를 검증한다.
  • 누락되거나 잘못된 date-only executed_at을 검증한다.
  • 음수·8자리 초과 소수·저장 범위를 벗어난 tax를 검증한다.
  • quantity, price, fee, target_ledger_entry_id 입력을 검증한다.
  • 각 거부가 HTTP 422이며 상태가 변하지 않는지 확인한다.

Verification Classification

  • Level: unit | API
  • Backend: 없음
  • Cases: negative | boundary | unchanged-state

State Invariants

  • 거부된 요청은 원장 항목을 만들지 않아야 한다.
  • 거부된 요청은 모든 portfolio snapshot을 유지해야 한다.

REQ-001.02-AC01 — 배당 상태 효과

Criterion

유효한 배당 기록 후 profit_buffer_after는 직전 값에 amount를 더한 값이어야 한다. cash_balance_after와 asset_quantity_after는 직전 값과 같아야 하며, total_value_after는 증가한 buffer를 반영해야 한다. tax 값은 이 계산에 영향을 주지 않는다.

Verification

서로 다른 amount와 tax 조합으로 기록 전후 portfolio와 ledger after-snapshot을 비교하고 각 계산식을 확인한다.

Verification Classification

  • Level: API | integration
  • Backend: PostgreSQL
  • Cases: positive | boundary | unchanged-state

State Invariants

  • profit_buffer 외의 현금·수량 상태는 변경되지 않아야 한다.
  • 동일 amount에 대해 tax 값이 달라도 상태 효과는 같아야 한다.
  • ledger after-snapshot과 portfolio snapshot은 같아야 한다.

REQ-001.03-AC01 — UTC 입금일 저장

Criterion

YYYY-MM-DD 입금일은 executed_at의 해당 UTC 날짜 00:00:00Z로 저장·반환되어야 한다. recorded_at은 서버 append 시각이어야 한다.

Verification

  • 서로 다른 날짜를 입력해 UTC 자정 저장과 조회를 확인한다.
  • 서버 환경 timezone과 무관한 결과를 확인한다.
  • recorded_at이 입력한 입금일로 대체되지 않는지 확인한다.

Verification Classification

  • Level: API | integration
  • Backend: PostgreSQL
  • Cases: positive | boundary

State Invariants

  • date-only 입력은 시간대 변환으로 전날 또는 다음 날이 되어서는 안 된다.
  • recorded_at의 감사 의미는 유지되어야 한다.

REQ-001.04-AC01 — Unified ledger 배당 조회

Criterion

기록된 배당은 manual ledger와 unified ledger에서 source=manual, action=DIVIDEND, executed_at, canonical amount, tax_amount, note 및 변경 후 snapshot으로 조회되어야 한다.

Verification

  • 배당 생성 후 source=manual filter를 사용해 unified ledger를 조회한다.
  • canonical amount가 별도 field로 존재하며 거래 price 해석에만 의존하지 않는지 확인한다.
  • OpenAPI의 request·response schema에 같은 field와 nullability가 나타나는지 확인한다.

Verification Classification

  • Level: API | integration
  • Backend: PostgreSQL
  • Cases: positive | unchanged-state

State Invariants

  • 조회는 원장과 portfolio 상태를 변경하지 않아야 한다.
  • broker entry와 기존 manual entry의 기존 표현을 변경하지 않아야 한다.

REQ-001.05-AC01 — 최초 중복 후보 경고

Criterion

동일 portfolio, UTC 입금일, Decimal amount, note의 reversal되지 않은 기존 배당이 있고 confirm_duplicate가 없거나 false이면 시스템은 새 항목을 기록하지 않고 HTTP 409 POTENTIAL_DUPLICATE_DIVIDEND와 matched_ledger_entry_id를 반환한다.

Verification

  • 첫 요청을 기록한 뒤 동일 요청을 재제출한다.
  • 409 code와 원본 entry ID를 확인한다.
  • 원장 수와 portfolio snapshot이 변하지 않았는지 확인한다.
  • Decimal 값이 다른 요청을 같은 값으로 오판하지 않는지 확인한다.

Verification Classification

  • Level: API | integration
  • Backend: PostgreSQL
  • Cases: negative | boundary | unchanged-state | concurrency

State Invariants

  • 경고 응답은 새 원장을 만들지 않아야 한다.
  • 경고 응답은 profit_buffer를 변경하지 않아야 한다.
  • 일치 ID는 실제 중복 후보를 정확히 가리켜야 한다.

REQ-001.05-AC02 — 확인된 중복과 reversal된 원본 처리

Criterion

중복 후보 요청에 confirm_duplicate=true가 있으면 시스템은 별도 배당 항목을 기록해야 한다. 이미 reversal된 배당 원본은 새로운 중복 후보에서 제외해야 한다.

Verification

  • 409 이후 동일 요청에 confirm_duplicate=true를 보내 HTTP 201과 별도 entry ID를 확인한다.
  • 원본을 reversal한 뒤 동일 배당을 다시 제출해 경고 없이 기록되는지 확인한다.

Verification Classification

  • Level: API | integration
  • Backend: PostgreSQL
  • Cases: positive | boundary

State Invariants

  • 확인된 중복은 각각 독립적인 append-only 원장이어야 한다.
  • reversal된 원본은 중복 억제 상태를 영구히 남기지 않아야 한다.

REQ-001.05-AC03 — 동시 중복 요청의 일관성

Criterion

동일 portfolio에 확인되지 않은 같은 배당 요청이 동시에 도착하더라도 새 배당 항목은 최대 하나만 기록되어야 하며 최종 원장과 portfolio snapshot은 일치해야 한다.

Verification

실제 PostgreSQL에서 같은 요청을 동시에 제출해 확인되지 않은 요청 둘이 모두 자동 기록되지 않으며, 거부된 요청은 상태를 변경하지 않고 최종 원장 수와 profit_buffer가 허용된 기록 수와 일치하는지 확인한다.

Verification Classification

  • Level: integration
  • Backend: PostgreSQL
  • Cases: concurrency | unchanged-state

State Invariants

  • lost update가 발생하지 않아야 한다.
  • 원장 수와 최종 snapshot 사이에 불일치가 없어야 한다.
  • 경고로 거부된 transaction은 부분 상태를 남기지 않아야 한다.

REQ-001.06-AC01 — 배당 reversal

Criterion

유효한 DIVIDEND를 reversal하면 원본 amount만큼 profit_buffer가 감소하고 원본 이전 값으로 복원되어야 한다. 원본과 reversal 원장은 모두 남아야 하며 현금과 수량은 변경되지 않아야 한다.

Verification

  • 정상 금액과 최소 단위 0.00000001을 각각 기록하고 reversal한다.
  • 원본 이전 buffer로 정확히 복원되는지 확인한다.
  • 원본 보존, reversal 대상 ID, 중복 reversal 거부와 buffer 부족 거부를 확인한다.

Verification Classification

  • Level: API | integration
  • Backend: PostgreSQL
  • Cases: positive | negative | boundary | unchanged-state

State Invariants

  • 원본 원장은 수정·삭제되지 않아야 한다.
  • reversal은 cash_balance와 asset_quantity를 변경하지 않아야 한다.
  • 실패한 reversal은 새 원장이나 부분 snapshot을 남기지 않아야 한다.

REQ-001.07-AC01 — Decimal round-trip

Criterion

API가 수용한 최대 8자리 소수의 amount와 tax는 PostgreSQL 저장, 조회, 중복 비교와 reversal에서 같은 Decimal 의미를 유지해야 한다.

Verification

  • 0.00000001 등 경계값을 기록하고 DB text 표현·API 응답·reversal 결과를 비교한다.
  • float로 변환하면 같아질 수 있는 서로 다른 Decimal 입력이 동일한 중복으로 판정되지 않는지 확인한다.

Verification Classification

  • Level: API | integration
  • Backend: PostgreSQL
  • Cases: positive | boundary

State Invariants

  • 수용된 원장 값과 snapshot 효과가 달라져서는 안 된다.
  • reversal 기준은 원본의 정확한 Decimal 금액이어야 한다.

REQ-001.08-AC01 — 기존 action 회귀 없음

Criterion

기존 manual action은 DIVIDEND 도입 전과 같은 유효 입력, 상태 효과와 충돌 정책을 유지해야 한다. 새 배당 전용 field를 사용하지 않는 기존 호출자는 변경 없이 동작해야 한다. 비-DIVIDEND action에 confirm_duplicate=false를 명시한 요청은 허용하고, confirm_duplicate=true를 명시한 요청은 원장 기록 전에 HTTP 422로 거부해야 한다.

Verification

  • 기존 action 전체의 정상·잔액 부족·reversal 경로를 실행한다.
  • 새 배당 전용 field를 생략한 기존 요청과 저장 데이터의 회귀를 확인한다.
  • 비-DIVIDEND action에 confirm_duplicate=false를 명시한 요청이 기존 action과 동일하게 처리되는지 확인한다.
  • 비-DIVIDEND action에 confirm_duplicate=true를 명시한 요청이 HTTP 422이며 상태가 변하지 않는지 확인한다.

Verification Classification

  • Level: API | integration
  • Backend: PostgreSQL
  • Cases: positive | negative | boundary | unchanged-state

State Invariants

  • 기존 action의 계산식과 원장 표현이 변경되지 않아야 한다.
  • 새 배당 전용 field의 기본값은 기존 호출자를 깨뜨리지 않아야 한다.
  • 비-DIVIDEND action의 confirm_duplicate=true 거부는 원장과 portfolio snapshot을 변경하지 않아야 한다.

REQ-001.09-AC01 — 권한 유지와 민감정보 비노출

Criterion

배당 기록은 기존 portfolio 소유권과 manual-write 확인 정책을 통과한 사용자에게만 허용되어야 한다. 응답·오류·로그는 이 기능으로 인해 secret, credential 또는 불필요한 account 식별정보를 추가로 노출하지 않아야 한다.

Verification

  • 기존 인증·소유권·daemon-managed portfolio 확인 경로를 실행한다.
  • 정상 응답, 422, 409와 persistence 오류의 body 및 rendered log를 민감정보 sentinel로 검사한다.

Verification Classification

  • Level: API | integration
  • Backend: PostgreSQL, application log
  • Cases: positive | negative | unchanged-state

State Invariants

  • 권한이 없는 요청은 원장과 snapshot을 변경하지 않아야 한다.
  • 오류 경로에서도 credential과 connection 정보가 노출되지 않아야 한다.

8. 오류 및 충돌 계약

  • 유효하지 않은 schema 입력은 HTTP 422로 거부하고 상태를 변경하지 않는다.
  • 중복 후보의 최초 요청은 HTTP 409 POTENTIAL_DUPLICATE_DIVIDEND와 matched_ledger_entry_id를 반환한다.
  • 사용자 확인이 필요한 daemon-managed portfolio 정책과 disabled portfolio 정책은 기존 HTTP 409 계약을 유지한다.
  • reversal 대상 없음, 중복 reversal과 잔액 부족은 기존 manual ledger 충돌 계약을 유지한다.
  • 입력 검증 또는 저장 실패로 요청을 기록할 수 없으면 부분 기록 없이 거부한다.
  • DB 오류 또는 transaction 실패 시 원장 append와 portfolio snapshot update를 모두 rollback한다.

9. 데이터 및 인터페이스 계약

요청

  • action: DIVIDEND
  • amount: 필수, >0, Decimal, 최대 20자리·소수점 이하 최대 8자리
  • tax: 선택, 기본값 0, >=0, Decimal, 최대 20자리·소수점 이하 최대 8자리
  • executed_at: 필수 date-only YYYY-MM-DD
  • note: 선택 문자열
  • confirm_duplicate: 선택 boolean, 기본값 false

confirm_duplicate는 DIVIDEND의 중복 확인 계약에 사용한다. 비-DIVIDEND action에서는 명시적 false를 허용하고 명시적 true를 HTTP 422로 거부한다.

허용하지 않는 DIVIDEND 입력

  • quantity
  • 거래 price
  • fee
  • target_ledger_entry_id
  • 주문 또는 체결 식별자

생성 응답과 unified ledger

  • ledger_entry_id
  • portfolio_id
  • source=manual
  • action=DIVIDEND
  • canonical amount
  • tax 또는 unified ledger의 tax_amount
  • UTC 자정의 executed_at
  • 서버 append 시각 recorded_at
  • note
  • cash_balance_after
  • asset_quantity_after
  • profit_buffer_after
  • total_value_after

저장 계약

  • 입금일 전용 DB column을 추가하지 않고 기존 executed_at을 사용한다.
  • tax는 참고 metadata로 저장한다.
  • 배당 사실과 after-snapshot은 source=manual인 append-only 원장으로 보존한다.

10. 하위호환성

  • 기존 manual ledger action의 필수·선택 field와 상태 계산을 유지한다.
  • 기존 broker ledger write와 unified ledger 조회 계약을 유지한다.
  • 새 배당 전용 field를 사용하지 않는 기존 action 요청을 변경 없이 허용한다.
  • REVERSAL의 대상 없음, 잔액 부족과 중복 reversal 정책을 변경하지 않는다.

11. 검증 계획

  • API schema와 OpenAPI에서 DIVIDEND action 및 입력·응답 field 검증
  • 배당 기록 전후 portfolio와 ledger after-snapshot 비교
  • UTC 입금일과 서버 recorded_at 분리 검증
  • 최초 중복 409, 확인 재제출, reversal된 원본 제외 검증
  • 동시 중복 요청의 최대 1건 기록과 최종 원장·snapshot 일치 검증
  • 최소 단위와 저장 범위 경계값의 실제 PostgreSQL Decimal round-trip 검증
  • 입력 또는 저장 실패의 pre-write rejection과 rollback 검증
  • 기존 action과 기존 저장 데이터의 하위호환 회귀
  • 응답·오류·로그의 민감정보 비노출 검증
  • 문서와 OpenAPI 계약 정합성 검증

Test Traceability Contract

  • 이 requirement 문서에는 검증 의도와 판정 조건만 기록한다.
  • 실행 가능한 테스트 정의는 별도 test catalog artifact에서 관리한다.
  • 각 테스트는 안정적인 identifier와 verifies 목록으로 하나 이상의 REQ-001.XX-ACYY를 참조한다.
  • 테스트 정의의 존재, 자동화 여부, 특정 commit에서의 실행 여부와 PASS 결과를 별도 상태로 관리한다.
  • 실행 결과는 requirement나 test definition을 수정하지 않고 별도 evidence에 기록한다.
  • Evidence는 최소한 REQ-001 revision, 검증한 commit SHA, runtime/backend, 결과와 실행 시각을 포함한다.

12. 추적성

  • 근거 이슈: https://github.com/fourblocks2020/portfolio-hub/issues/31
  • 구현 PR: https://github.com/fourblocks2020/portfolio-hub/pull/33
  • 검토한 PR head: 6df3e4ba029cc465bf28030767190d6af55c5402
  • 관련 설계 문서:
    • docs/interface-contract.md
    • docs/data-model.md
    • docs/runtime-flows.md
  • 관련 requirement: 없음

13. 확정된 결정

  • 사용자는 실수령액 amount를 입력한다.
  • tax는 기본값 0인 선택적 non-negative metadata이며 자동 계산하지 않는다.
  • 입금일은 새 DB column 없이 기존 executed_at에 저장한다.
  • date-only 입금일은 UTC 자정으로 변환한다.
  • 최초 중복 후보 요청은 409로 거부하고 confirm_duplicate=true인 재요청만 기록한다.
  • 구현 범위는 portfolio-hub API·DB·OpenAPI·테스트이며 UI는 별도 연계 범위다.
  • 잘못 입력한 배당은 기존 append-only REVERSAL로 정정한다.
  • 비-DIVIDEND action에서는 confirm_duplicate=false를 허용하고 confirm_duplicate=true를 HTTP 422로 거부한다.

14. Open Questions

  • 없음

15. 승인 기록

  • 상태: approved
  • 승인 주체: genichin
  • 승인 근거: 이 대화에서 REQ-001 승인과 비-DIVIDEND confirm_duplicate=false 허용/true HTTP 422 결정을 명시적으로 확정함