Frame
한 줄 소개: 한국 회계기준(KSME · K-GAAP)을 따르는 복식부기 회계 백엔드. 법인마다 장부가 물리적으로 분리되고, 모든 쓰기는 되돌릴 수 없는 감사 로그로 남는다. 사람은 웹 화면으로, AI 에이전트는 MCP 93개 도구로 같은 장부를 다룬다.
주소
| 용도 | 주소 |
|---|---|
| MCP 엔드포인트 | https://frame.axelabs.ai/frame/mcp |
| 운영 웹 UI | https://frame.axelabs.ai/frame/ |
| 스키마 목록 | https://frame.axelabs.ai/frame/schemas |
세 주소 모두 인증이 필요하다 — 로그인하지 않고 열면 401 이 돌아온다(브라우저는 로그인 화면으로 간다). 호스트 맨 주소 https://frame.axelabs.ai 는 /frame/ 으로 옮겨 준다.
고객 전용 인스턴스는 같은 호스트의 경로에 선다 — 웹 https://frame.axelabs.ai/t/<tenant>/frame/, MCP https://frame.axelabs.ai/t/<tenant>/frame/mcp. 입구는 모두에게 같다: 어느 경로로 들어와도 로그인한 계정의 테넌트가 화면을 정하고, 다른 테넌트의 입구로 들어온 사람은 로그인 직후 자기 자리로 옮겨진다(그 입구에서는 세션을 만들지 않는다). 인스턴스마다 쿠키 경로가 겹치지 않아 한 인스턴스의 세션이 다른 인스턴스로 실리지 않는다. 예전 주소 https://axe.axelabs.ai/frame/... 는 호환용으로 남아 MCP 커넥터는 그대로 쓰고, 브라우저 페이지는 frame.axelabs.ai 의 같은 경로로 옮겨 준다.
무엇을 하는가
- 복식부기 장부 — 차변·대변이 맞지 않는 분개는 애초에 기재되지 않는다. 분개 등록은 다층 검증을 통과해야 커밋된다.
- 개인정보 암호화 — 법인별 salt는 완성된 값만 원자적으로 확정하고 디스크 저장을 확인한 뒤 사용한다. 첫 접근이 동시에 일어나도 빈 salt를 읽는 경합을 막으며, 기존 salt를 덮어쓰지 않는다.
- 법인별 격리 — 법인·펀드마다 별도 스키마. 한 법인의 조회가 다른 법인의 데이터에 닿지 않는다. 법인(corporate)과 펀드(개인투자조합·벤처투자조합)를 같은 평면에서 다룬다.
- append-only 원장 — 원천 거래(입출금)와 분개 라인은 수정·삭제 권한 자체가 없다. 정정은 역분개로만 한다. 모든 쓰기는 감사 로그에 1행씩 남는다.
- 원천 데이터 자동 적재 — 은행·카드·홈택스 등에서 받은 파일을 올리면 포맷을 자동 인식해 원천 거래로 적재한다. 한 번 인식한 포맷은 캐시되어 다음부터 즉시 처리된다.
- 재무제표 4종 — 재무상태표 · 손익계산서 · 현금흐름표(간접법) · 자본변동표. 시산표는 임의 시점으로 조회할 수 있다.
- 증빙 보관 — 모든 증빙은 내용 해시로 주소화해 보관하고, 분개에서 증빙까지 링크가 끊기지 않는지 정기적으로 검사한다.
- 외부 재무 서비스 수집기 — AXE 법인과 고객 전용 인스턴스가 클로브의 공식 MCP에서 은행 거래·세금계산서를 받아 원천 행으로 적재한다. 조회 도구 이름 허용 목록과 법인별 고정 워크스페이스를 지킨다. 도구 목록 변경은 자동 비교하고, 쓰기 도구 변경과 호환되는 조회 도구 변경은 기록하며 계속한다. 조회 도구 삭제·쓰기 전환·비정상 도구 정의·인자 또는 응답 적재 계약 불일치면 멈추고 알린다. 응답의 필수값은 거래 ID·일자와 계좌 식별로 제한하며 선택 금액·통화·빈 목록은 기존 기본값으로 처리한다. AXE 세금계산서의 13자리 개인 식별번호는 저장 전에 마스킹하고, 승인번호 형식이 맞지 않으면 증빙 생성을 보류한다. 고객 증빙은 재현 계약에 따른 원문 보존 정책을 유지한다. 같은 기간의 재수신은 중복을 걸러 내고 회차별 건수·합계·잔액 흐름을 점검한다. AXE 법인의 카드 승인내역을 적재하며, 계좌별 전환 시점 이하 거래는 대조에 사용하고 해외 승인의 환산 차이도 제한된 범위에서 짝지어 처리한다. 전환 시점 이하에서는 이전 수집에 빠진 전액취소만 원승인과 연결해 추가하고, 원승인이 이미 분개됐으면 정정 필요를 표시한다. 모호한 카드 계좌와 검토가 필요한 취소는 보류하고 이슈로 남긴다. 현금영수증은 실제 응답으로 검토한 매핑에 따라 수집에 동의한 법인만 적재한다. 상호·메모 칸은 저장하지 않고, 식별번호가 개인 식별번호 형태이면 그 행을 보류하고 알린다.
- AXE 롯데카드 수집 일원화 — 롯데 자체 스크래퍼는 비활성으로 유지하고 클로브 카드 적재로 일원화한다. 전환 시점 이하 거래는 해외 승인 환산 차이를 포함해 대조하고, 이전 수집에 없는 취소만 원승인에 연결해 보충한다. 이미 분개된 원승인은 정정 대상으로 표시하며, 해소된 보류와 경보는 자동으로 닫는다.
- 고객 전용 인스턴스 옵션 — 외부 AI 호출 차단(양식 자동 판별·AI 분류를 쓰지 않는다), MCP 응답의 거래처·적요 가림, 그 고객 테넌트가 아닌 계정의 로그인·MCP 접근 차단. 기본은 모두 꺼져 있다. 고객 전용 외부 재무 서비스 수집기는 외부 AI 차단과 테넌트 차단이 켜진 인스턴스에서 돈다. AXE 법인 수집은 AXE 인스턴스에서 실행하며 이 두 스위치를 기동 조건으로 강제하지 않는다. 가림은 고객이 고르며 수집기의 조건이 아니다.
- 결산 — 회계기간을 열고(open) → 가마감(soft close) → 확정(hard close) 으로 전이시킨다. 확정된 기간에는 기재가 막힌다. 전기 마감 잔액을 당기 기초로 이월한다.
쓰는 방법
| 표면 | 대상 | 내용 |
|---|---|---|
| MCP | AI 에이전트 | Claude 등 MCP 클라이언트에 커넥터로 등록하면 아래 93개 도구를 그대로 쓴다. 조회·기재·결산이 전부 도구로 노출된다. |
| 운영 웹 UI | 사람 | 브라우저에서 로그인해 회계기간·계정과목·거래처·세금계산서 정기 발급 스케줄을 직접 관리한다. 도구 호출이 어색한 운영 작업용. 실제 발급은 화면이 아니라 자동 실행기(워커)만 한다. |
| Export | 외부 회계법인 | 시산표·총계정원장 등을 CSV/JSON 으로 내보낸다. |
인증
Microsoft 계정으로 로그인한다. MCP 클라이언트는 커넥터 등록 시 표준 OAuth(PKCE) 흐름을 타고, 브라우저는 같은 계정으로 SSO 한다. 인증되지 않은 MCP 요청에는 401 과 함께 인가 서버 위치를 알려주는 표준 메타데이터가 돌아가므로, 클라이언트가 흐름을 자동으로 이어간다.
권한은 법인 × 스코프로 부여된다.
| 스코프 | 할 수 있는 일 |
|---|---|
read | 조회 전반 (잔액·시산표·재무제표·분개·증빙·거래처) |
write | 분개 기재·역분개, 파일 적재, 증빙 등록, 거래처·계좌 관리 |
close | 마감 미리보기·확정, 기간 전이, 기초 이월, 거래처 병합 |
reopen | 이미 소프트클로즈된 기간에 다시 기재하기 — post_journal·reverse_journal 의 allow_soft_closed 를 실제로 허가하는 스코프 |
admin | 법인 등록, 계정과목 신설·수정·삭제, 회계기준 정책 관리 |
고객 전용 인스턴스는 그 고객 테넌트의 계정만 받는다 — 다른 테넌트 계정은 MCP 에서 TENANT_MISMATCH, 웹에서는 로그인 거부(AXE 운영자는 인스턴스의 허용 목록으로만). 같은 인스턴스는 외부 재무 서비스 수집기의 회차마다 받은 건수·적재 건수·점검 결과를 법인 장부 안의 회차 기록으로 남기고, 수집기는 운영자 CLI 명령 하나(1회 또는 상주 루프)로 돈다.
앞의 셋은 위가 아래를 겸한다(close 를 가지면 read·write 도 된다). reopen 과 admin 은 그 사다리 밖이라 토큰에 그 이름이 그대로 있어야 하고, close 나 admin 이 reopen 을 대신 서지 못한다. 그래서 write 만 든 호출자가 allow_soft_closed=True 를 넘기면 기재가 아니라 SCOPE_INSUFFICIENT 를 받는다 — 그 인자는 호출자가 채우는 불리언이라, 인가가 평범한 write 뿐이면 write 스코프 하나로 소프트클로즈된 기간이 열린다.
CLI
| 명령 | 동작·실패 의미 |
|---|---|
frame migrate | 공용 스키마와 등록된 법인·펀드 스키마를 마이그레이션한 뒤, 등록 누락·미스탬프 스키마를 포함한 모든 frame 엔티티 스키마가 실행 이미지의 최신 리비전(head)인지 확인한다. 하나라도 일치하지 않으면 실패하며 entity schema(s) not at entity head ... 와 대상 스키마·Fix: 안내를 출력한다. 등록 누락·미완료 또는 이미지와 DB 리비전 불일치를 안내에 따라 해소한 뒤 재실행한다. 법인 등록과 같은 잠금을 사용하므로 등록 중이면 완료까지 기다린다. |
frame clobe-seal-buyer-no --entity <고객 법인> [--apply] | 이미 적재된 클로브 세금계산서의 13자리 공급받는자 번호(주민·법인등록번호 형태)를 적재 단계와 같은 모양(법인 PII 키의 암호문)으로 바꾸는 1회 정리. 기본은 세기만 한다. --apply 는 한 트랜잭션에서 증빙·감사 기록·원천 응답 파일을 바꾸고, 원문이 남았거나 깨끗함을 증명할 수 없는 곳(못 읽거나 없는 원천 파일, 다른 법인이 함께 가리키는 파일)이 있으면 전부 되돌린다. 출력은 건수만이다. 저장소를 공유하는 어느 법인이든 수집기 회차 중이면 실패한다 — 회차 사이에 다시. AXE 법인은 대상이 아니다(적재 단계 가림). |
환경 변수
frame 스택의 망 구성과 원본 저장소 설정이다. 비밀값이 아니므로 컨테이너·호스트 환경에 평문으로 둔다.
| 이름 | 의미 | 기본값 | 언제 바꾸나 |
|---|---|---|---|
FRAME_WEB_MOUNTED_PREFIX | 이 인스턴스의 웹·MCP 경로 접두. /frame(기본) 또는 /t/<tenant>/frame 만 허용 — 그 밖이면 기동하지 않는다. 쿠키 경로·로그인 콜백·MCP 리소스가 모두 이 값을 따른다. | /frame | 고객 전용 인스턴스를 같은 호스트의 경로에 세울 때 |
FRAME_WEB_PUBLIC_BASE_URL | 웹의 공개 오리진(로그인 콜백·정본 링크의 앞부분). | https://axe.axelabs.ai | 웹 정문 호스트를 바꿀 때 |
FRAME_WEB_LEGACY_HOSTS | 옛 공개 호스트(쉼표 구분). 그 호스트로 온 브라우저 페이지 열람(GET/HEAD)만 공개 오리진의 같은 경로로 302 한다. MCP·메타데이터·JSON 요청은 옮기지 않는다. | 비움 | 웹 정문을 옮긴 뒤 옛 주소를 받을 때 |
FRAME_TENANT_HOMES | 다른 테넌트의 자리 표(JSON {tenant: 경로}). 값은 그 테넌트의 접두(/frame 또는 /t/<tenant>/frame)여야 하고 자기 자리를 가리킬 수 없다. 로그인한 계정의 테넌트가 표에 있으면 세션 없이 그 자리로 옮긴다. | 비움 | 같은 호스트에 인스턴스를 더할 때 |
FRAME_MCP_PUBLIC_BASE_URL | MCP 리소스의 공개 오리진(https://<host> 만). 비우면 기존 주소 그대로. | 비움 | 고객 전용 인스턴스의 MCP 를 서비스 호스트에서 받을 때 |
FRAME_CLOBE_TOKEN_FILE | 회전 토큰 파일의 영속 디렉터리 내 위치. 두 배포 색이 같은 파일을 공유한다. | AXE: 영속 볼륨 안의 토큰 파일(경로는 운영 문서) | 토큰 영속 마운트를 바꿀 때 |
FRAME_CLOBE_ACTIVE_ALIAS | 클로브 루프의 활성 배포 색을 판정하는 네트워크 alias. 1회 실행에는 적용하지 않는다. | AXE: frame-clobe-worker | 워커 alias를 바꿀 때 |
FRAME_CLOBE_CASH_RECEIPT_ENTITIES | 현금영수증 수집에 추가로 동의한 법인 목록(쉼표 구분). 응답 매핑은 실측으로 검토해 고정했다. | 비움(모든 법인 기본 off) | 법인이 현금영수증 수집에 동의했을 때 |
FRAME_CLOBE_CARD_ENTITIES | 카드 수집의 코드 기본 목록에 더할 법인 목록(쉼표 구분). 추가 법인도 응답 매핑 검토가 필요하다. | 비움(AXE axec·axev 기본 적재 활성; 전환 이후만 적재, 모호·취소 검토 건 보류) | 매핑 검토 후 법인을 추가할 때 |
FRAME_FILE_STORAGE_PATH | 원본 파일 저장소. 절대경로이며 쓰기 가능한 영속 마운트 안에 있어야 한다. MCP·worker·invoice-worker는 기동 시 검사한다. | /app/.local/files | 저장소 마운트 위치를 바꾸는 배치에서만 |
FRAME_ALLOW_EPHEMERAL_STORAGE | 1이면 영속 마운트 검사를 생략한다(절대경로 요건은 유지). 개발·테스트 전용이며 운영에서는 금지. | 0 | 로컬 개발·일회용 테스트에서만 |
FRAME_TRUSTED_PROXIES | 앱(blue/green)이 X-Forwarded-For · X-Forwarded-Proto 를 믿어 줄 피어 주소 집합. 콤마로 구분한 IP 또는 CIDR 이고, 목록 밖 피어가 붙여 보낸 forwarded 헤더는 무시된다. | 비우거나 미설정이면 loopback(127.0.0.1 · ::1) + RFC1918 세 대역. 셀프호스트 compose 는 이 값을 리버스 프록시 홉 전용 네트워크(frame_edge)의 서브넷 하나로 좁혀서 준다 | 앞단 프록시가 그 홉 밖에 있는 배치일 때만 |
FRAME_PROXY_TRUSTED_RANGES | 앞단 Caddy 프록시의 trusted_proxies — 상류가 붙여 보낸 forwarded 헤더를 그대로 통과시킬 소스 대역. 그 밖에서 들어온 요청의 헤더는 종전대로 프록시가 덮어쓴다. | Caddyfile 기본값 private_ranges(loopback + RFC1918). 셀프호스트 compose 는 여기에도 같은 frame_edge 서브넷을 준다 | 위와 같음 |
FRAME_SCRAPE_INGEST_URL | 미니 호스트의 스크래퍼(frame scrape-due, launchd)가 다운로드한 파일을 어디에 적재하느냐. 비우면 종전대로 호스트의 로컬 파일저장소 + DB 직결. 클라우드 frame-mcp 의 MCP URL(https://axe.axelabs.ai/frame/mcp)을 주면 파일을 MCP 도구 ingest_file_blob(parser=<source_key>) 로 보내 행(RDS)과 blob(EC2)이 한 곳에 모인다. 전송 토큰은 호스트가 이미 가진 frame 토큰 서명 비밀로 그 자리에서 발급(10분·해당 법인 write) — 클라우드가 검증하는 같은 비밀이라 별도 보관·회전 없음. 전송 실패분은 .local/scrape-outbox/<source>/<entity>/ 에 남아 다음 틱이 브라우저 없이 재전송한다(서버가 거부한 파일은 옆에 .err 를 남기고 하루 한 번만 재시도). 이미 내려받은 파일을 손으로 보내려면 frame scrape-resend <파일…> --entity axec --source lottecard(파일 보존, 같은 바이트면 서버가 duplicate 로 답하며 blob 만 채운다 — 컷오버 전 미니에만 남은 blob 의 백필 경로). | 비움(로컬) | 웨이브 8 이후 미니 .env 에 한 줄. 스크래퍼가 클라우드 DB 를 볼 때(FRAME_DB_HOST 가 RDS 포워드) 반드시 함께 켠다 — 안 켜면 split-brain |
두 값이 말하는 것은 같은 체인의 앞뒤 홉이다. 프록시가 믿는 대역이 상류 홉을 덮지 못하면 프록시가 상류의 X-Forwarded-Proto 를 스푸핑으로 보고 버린 뒤 자기 평문 리스너의 스킴으로 덮어쓰고, 앱이 믿는 집합이 프록시 홉을 덮지 못하면 앱이 프록시가 붙인 그 헤더를 아예 보지 않는다 — 어느 한쪽만 자기 앞 홉을 놓쳐도 리다이렉트가 평문으로 나가고 Secure 쿠키가 붙지 않아 로그인이 이뤄지지 않는다. 실제로 났던 회귀가 그 두 모양이다: 프록시 설정에 신뢰 선언이 아예 없었고, 앱은 웹 서버 기본값이 loopback 하나뿐이라 컨테이너 네트워크로 들어온 프록시를 신뢰하지 못했다. 지금은 양쪽 기본값이 그 홉을 덮으므로 두 변수를 비워 둔 배치에서도 서고, 변수는 기본값을 통째로 대체한다 — 배치마다 믿어야 할 프록시가 다르기 때문이며, 셀프호스트 compose 는 그것으로 신뢰를 홉 하나로 좁힌다. 닫히지 않는 것도 적어 둔다: 프록시가 게시한 포트로 들어오는 요청의 소스 주소는 언제나 그 홉의 게이트웨이라 프록시는 터널과 호스트-로컬 피어를 구별하지 못하고 그 헤더를 그대로 넘긴다 — LAN·tailnet 피어는 프록시와 앱의 호스트 포트를 loopback 에만 묶어 닫고, 이 두 값이 닫는 것은 앱 직행 위조이며, 호스트-로컬까지 닫으려면 터널이 frame_edge 에 직접 붙어 그 주소 하나만 신뢰해야 한다(전용 인입 홉 = B-frame-edge-dedicated-ingress-hop).
외부 요청은 터널 → 프록시 → frame 순으로 오고 마지막 홉이 컨테이너 네트워크라, frame 이 보는 피어는 loopback 이 아니라 프록시 컨테이너의 주소다. 그 주소가 이 집합에 없으면 forwarded 헤더가 버려져 리다이렉트의 Location 이 https 에서 http 로 강등되고(트레일링 슬래시 리다이렉트에서 실제로 났던 증상), 로그인 감사 줄에 실제 클라이언트 IP 대신 프록시 주소가 남는다.
반대로 아무 피어나 믿게 두면 두 값이 요청자 선택값이 된다 — 감사 로그의 IP 도, 모든 리다이렉트의 스킴도. 그래서 신뢰를 주소가 아니라 소속으로 정의한다. frame_edge 는 리버스 프록시와 MCP blue/green 만 올라가는 전용 네트워크이고 서브넷이 compose 에 못박혀 있어 셀프호스트 배치가 두 변수에 주는 값이 그 네트워크를 글자 그대로 가리킨다(셋이 어긋나면 테스트가 잡는다). 프록시는 이 네트워크에만 붙어 있으므로 신뢰 집합에 드는 홉은 그것 하나뿐이다. frame 워커와 blueprint 는 종전 기본 네트워크로 같은 이름의 MCP 에 계속 닿지만, 그 홉의 주소는 이 서브넷 밖이라 그들이 보낸 forwarded 헤더는 무시된다 — 브라우저가 아니라 Bearer 토큰 API 클라이언트라 잃는 것이 없고, 대신 감사 IP 와 리다이렉트 스킴을 그들이 정할 수 없게 된다.
/health 응답에는 forwarded_proto 필드가 있다 — 신뢰하는 프록시가 보낸 X-Forwarded-Proto 를 앱이 실제로 반영했는지 배포 검증에서 확인할 수 있도록, 그 요청에 대해 해석된 스킴(https 또는 http)을 그대로 보고한다. 운영자 CLI 의 frame 스왑은 컷오버 뒤 프록시를 거쳐 이 값이 https 인지 확인한다.
로그인 경로 점검 /frame/health/sso 은 인증 없이 실제 로그인과 같은 코드로 플랫폼 로그인의 토큰 교환과 서명키 조회를 시험해, 둘 다 되면 200·하나라도 막히면 503 을 돌려준다(결과는 60초 캐시, 비밀·주소·오류 문구는 싣지 않는다 — D-ops-120).
AXE 클라우드 배치는 compose 를 운영자 CLI 가 생성해 frame_edge 도 이 두 변수도 없다 — 거기서는 코드·파일 기본값(loopback + RFC1918)이 그대로 서서 프록시가 계속 신뢰받는다. 기본값을 홉 서브넷으로 좁히지 않는 이유가 그것이다: 기본값이 착지할 수 있는 위상보다 좁으면 그런 배치가 조용히 자기 프록시를 불신하게 되고, 좁히기는 좁은 위상을 실제로 가진 compose 가 준다.
상태 점검
| 경로 | 의미 |
|---|---|
/health | 프로세스 생존 확인. DB가 응답하지 않아도 200이므로 서비스 준비 여부는 아래 경로로 확인한다. |
/health/ready | DB 연결까지 확인. 성공하면 200, 실패하면 503. |
두 응답 본문의 slot은 응답한 배포 색(blue 또는 green)이다. 엣지에서도 어느 색이 응답했는지 구별할 수 있으며, 색을 지정하지 않은 단일 컨테이너·로컬 실행에서는 null이다. /health/ready의 503 응답에도 slot이 포함된다.
MCP 도구 93개
조회 — 잔액·재무제표
| 도구 | 스코프 | 설명 |
|---|---|---|
query_balance | read | 단일 계정의 시점 잔액 |
query_trial_balance | read | 현재 시산표 — 잔액이 있는 전 계정을 돌려주며(폐지된 is_postable=false 계정은 retired: true 로 표기하되 잔액은 숨기지 않는다 — 판정 기준은 포스팅과 같은 as_of_date 가 속한 정책 시대(명시 policy_id 우선)이며 그 시대에 없는 계정코드도 retired: true; 레거시 only_postable=true 는 구 leaf-only 필터를 그대로 재적용한다, DEPRECATED) 응답에 total_debit · total_credit · is_balanced 를 동반한다. is_balanced=false 는 원장 결함이다. |
trial_balance_at | read | 특정 시점 시산표 (검증용) |
trial_balance_export | read | 전 계정 차변·대변 양변 내보내기 |
balance_sheet | read | 재무상태표 (상위 계정 롤업) |
income_statement | read | 손익계산서 |
cash_flow_statement | read | 현금흐름표 (간접법). 활동 분류가 완전하면 검증 플래그가 참으로 오고, 분류 못 한 계정은 따로 표시된다 |
equity_changes_statement | read | 자본변동표 |
cap_table_at | read | 시점별 캡테이블 snapshot — shareholding_event 합산으로 총주식수·주주별 지분율·주식종류별 집계 재구성 (as_of_date 기준) |
simulate_round | read | 라운드 희석 시뮬레이션 — DB 기록 없는 순수 계산. pre-money·투자자별 신규 투자금·옵션풀 목표비중(pre-money carve-out, P = kS/(1−k) 대수해)으로 주당가격·신주·post 지분율·주주별 희석폭 산출 |
shareholder_register_doc | read | 주주명부 markdown 생성 — 마스킹된 주민/사업자번호만 사용(평문 PII 미노출), 주식종류·지분율·합계 표 포함 |
upsert_shareholder | write | 주주 등록/수정 — (entity_id, name) 기준 upsert 라 같은 이름으로 다시 부르면 중복 없이 그 행을 갱신한다. 주주명부 ingest 가 만드는 것은 이 주주 행까지다 |
add_shareholding_event | write | 지분 변동 이벤트 1건 append — cap_table_at 이 이 이벤트들을 합산한다. shares_delta 는 부호 있는 값(처분은 음수), effective_date 를 생략하면 event_date 와 같다. event_kind 가 허용 enum 밖이거나 shares_delta 가 0 이거나 effective_date 가 event_date 보다 앞서면 INVALID_SHAREHOLDING_EVENT 로 거부. ingest 는 이벤트를 만들지 않으므로 유상증자·CB전환 반영은 이 도구로 한다 |
general_ledger | read | 총계정원장 — 계정별 거래 + 누계. counterparty_id 로 그 거래처가 걸린 라인만(거래처별 보조부), hide_reversals=true 로 검증된 원본+역분개 쌍을 양쪽 다 숨겨 살아있는 항목만 볼 수 있다(기본 false) |
vat_summary | read | 부가세 신고 지원 집계 — 기간 × 법인의 매출세액·매입세액·차액. 라인 집계이지 최종 납부세액이 아니다 (의제매입·신용카드·대손 공제 미반영) |
조회 — 분개·거래·증빙
| 도구 | 스코프 | 설명 |
|---|---|---|
list_journals | read | 분개장 목록. hide_reversals=true 로 검증된 원본+역분개 쌍을 양쪽 다 숨긴다(기본 false) |
get_journal | read | 단일 분개 상세 (라인 + 출처 링크) |
journal_listing | read | 분개 리포트 |
list_raw_transactions | read | 원천 거래(은행 입출금 등) 목록. 페이지네이션 상태를 함께 반환한다 |
search_evidence | read | 증빙 전문 검색. 세금계산서는 공급받는자 사업자번호(buyer_biz_no)를 숫자 10자리일 때만 싣는다 — 개인 발행분(13자리)은 비운다 |
list_open_items | read | 미결 항목 |
list_periods | read | 회계기간 목록 |
list_sub_entities | read | 자회사·펀드 등 하위 entity |
list_entity_members | read | 그 entity 에 접근하는 사람 목록 — 명시 매핑된 이메일과 각자의 역할, 도메인 단위 허용. 읽기만 하며 권한을 바꾸지 않는다 |
조회 — 결의서·고정자산·급여
| 도구 | 스코프 | 설명 |
|---|---|---|
list_resolutions | read | 결의서 목록 |
get_resolution | read | 단일 결의서 상세 (연결된 분개 포함) |
query_effective_resolutions | read | 특정 시점에 유효한 결의서 |
list_fixed_assets | read | 고정자산 목록 |
query_asset_book_value_at | read | 고정자산의 시점 장부가액 |
list_pending_payroll | read | 대기 중인 급여 지급 건 |
기재 — 분개
| 도구 | 스코프 | 설명 |
|---|---|---|
post_journal | write | 분개 등록. 다층 검증 통과 시에만 커밋. 원천 거래가 정확히 하나로 특정되면 거래처를 규칙에 따라 자동 태깅한다 (명시값이 항상 우선) |
reverse_journal | write | 역분개 — 반대 분개를 새로 만들고 원본을 reversed 로 표시한 뒤 서로 링크한다. 이미 회수된 매출채권을 다시 살리는 방향의 역분개는 산술 불변식으로 거부한다. 다른 전표의 역분개(counter-entry)는 다시 역분개할 수 없다(REVERSAL_CHAIN_FORBIDDEN) — 되살리려면 post_journal 로 신규 전표를 친다 원 전표에 걸린 결의 중 역분개 일자에 유효한 것(status=effective, effective_from ≤ 일자 ≤ effective_until)만 역분개에 이어받아(link_reason = "inherited from reversed journal #N") 결의 필수 규칙을 원 전표와 같은 근거로 충족한다 — 이어받을 유효 결의가 없으면 종전처럼 거부되고, 취소·대체·만료 결의는 이어받지 않는다(도구 시그니처 불변, frame#48). |
relink_reversal | write | 수동으로 만든 반대 분개를 원본과 사후 연결. 계정별 순액이 0 이 아니면 거부. 새 분개를 만들지 않는다. 원본의 기간이 닫혀 있으면 거부한다 — 상태와 역분개 링크가 둘 다 원장이라 되열고 다시 부른다 |
flag_uncertainty | write | 판단이 필요한 건을 미결로 올린다 |
resolve_open_item | write | 미결 해결 |
update_conversation_context | write | 미결에 붙은 대화 맥락 갱신 |
reconcile_and_propose | write | 대사 후 분개 제안 |
기재 — 적재·증빙
| 도구 | 스코프 | 설명 |
|---|---|---|
ingest_file_blob | write | 파일을 인라인(base64, content_base64) 또는 샌드박스 상대경로(source_path)로 올려 원천 거래로 추출 — 둘 중 정확히 하나. source_path 는 운영자가 읽기 전용 샌드박스 디렉터리를 설정한 인스턴스에서 승인된 워크로드 주체만 쓸 수 있고(..·절대경로·심볼릭 링크·빈 파일 거부, 256 MiB 상한), 설정이 없으면 거부된다(기본). 같은 바이트는 어느 모드로 보내도 같은 요청으로 취급된다(멱등 키·충돌 판정이 내용 기준). filename 이 source_file 의 원본 파일명·storage_url 에 그대로 남는다. 선택 인자 parser — lottecard 면 롯데 법인카드 CP2 승인내역 고정컬럼 파서(법인전체 export 를 카드별 계좌로 분배, 호스트 스크래퍼와 같은 파서)로 보내고 응답이 cards[]·raw{inserted,skipped,duplicate}·file_status; lottecard_statement 면 롯데카드 청구명세서 파서 — 머리글·카드번호를 먼저 검증해 양식이 다르거나 다른 카드 행이 섞이거나 카드가 기존 계좌 둘 이상에 맞으면 아무것도 쓰지 않고 거부한다. 승인내역 스크래퍼 행과 승인번호가 같은 경우에만 같은 거래로 인정해 다시 넣지 않고(짝은 검토 항목에 기록해 같은 행이 두 번 짝지어지지 않음), 승인번호가 같은데 날짜·금액·방향이 엇갈리거나(거래일 ±45일 안) 승인번호 없는 행이 같은 날·금액의 스크래퍼 행과 겹치면 넣지 않고 검토 항목(open_item, 태그 card-dedup)으로 보류한다 — 보류된 행은 다시 보내도 자동 적재되지 않는다. 같은 계좌의 적재는 서로 직렬화된다. 응답 raw 에 held·open_item_ids 가 붙는다. 생략 = generic 파이프라인. 같은 바이트를 다른 filename/parser 로 다시 보내면 IDEMPOTENCY_CONFLICT. 이미 행이 있는 파일을 다시 보내면 duplicate 를 돌려주되 blob 은 저장한다(행만 있고 blob 이 없는 상태의 치유) |
ingest_file_local | write | 허용된 디렉터리의 파일 경로로 추출 (대용량 파일용) |
register_evidence_blob | write | 증빙 등록 — 파일은 content_base64 또는 source_path 중 하나(ingest_file_blob 과 같은 규칙) |
promote_source_file_to_evidence | write | 적재 단계에서 거부된 파일을 증빙으로 승격 |
register_resolution | write | 결의서 등록 |
link_journal_to_resolution | write | 분개 ↔ 결의서 연결 |
update_resolution_evidence | write | 결의서의 증빙만 사후 교체. 결의 본문·상태·연결 분개는 불변이고, 교체 이력은 결의에 누적된다 |
기재 — 전자(세금)계산서 발행 (볼타)
매출 전자(세금)계산서를 발행대행사 볼타(Bolta)의 API 로 정발행하고, 국세청 승인번호가 나오면 증빙과 분개(미수금 / 매출 / 부가세)를 장부에 자동으로 남긴다. 순서는 항상 미리보기 → 발행 → (필요하면) 재확인이다. 라이브 발행은 되돌릴 수 없고 정정은 수정발행으로만 한다(frame 미지원 — 볼타 대시보드).
- 키는 법인마다 live(실발행)와 test(샌드박스)를 따로 둔다 —
FRAME_BOLTA_API_KEY_<법인>/FRAME_BOLTA_TEST_API_KEY_<법인>, 값은 vault 에서 주입. 호출자가mode를 명시하면 그 슬롯의 키만 쓰고, 다른 법인·다른 mode 키로 넘어가지 않는다. - 이중 발행을 막는다. 같은 요청은 같은 관리번호로 보내고, 보내기 직전 발행 의도를 장부에 기록하며, 이미 보낸 요청과 같은 내용이면 볼타 키나 서버 비밀이 바뀌었어도 다시 보내지 않는다. 결과가 확정되지 않은 건은 재발행이 아니라 재확인(
sync_tax_invoice)만 안내한다. - test 키 결과는 실제 법인 장부에 기록하지 않는다. 작성일자 기간이 닫혀 있으면 증빙만 남기고 분개는 보류한다(기간을 연 뒤 재확인하면 분개).
| 도구 | 스코프 | 설명 |
|---|---|---|
preview_tax_invoice | read | 발행 미리보기 — 부작용 없음. mode 기본값 test. 입력 정규화·세액 규칙(과세는 생략 시 공급가액의 10%, 직접 주면 ±1원까지)·사업자번호 검증·공급자=해당 법인 확인·기간/계정 사전 검사·볼타 발행 마감일과 발급자 확인 → 요청 본문과 preview_token 을 돌려준다. 같은 내용이 이미 발행 요청됐으면 경고. 선택 인자 revenue_account_code(기본 4101)로 분개할 매출계정을 지정하면 그 계정이 계정표에 있는지도 미리 검사한다 — 정기 발행 스케줄의 회차를 사람이 직접 발행할 때는 스케줄과 같은 계정을 준다 |
issue_tax_invoice | write | 정발행. preview_token 과 mode 필수 — 미리보기와 다르면 보내지 않는다. 승인번호가 나오면 장부에 기록하고, 아직이면 pending_approval 을 돌려준다. 결과 미확정 오류(TAX_INVOICE_SUBMITTED_UNCONFIRMED · TAX_INVOICE_OUTCOME_UNKNOWN)에는 재발행하지 말고 재확인할 것. 선택 인자 revenue_account_code(기본 4101, 미리보기와 같은 값)는 발행 기록과 증빙에 남아, 나중에 재확인이나 홈택스 목록 적재로 분개해도 같은 계정으로 간다. 장부에 기록되는 발행이면 같은 거래처·같은 작성월을 정기 발행 스케줄 회차가 이미 발행했거나 발행 중일 때 거부한다(TAX_INVOICE_SCHEDULE_CONFLICT, 볼타에 보내지 않음) — 별개 공급임을 사람이 확인했을 때만 선택 인자 confirm_not_schedule_duplicate=true 로 발행하고, 그 확인은 발행 기록에 남는다 |
sync_tax_invoice | write | 승인 대기·오류 건 재확인 → 승인번호가 나오면 장부 기록. mode 필수, issuance_key 또는 client_reference_id. 재실행해도 한 건만 남는다 — 홈택스 목록을 나중에 적재해도 승인번호로 중복을 걸러 낸다 |
기재 — 정기 발행 스케줄 (볼타)
매월 같은 거래처에 같은 내용으로 발행하는 매출 전자(세금)계산서를 스케줄로 등록하면, 실행기가 회차마다 위 볼타 발행과 같은 경로(미리보기 → 발행 → 재확인)로 발행하고 장부에 기록한다. 순서는 초안 생성(미리보기) → 사람이 승인 → 회차 자동 발행이다. 라이브 스케줄은 승인 후 자동 발행되며 되돌릴 수 없다(정정은 수정발행 — frame 미지원).
- 작성일은 스케줄마다 규칙으로 정한다 — 예: 매월 25일(휴일이면 전 영업일), 매월 1일(휴일이면 다음 영업일). 달에 없는 날은 말일로, 휴일 이월은 작성월 안에서만 한다. 휴일은 토·일과 frame 에 내장된 관공서 공휴일 달력(설·추석·대체공휴일·선거일 포함, 매년 다음 해 월력요항이 나오면 갱신)이다. 달력이 아직 없는 해의 회차는 “토·일만 반영했다”고 경고하고, 그 해 달력이 등록되면 작성일이 공휴일에 걸린 회차를 알려 준다 — 작성일을 자동으로 옮기지는 않는다(승인 때 본 날짜가 모르게 바뀌지 않게). 새로 지정된 임시공휴일은 운영자가 설정으로 더한다.
- 계약 만료일(
valid_until)은 필수이고(한 스케줄은 최대 36개월), 만료일 이후 작성일의 회차는 만들지도 발행하지도 않는다. 자동 연장은 없다 — 만료가 다가오면 갱신 확인 알림이 뜨고, 사람이 갱신을 승인해야 다음 기간이 이어진다. 갱신하지 않으면 만료일 다음 실행에서 스케줄이 종료된다. - 첫 회차는 기본으로 작성일이 오늘 이후인 달부터다. 이미 지난 달은 홈택스에서 직접 발행했을 수 있으므로 시작 달(
start_period)을 명시해야만 포함한다. - 이중 발행을 막는다. 회차(스케줄 × 작성월)마다 한 번만 발행하고, 같은 거래처·같은 작성월의 매출 계산서가 이미 장부에 있으면(홈택스에서 직접 발행한 경우 등) 발행하지 않고 확인을 요청한다. 결과가 확정되지 않은 회차는 재발행하지 않고 재확인만 한다.
- 같은 거래처(같은 사업자번호)에 기간이 겹치는 스케줄은 만들 수 없다 — 정말 별개 용역이면 겹치는 스케줄을 별개로 선언해야 하고, 그 선언도 승인 대상이다. 다른 스케줄이 이미 발행한 같은 달 계산서도 중복 후보로 본다.
- 다른 스케줄(종료한 스케줄 포함)이 같은 거래처·같은 달을 이미 발행했거나 발행 중(국세청 승인 대기 등)이면, 그 달을 덮는 새 스케줄의 생성·갱신·승인을 거부하고 발행 직전에도 다시 확인해 발행하지 않는다. 종료한 스케줄이라도 이미 보낸 계산서는 결과가 확정되면 발행되기 때문이다 — 발행 중인 달은 별개 용역 선언으로도 풀리지 않는다.
- 스케줄 밖에서 직접 발행한 계산서와도 서로 막는다. 같은 거래처·같은 달을 직접 발행했는데 아직 국세청 승인 전이면 그 달을 덮는 스케줄을 만들거나 승인하지 않고 그 회차도 발행하지 않는다(재확인으로 확정한 뒤 판단). 반대로 스케줄 회차가 이미 발행됐거나 발행 중인 달을 직접 발행하려 하면 거부한다 — 별개 공급(추가 1회성 청구 등)임을 확인했을 때만 확인 표시와 함께 발행되고, 그 확인은 기록에 남는다.
- 이자처럼 세금계산서 대상이 아닌 거래는 스케줄로 만들 수 없다. 공급시기 전 발행(선발행)은 계속적 용역에만 옵션으로 허용하고, 선발행한 계산서의 작성일자는 발행일이다(미래 날짜로 발행하지 않는다).
- 일시정지했다가 재개하면, 정지 중 발행 시점이 지난 회차는 자동 발행하지 않고 승인을 기다린다(용역을 공급하지 않은 달은 발행하면 안 된다). 단 발행 시점이 재개 뒤인 회차는 정지가 그 달 일부에 걸쳤어도 보류하지 않는다 — 자동 승인 스케줄이면 원래 금액 그대로 발행되니, 일할 청구가 필요하면 발행 전에 그 회차 금액을 조정하거나 건너뛴다(재개 결과가 그 회차를 알려 준다).
- 스케줄을 종료한 뒤에 남은 미발행 회차(종료 순간 발행이 멈춰 있던 회차 등)도 실행기가 취소하고, 작성일이 지났으면 직접 발행 여부를 확인하라는 알림을 남긴다 — 공급한 달이 조용히 미발행으로 남지 않게.
- 알림(승인 대기·발급 기한 임박·실패·만료 임박·실행기 오류)은 미결(
list_open_items)로 남는다. 한 스케줄이 연속으로 실패하거나, 실패를 기록하는 것조차 실패하면 실행기 상태도 비정상으로 보고된다. - 무중단 배포(두 슬롯 동시 운영) 중에는 트래픽을 받는 슬롯의 실행기만 발행한다 — 이전 슬롯의 실행기는 대기한다(수정 배포 뒤 옛 코드가 발행을 계속하지 않게). 대기 중인 실행기는 발행할 일이 있는데 어느 슬롯도 제때(기본 90분 안에) 실행하지 않으면 비정상으로 보고하고 미결 알림을 남긴다 — 두 슬롯 모두 대기하는 설정 오류가 조용히 미발행으로 남지 않게.
| 도구 | 스코프 | 설명 |
|---|---|---|
create_invoice_schedule | write | 정기 발행 스케줄 초안 생성 — 아직 아무것도 발행하지 않는다. 템플릿(볼타 발행 내용에서 작성일자만 뺀 것)·작성일 규칙·계약 기간·mode(기본 test)·approval(기본 manual)·매출계정(기본 4101)·시작 달(선택)을 받아 첫 회차를 미리보기로 점검하고, 회차 목록·회차가 없는 계약 월 경고·승인 토큰을 돌려준다. 같은 공급받는자에 기간이 겹치는 스케줄이 있으면 거부(별개 용역이면 distinct_from_schedule_ids 로 선언). 다른 스케줄이 같은 달을 이미 발행했거나 발행 중이면 거부(거부 메시지에 그 회차 표시). 선발행을 켠 채 승인하면 ‘작성일 전에 발행되면 작성일자 = 발행일’ 까지 승인한 것이다 |
approve_invoice_schedule | write | 승인 토큰으로 초안을 활성화하고 기간 안의 회차를 만든다. 토큰은 스케줄 내용(선발행 여부 포함)에 묶여 있어 내용이 바뀌면 거부되고, 활성화 뒤에 내용이 바뀐 스케줄은 실행되지 않는다. 그 사이 다른 스케줄이 같은 달을 발행했거나 발행 중이면 거부. 라이브 스케줄은 승인 후 자동 발행되며 되돌릴 수 없다 |
list_invoice_schedules | read | 스케줄 목록 — 상태, 계약 만료까지 남은 날, 만료 임박 여부, 다음 회차, 발행이 밀린 회차 여부 |
get_invoice_schedule | read | 스케줄 1건과 회차 전체(작성일자·실제 보낸 작성일자·상태·금액·승인번호·장부 연결) |
update_invoice_occurrence | write | 회차 1건 조정 — skip(발행 안 함, 사유 필수) · retry(실패·확인 필요 회차 재시도) · adjust(그 회차만 금액·작성일 조정, 미리보기 토큰 필요) · approve(approval='manual' 스케줄의 회차, 그리고 재개로 보류된 회차의 승인 — 미리보기 토큰 필요). 볼타에 이미 접수된 회차는 건너뛸 수 없다. 다른 스케줄 회차와 같은 달이라 멈춘 회차는 그 회차가 발행 확정된 뒤에만 재시도할 수 있다 |
set_invoice_schedule_status | write | 일시정지 / 재개 / 종료. 재개하면 정지 중 발행 시점이 지난 회차는 승인 대기로 돌린다(공급했으면 승인, 아니면 건너뛰기). 발행 시점이 재개 뒤인 회차는 정지가 그 달 일부에 걸쳤어도 그대로 자동 발행되니 필요하면 발행 전에 조정한다. 종료(사유 필수, 되돌릴 수 없음)하면 아직 보내지 않은 회차를 모두 취소하고, 이미 발행한 이번 달 이후 회차는 수정발행 검토 미결로 남긴다 |
renew_invoice_schedule | write | 새 계약 기간(선택: 새 템플릿·계약서)으로 후속 스케줄 초안을 만든다 — 승인해야 이어진다. 거래처·mode·approval·매출계정·작성일 규칙·선발행 여부·별개 용역 선언은 이어받는다(그 밖을 바꾸려면 새로 만든다). 이전 스케줄이 아직 잡고 있는 작성월과 겹치거나 이미 후속 스케줄이 있으면 거부 |
run_invoice_schedules | write | 실행기를 한 번 돌린다(워커와 같은 로직). dry_run 기본 true — 보고만. false 면 만료 처리·결과 재확인·발행·알림 |
기재 — 고정자산·급여·정기분개
| 도구 | 스코프 | 설명 |
|---|---|---|
register_fixed_asset | write | 고정자산 등록 |
post_depreciation | write | 감가상각 분개 |
dispose_fixed_asset | write | 고정자산 처분 |
match_pending_to_raw_tx | write | 대기 급여 1건을 원천 거래와 매칭하고 분개 자동 생성. 급여명세서에 기타정산(지급·회수) 항목이 있으면 급여와 나눠 지급은 차변 미지급금, 회수는 대변 가지급금으로 잡고 적요를 분개 라인에 남긴다 — 그 이름의 계정이 계정표에 없으면 분개하지 않고 대기에 남긴다 |
match_pending_sweep | write | 미분류 대기 급여를 일괄 매칭. 같은 직원·기간·금액의 기존 분개가 이미 있으면 자동 매칭 대신 대기 상태를 유지하고 사람 검토를 기다린다 |
cancel_pending_payroll | write | 대기 급여 취소 (송금 취소·퇴사 정산 보류) |
sweep_stale_pending | write | 기한이 지난 대기 건을 stale 로 표시 |
compose_kb_mmf_q_journals | write | 분기 MMF 자동 분개 초안 |
compose_kbsc_q_journals | write | 분기 증권계좌 자동 분개 초안 |
compose_bank_q_journals | write | 분기 은행 이자 자동 분개 초안 |
기재 — 거래처·계좌
| 도구 | 스코프 | 설명 |
|---|---|---|
register_counterparty | write | 거래처 등록 (멱등 — 같은 키에 다른 인자면 오류) |
list_counterparties | read | 거래처 검색 — 이름·사업자번호·법인등록번호·별칭, 특수관계자 필터 |
update_counterparty | write | 거래처 필드 수정 (별칭은 replace/add/remove 중 하나만) |
merge_counterparty | close | 중복 거래처 병합 — 참조를 전부 옮기고 원본 삭제 |
suggest_counterparty_tags | read | 거래처가 비어 있는 분개 라인의 백필 후보를 제안 (읽기 전용 dry-run). 모호하면 제안하지 않는다 |
apply_counterparty_tags | write | 승인된 제안만 커밋. 이미 태깅된 라인은 건너뛰고 덮어쓰지 않는다 |
list_bank_accounts | read | 은행·카드 계좌 목록 |
update_bank_account | write | 계좌 메타(별칭·연결 거래처) 수정. 금융 데이터 자체는 불변 |
reassign_bank_account | write | 원천 거래의 계좌 배정 교정. 금액·일시는 불변 |
backfill_raw_transaction_amount | write | 파서 오류로 0 으로 적재된 금액 1회 복구. 이미 값이 있거나 연결된 분개와 어긋나면 거부 |
결산
| 도구 | 스코프 | 설명 |
|---|---|---|
closing_preview | close | 마감 미리보기 |
closing_commit | close | 마감 확정 |
post_quarterly_close_batch | close | 분기 자동 분개 초안을 한 트랜잭션으로 기재 |
opening_from_closing | close | 전기 마감 잔액을 당기 기초로 이월 |
open_period | close | 회계기간 개시 |
close_period | close | 기간 상태 전이 (open → soft_closed → hard_closed, 재개방 포함) |
폐지 계정 잔액은 결산 전에 재분류한다 — 마감·이월 분개에는 폐지 계정 전기 예외가 없다. 폐지(기재불가) 표시는 신규 전기를 막으려는 것이라, 잔액을 0 으로 만드는 마감 라인이나 새 기로 넘기는 이월 라인도 예외로 두지 않는다. 대신 막히는 이유를 미리보기에서 지목한다.
closing_preview 는 잔액이 남은 폐지 손익계정이 있으면 ACCOUNT_NOT_POSTABLE 로 거부하고, 메시지(폐지 계정 <코드> <계정명> 잔액 <금액> — 재분류 후 마감)와 context.retired_balances 에 해당 계정을 모두 열거한다. closing_commit 은 전기 전에 같은 미리보기를 다시 돌리므로 같은 이유로 거부하고 아무것도 전기하지 않는다. 재무상태표 계정은 마감 분개가 닿지 않으므로 마감을 막지 않는다. opening_from_closing 은 이월 분개가 계정을 찾을 날짜(새 기간 시작일)를 지배하는 정책 era 로 폐지 여부를 판정해, 잔액이 남은 폐지 재무상태표 계정이 있으면 미리보기(dry-run)는 정상 미리보기에 blocked: true 와 blocked_reason(같은 메시지·잔액표·조치)을 얹어 보고하고, force_post 는 같은 메시지로 거부하며 아무것도 전기하지 않는다 — 전기 단계의 일반 거부나 분개 불평형으로 원인이 가려지지 않는다. 조치는 하나다: 각 폐지 계정의 잔액을 사용 중인 통합 계정으로 옮기는 재분류 분개를 먼저 게시하고 다시 마감·이월한다. 재분류 분개의 폐지 계정 쪽 라인은 update_account(is_postable=true) 로 잠깐 되살려 게시한 뒤 곧바로 다시 폐지한다(감사 로그에 남는다).
전기는 법인 단위로 직렬화된다 (아래 셋, frame 다음 ship · 2026-09). 원장에 전표를 세우는 트랜잭션은 — MCP 쓰기 경로만이 아니라 고정자산·세금계산서·펀드·스냅샷·적재 분류처럼 원장에 직접 쓰는 경로까지 — 전부 그 법인의 전기 잠금을 첫 문장으로 잡고 커밋까지 쥔다. 그래서 원장을 다시 읽어 내린 판정이 판정과 기재 사이에 무효가 되지 않는다. 마감 확정이 그 잠금 안으로 들어온 것이 이 축의 본론이다: closing_commit 은 잠금을 잡은 뒤 같은 트랜잭션 안에서 기간을 확인하고, 마감 라인을 다시 계산하고, 순이익을 대조하고, 전기하고, 기간을 소프트클로즈한다. 재계산이 트랜잭션 밖이던 동안에는 그 사이 커밋된 손익 전표 하나가 낡은 라인으로 마감되고 그 잔액이 마감되지 않은 채 닫힌 기간에 남았다 — 되돌리려면 전기된 마감을 역분개하고 기간을 다시 열어야 한다. 미리보기 뒤에 손익 전표가 커밋됐으면 마감은 조용히 새 잔액까지 닫지 않고 순이익 불일치로 거부한다. 전기되는 것은 호출자가 되돌려준 preview_spec 이 아니라 서버가 잠금 안에서 다시 계산한 그 분개이고, 넘어온 preview_spec 의 라인이 그것과 다르면 MATERIALITY_VIOLATION 으로 거부한다 — 미리보기는 제안이고 잠금 안 재계산이 정본이라, 한 분개를 승인했는데 성공 코드와 함께 다른 분개가 전기되는 쪽이 거부보다 나쁘다. 비교는 금액을 정규화하고 라인을 정렬해서 보므로, 미리보기를 JSON 으로 왕복시키거나 순서를 바꿔 넘겨도 걸리지 않는다.
마감된 기간의 판정은 잠금 안에서 한 번, 원장 옆에서 또 한 번. 사전 게이트는 잠금을 잡기 전에 나온 통과권이라, 그것을 쥐고 잠금 앞에 선 평범한 전표는 앞선 마감이 그 사이 기간을 닫고 커밋해도 자기 통과권만 보고 기재한다. 그래서 잠금 안 기간 재확인을 posted 로 서는 모든 전표로 넓혔다 — 예전에는 마감 전표에만, 그것도 호출자가 채워 넣는 문자열을 기준으로 걸려 있었다. 그리고 같은 판정을 DB 트리거가 원장 옆에서 한 번 더 한다: journal 에 posted 행이 들어오거나 다른 상태이던 행이 posted 로 바뀔 때 entry_date 를 덮는 기간이 hard_closed 면 언제나, soft_closed 면 트랜잭션 로컬 설정 frame.allow_soft_closed 가 켜져 있지 않으면 거부다 — 닫힌 기간에 draft 로 넣어 두고 나중에 posted 로 뒤집는 우회도 같은 판정을 받는다. 판정의 근거인 기간 행은 그 쓰기가 커밋할 때까지 붙들려 있어, 판정과 기간 마감이 엇갈리지 않는다. 게이트를 호출 지점마다 복제하면 새 경로가 하나 생길 때 조용히 빠지므로(실제로 그렇게 빠져 있었다) 판정을 원장 옆에 못박았다 — 파이썬 층을 지나지 않는 직접 기재도, 다른 언어로 쓰인 자동분개도 같은 답을 받는다. 그 설정은 post_journal·reverse_journal 이 받는 명시적 인자 allow_soft_closed 가 켠다 — 이미 소프트클로즈된 기간에 전표를 세워야 하는 두 쓰기 도구다. 마감 자체는 그 인자를 쓰지 않는다: closing_commit 은 아직 열린 기간에 전기하고, 소프트클로즈는 같은 트랜잭션에서 그 뒤에 한다. 설정은 트랜잭션 로컬이라 커밋·롤백에서 저절로 풀린다. entry_date 를 덮는 기간이 아예 없으면 통과시킨다 — 첫 회계기간 이전의 과거 이관이 그 자리다.
닫힌 기간에서는 역분개 계보도 같은 문을 지난다. 수동으로 만든 반대 분개를 원본과 사후 연결하는 relink_reversal 과 링크만 채우는 백필은 원본의 상태와 역분개 링크를 고치므로, 원본의 기간이 닫혀 있으면 거부된다 — 두 함수 다 allow_soft_closed 를 켜지 않으므로 소프트클로즈도 예외가 아니고, 소프트클로즈든 하드클로즈든 close_period 재개방 전이(close 스코프)로 기간을 먼저 열어야 한다. 숫자는 한 푼도 움직이지 않지만 list_journals(status='reversed') 와 총계정원장의 역분개 숨김, 역분개 쌍 무결성 검사가 정확히 그 두 열을 읽는다 — 닫힌 해의 그림이 재개방 흔적 없이 바뀌는 것을 막는 것이 이 판정이다. 링크만 채우는 백필은 예전에 이 판정에서 면제돼 닫힌 해에도 돌았고, 그 면제는 날 SQL 이 하드클로즈된 해의 계보를 아무 잠금 없이 옮기는 길이기도 해서 함께 없어졌다.
역분개 계보는 원장이 직접 지킨다. reversed 로 표시된 전표는 자기를 되감은 상대 전표를 반드시 가리켜야 하고, 링크 없는 reversed·자기 자신을 가리키는 링크·존재하지 않는 전표를 가리키는 링크는 어떤 쓰기 경로로도 원장에 들어가지 못한다. 링크된 상대 전표만 따로 지울 수도 없다. 예전에는 이 규칙을 쓰기 도구만 지켜서, 도구를 우회한 쓰기가 깨진 계보를 남겼고 정합성 검사가 사후에 찾아야 했다. 규칙 도입 이전에 남은 손상 행은 고치지 않고 그대로 둔다 — 새로 들어오거나 바뀌는 행에만 적용되고, 정합성 검사가 그 행들을 계속 보고한다.
이월의 폐지 판정은 도착 기간의 정책 era 에서 읽는다. 한 계정코드가 여러 회계정책 era 에 걸치면 era 마다 계정 행이 따로 있고 계정 수정은 한 era 만 바꾼다. 잔액을 era 행 단위로 묶던 동안에는 두 방향으로 어긋났다 — 옛 era 는 살아있고 도착 era 가 폐지면 게이트가 아무것도 못 본 채 이월이 전기에서 깨졌고, 반대면 이미 지나간 era 의 폐지 표시가 멀쩡한 이월을 막아 지나간 era 의 계정을 되살려야 풀렸다. 이제 잔액은 계정코드로 합치고 폐지 여부·계정명·유형은 새 기간 시작일을 지배하는 era — 이월 분개가 실제로 계정을 찾을 그 행 — 에서 읽는다(지배 era 판정은 전기와 같은 규칙이다). 도착 era 에 그 코드가 아예 없으면 전기 불가로 보고, 도중에 깨지느니 무엇을 재분류해야 하는지 알려 주며 막는다.
관리
| 도구 | 스코프 | 설명 |
|---|---|---|
register_entity | admin | 법인·펀드 등록 + 표준 계정과목 자동 시딩. 사업자등록번호·법인등록번호(체크디짓 검증). 다른 등록·배포 마이그레이션이 진행 중이면 최대 10초 기다리며, 그 안에 등록을 시작하지 못하면 ENTITY_PROVISIONING_BUSY를 반환한다. 이 오류에서는 아무것도 기록하지 않으며, 진행 중인 작업이 끝난 뒤 같은 인자로 재시도한다. |
update_period | admin | 열려 있는 기간의 시작·종료·라벨·비고 수정 |
list_accounts | read | 계정과목 조회 — 유형·상위계정·기재가능 여부 필터 |
create_account | admin | 계정과목 신설 — 유형 검증 + 상위계정 존재 검증 |
update_account | admin | 계정과목 이름·상위계정·활성여부·순서 수정 (코드·유형은 불변) |
delete_account | admin | 계정과목 삭제. 분개에서 쓰이거나 자식 계정이 있으면 거부 |
list_accounting_policies | read | 적용 중인 회계기준 정책 조회 |
create_accounting_policy | admin | 새 회계기준 정책 생성 (기준 개정 등) |
update_accounting_policy | admin | 정책의 기준·적용시작·적용종료·비고 수정 |
list_scrape_schedules | read | 포털 수집 주기 목록 |
get_scrape_schedule | read | 단건 수집 주기 (지연 여부 포함) |
set_scrape_schedule | write | 수집 주기 설정 (cron 또는 daily/weekly/monthly 프리셋, KST) |
공개 API — 스키마 목록
Frame 이 생산하는 사실(fact)의 타입 규격을 그대로 노출한다. 다른 서비스나 에이전트가 이 규격을 가져다 캐시·검증에 쓴다.
curl -H "Authorization: Bearer $TOKEN" \
https://frame.axelabs.ai/frame/schemas응답 봉투는 안정 형식이다.
{
"version": "1.0",
"service": "frame",
"schemas": {
"[email protected]": {
"description": "단일 계정의 시점 잔액 (차변/대변 합계 + 순 잔액).",
"produced_by": ["query_balance"],
"key_fields": ["entity_id", "account_code", "as_of_date", "debit", "credit", "balance", "policy_id"]
}
}
}현재 13종을 노출한다 — 잔액 · 시산표 · 분개 · 분개목록 · 원천거래 · 미결 · 결의서 · 고정자산 장부가액 · 손익계산서 · 재무상태표 · 현금흐름표 · 자본변동표 · 총계정원장.
- 인증 필요 — MCP 클라이언트와 동일한 토큰을 그대로 쓴다.
- 버전 — 스키마 ID 의
@1.0접미사가 개별 스키마의 호환성을, 봉투의version이 응답 모양 자체의 변경을 나타낸다.
시작하기
- 법인 등록 — 등록 시점에 KSME 표준 계정과목이 자동으로 복사된다. 별도 시딩 작업이 없다.
- 원천 데이터 적재 — 은행·카드 거래내역 파일을 올린다. 포맷은 자동 인식된다. 처음 보는 포맷이면 몇 가지를 되묻고, 그 답을 캐시해 다음부터는 묻지 않는다.
- 기초 잔액 등록 — 외부 회계법인 검수본 기준 시산표를 개시 분개로 기재한다.
- 분개 — 자동 매칭이 붙는 건은 제안을 확인하고 커밋하고, 나머지는 직접 기재한다. 진척은 시산표로 매일 확인한다.
- 마감 — 기간을 가마감 → 확정으로 전이시키고, 다음 기 기초로 이월한다.
관련 문서
- Blueprint — Frame 을 도구로 호출하는 워크스페이스
- UI (디자인 시스템)