--========================================================================== -- PKG_LEDGER.sql - 원장 복식부기 기표 패키지 (As-Is 원본, Oracle PL/SQL) ---------------------------------------------------------------------------- -- 대상 : BC 매입시스템 / 회계 원장 도메인 공용 패키지 PKG_LEDGER -- DBMS : Oracle 19c -- ※ 이 파일은 As-Is 역사 원본이다. pkg_settle 및 ACQUIRE/SETTLE/ST_MDR -- 계열이 호출하는 복식부기(차/대변) 기표 엔진이다. 현행 오픈스택 -- 이식본에서는 ledger 단식 INSERT (app/src/st/svc/*, LG_* 서비스) 로 -- 단순화되었다. 컴파일 대상 아님(빌드 제외). ---------------------------------------------------------------------------- -- 변경이력 -- 1998.12.05 KIM K.H 최초작성 (전표 헤더/명세 복식부기 기표) -- 2007.06.18 PARK J.S 전표유형->계정 매핑 DECODE 테이블 도입 -- 2014.02.27 LEE S.W 전표 역분개 reverse_voucher, 차대 균형검증 강화 -- 2019.09.01 CHOI H.W Oracle 11g -> 19c, 예외코드 -20600 대역 정리 --========================================================================== CREATE OR REPLACE PACKAGE pkg_ledger AS -- 차/대변 구분 C_DEBIT CONSTANT VARCHAR2(1) := 'D'; -- 차변 C_CREDIT CONSTANT VARCHAR2(1) := 'C'; -- 대변 -- 사용자정의 예외 (-20600 대역) e_unbalanced EXCEPTION; e_bad_type EXCEPTION; e_no_voucher EXCEPTION; PRAGMA EXCEPTION_INIT(e_unbalanced, -20601); PRAGMA EXCEPTION_INIT(e_bad_type, -20602); PRAGMA EXCEPTION_INIT(e_no_voucher, -20603); -- 전표유형 -> (차변계정, 대변계정) 매핑 PROCEDURE map_accounts(p_type IN VARCHAR2, o_dr_acct OUT VARCHAR2, o_cr_acct OUT VARCHAR2); -- 단일 전표 복식부기 기표 (헤더 1 + 명세 2: 차변/대변) PROCEDURE post_voucher(p_pid IN NUMBER, p_type IN VARCHAR2, p_amount IN NUMBER, p_bizdate IN DATE); -- 전표 역분개 (차대 반대로 재기표) PROCEDURE reverse_voucher(p_voucher_id IN NUMBER, p_reason IN VARCHAR2); -- 전표 차대 균형 검증 (미균형시 예외) FUNCTION is_balanced(p_voucher_id IN NUMBER) RETURN BOOLEAN; END pkg_ledger; / CREATE OR REPLACE PACKAGE BODY pkg_ledger AS ---------------------------------------------------------------------- -- map_accounts : 전표유형별 차변/대변 계정 결정 (DECODE 매핑) ---------------------------------------------------------------------- PROCEDURE map_accounts(p_type IN VARCHAR2, o_dr_acct OUT VARCHAR2, o_cr_acct OUT VARCHAR2) IS BEGIN -- 매입채권(1101)/미지급금(2101)/수수료수익(4101)/현금(1001) o_dr_acct := DECODE(p_type, 'MDR', '1101', -- 매입채권 / 수수료수익 'SETTLE', '1101', -- 매입채권 / 정산미지급 'PAYOUT', '2101', -- 정산미지급 / 현금 'SETTLE_REV', '2101', -- (역) 정산미지급 / 매입채권 NULL); o_cr_acct := DECODE(p_type, 'MDR', '4101', 'SETTLE', '2101', 'PAYOUT', '1001', 'SETTLE_REV', '1101', NULL); IF o_dr_acct IS NULL OR o_cr_acct IS NULL THEN RAISE_APPLICATION_ERROR(-20602, '미정의 전표유형 type='||p_type); END IF; END map_accounts; ---------------------------------------------------------------------- -- post_voucher : 복식부기 전표 1건 기표 ---------------------------------------------------------------------- PROCEDURE post_voucher(p_pid IN NUMBER, p_type IN VARCHAR2, p_amount IN NUMBER, p_bizdate IN DATE) IS v_vid ledger_voucher.voucher_id%TYPE; v_dr VARCHAR2(8); v_cr VARCHAR2(8); v_abs NUMBER := ABS(p_amount); v_side VARCHAR2(1); BEGIN map_accounts(p_type, v_dr, v_cr); -- 음수 전표(역기표)는 차/대변을 교차 IF p_amount < 0 THEN v_side := v_dr; v_dr := v_cr; v_cr := v_side; END IF; v_vid := ledger_voucher_seq.NEXTVAL; INSERT INTO ledger_voucher (voucher_id, purchase_id, entry_type, amount, biz_date, status, created_at) VALUES (v_vid, p_pid, p_type, p_amount, p_bizdate, 'N', SYSDATE); -- 명세 2행: 차변 / 대변 INSERT INTO ledger_entry (entry_id, voucher_id, account_cd, dc_flag, amount, biz_date) VALUES (ledger_entry_seq.NEXTVAL, v_vid, v_dr, C_DEBIT, v_abs, p_bizdate); INSERT INTO ledger_entry (entry_id, voucher_id, account_cd, dc_flag, amount, biz_date) VALUES (ledger_entry_seq.NEXTVAL, v_vid, v_cr, C_CREDIT, v_abs, p_bizdate); -- 균형 검증 (방어적) IF NOT is_balanced(v_vid) THEN RAISE_APPLICATION_ERROR(-20601, '전표 차대 불균형 vid='||v_vid); END IF; END post_voucher; ---------------------------------------------------------------------- -- reverse_voucher : 기존 전표를 역분개 ---------------------------------------------------------------------- PROCEDURE reverse_voucher(p_voucher_id IN NUMBER, p_reason IN VARCHAR2) IS r_vou ledger_voucher%ROWTYPE; CURSOR c_ent(cp_vid NUMBER) IS SELECT account_cd, dc_flag, amount FROM ledger_entry WHERE voucher_id = cp_vid; v_newvid ledger_voucher.voucher_id%TYPE; v_flip VARCHAR2(1); BEGIN SELECT * INTO r_vou FROM ledger_voucher WHERE voucher_id = p_voucher_id FOR UPDATE OF status; IF r_vou.status = 'R' THEN RAISE_APPLICATION_ERROR(-20603, '이미 역분개 vid='||p_voucher_id); END IF; v_newvid := ledger_voucher_seq.NEXTVAL; INSERT INTO ledger_voucher (voucher_id, purchase_id, entry_type, amount, biz_date, status, ref_voucher, created_at) VALUES (v_newvid, r_vou.purchase_id, r_vou.entry_type||'_REV', -r_vou.amount, TRUNC(SYSDATE), 'N', p_voucher_id, SYSDATE); -- 원전표 명세를 차대 반대로 복제 FOR e IN c_ent(p_voucher_id) LOOP v_flip := DECODE(e.dc_flag, C_DEBIT, C_CREDIT, C_DEBIT); INSERT INTO ledger_entry (entry_id, voucher_id, account_cd, dc_flag, amount, biz_date) VALUES (ledger_entry_seq.NEXTVAL, v_newvid, e.account_cd, v_flip, e.amount, TRUNC(SYSDATE)); END LOOP; UPDATE ledger_voucher SET status = 'R', reverse_rsn = p_reason, reversed_at = SYSDATE WHERE voucher_id = p_voucher_id; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(-20603, '전표 없음 vid='||p_voucher_id); END reverse_voucher; ---------------------------------------------------------------------- -- is_balanced : 전표 차변합 = 대변합 여부 ---------------------------------------------------------------------- FUNCTION is_balanced(p_voucher_id IN NUMBER) RETURN BOOLEAN IS v_dr NUMBER := 0; v_cr NUMBER := 0; BEGIN SELECT NVL(SUM(DECODE(dc_flag, C_DEBIT, amount, 0)), 0), NVL(SUM(DECODE(dc_flag, C_CREDIT, amount, 0)), 0) INTO v_dr, v_cr FROM ledger_entry WHERE voucher_id = p_voucher_id; RETURN (v_dr = v_cr AND v_dr > 0); END is_balanced; END pkg_ledger; /