/*==========================================================================*/
/*  RECONCILE.pc  -  대사(對査) 온라인 서비스 (As-Is 원본, Oracle Pro*C)      */
/*--------------------------------------------------------------------------*/
/*  대상 : BC 매입시스템 / Tuxedo ATMI 서비스 RECONCILE                       */
/*  DBMS : Oracle 19c   전처리 : Pro*C/C++ (proc)                            */
/*  기능 : 당일 매입 vs 원장 대사 — 커서순회 매칭분류 + 예외적재 + 정산연계.   */
/*  ※ 이 파일은 **As-Is 역사 원본**이다. 현행 오픈스택 이식본은               */
/*     app/src/rc/svc/RECONCILE.pgc (ECPG/PostgreSQL). 컴파일 제외.          */
/*--------------------------------------------------------------------------*/
/*  변경이력                                                                  */
/*   2001.07.16  SEO K.T   최초작성 (일 매입-원장 대사)                        */
/*   2005.12.02  HAN Y.J   허용오차(tolerance) 매칭 도입, DECODE 등급          */
/*   2009.04.27  LEE S.W   조직/코드 트리 CONNECT BY 집계 추가                 */
/*   2015.10.13  JANG M.K  예외원장(rc_exception) 분리, 감사로그 표준화        */
/*   2019.09.01  CHOI H.W  Oracle 11g -> 19c, 커서/시퀀스 정비                */
/*==========================================================================*/
#include <stdio.h>
#include <string.h>
#include <atmi.h>
#include <fml32.h>
#include <userlog.h>

EXEC SQL INCLUDE sqlca;
EXEC SQL INCLUDE oraca;
EXEC ORACLE OPTION (ORACA=YES);

#include "rcfld.h"

EXEC SQL BEGIN DECLARE SECTION;
    VARCHAR   h_bizdate[9];       /* YYYYMMDD */
    VARCHAR   h_merch[64];
    VARCHAR   h_status[16];
    VARCHAR   h_pstat[2];
    VARCHAR   h_orgcode[16];      /* 조직/코드 트리 루트 */
    VARCHAR   h_orgname[64];
    long      h_pid;
    long      h_amt;
    long      h_fee;
    long      h_net;
    long      h_act;
    long      h_exp;
    long      h_gross;
    long      h_cnt;
    long      h_mcnt;
    long      h_ecnt;
    long      h_mdr_bps;
    long      h_limit;
    long      h_led;
    long      h_lvl;              /* 코드트리 계층레벨 */
    long      h_orgsum;
EXEC SQL END DECLARE SECTION;

/*--------------------------------------------------------------------------*/
/*  RECONCILE  -  Tuxedo 서비스 엔트리                                        */
/*--------------------------------------------------------------------------*/
void RECONCILE(TPSVCINFO *rqst)
{
    FBFR32 *fb = (FBFR32 *)rqst->data;
    char    bizdate[9], merch[64];
    long    tol = 0, rc_code = 0, adiff = 0;
    long    scan_rows = 0, matched = 0, unmatched = 0, unmatched_amt = 0;

    userlog("RECONCILE 진입: 당일 매입-원장 대사");
    EXEC SQL WHENEVER SQLERROR GOTO sql_error;

    /* ---- 1) 입력 검증 ------------------------------------------------ */
    Fget(fb, RC_BIZDATE, 0, bizdate, (FLDLEN32 *)NULL);
    if (strlen(bizdate) < 8) {
        EXEC SQL SELECT TO_CHAR(SYSDATE,'YYYYMMDD') INTO :h_bizdate FROM DUAL;
        strcpy(bizdate, (char *)h_bizdate.arr);
    }
    strcpy((char *)h_bizdate.arr, bizdate); h_bizdate.len = strlen(bizdate);
    h_pid = Fvall(fb, RC_PURCHASE_ID, 0);
    h_net = Fvall(fb, RC_NET, 0);

    /* ---- 2) 마스터/정책 조회 (미커밋 매입은 전문값, 외부조인 (+)) ---- */
    EXEC SQL
        SELECT NVL(p.amount,0), NVL(p.fee,0), NVL(p.net,:h_net),
               NVL(p.status,'A'), NVL(m.merchant_id, ' '),
               NVL(m.mdr_bps, 25), NVL(m.daily_limit, 100000000)
          INTO :h_amt, :h_fee, :h_net, :h_pstat, :h_merch, :h_mdr_bps, :h_limit
          FROM purchase p, merchant m
         WHERE p.purchase_id = :h_pid
           AND m.merchant_id (+) = p.merchant_id;
    if (sqlca.sqlcode == 1403) {
        h_amt = Fvall(fb, RC_AMOUNT, 0);
        h_fee = Fvall(fb, RC_FEE, 0);
        h_net = Fvall(fb, RC_NET, 0);
        Fget(fb, RC_MERCHANT, 0, merch, (FLDLEN32 *)NULL);
        strcpy((char *)h_merch.arr, merch); h_merch.len = strlen(merch);
        h_mdr_bps = 25; h_limit = 100000000;
        userlog("RECONCILE: 체인 신규매입(미커밋) pid=%ld — 전문값 대사", h_pid);
    }

    /* ---- 3) 당일 매입/대사로그/예외 집계 (NVL 널가드) --------------- */
    EXEC SQL
        SELECT COUNT(*), NVL(SUM(amount),0)
          INTO :h_cnt, :h_gross
          FROM purchase
         WHERE biz_date = TO_DATE(:h_bizdate,'YYYYMMDD');
    EXEC SQL
        SELECT COUNT(*) INTO :h_mcnt
          FROM rc_match_log
         WHERE biz_date = TO_DATE(:h_bizdate,'YYYYMMDD')
           AND status = 'MATCHED';
    EXEC SQL
        SELECT COUNT(*) INTO :h_ecnt
          FROM rc_exception
         WHERE biz_date = TO_DATE(:h_bizdate,'YYYYMMDD')
           AND resolved = 'N';

    /* ---- 4) 거래집합 커서순회 + 허용오차 매칭분류 ------------------- */
    EXEC SQL DECLARE cur_reconcile CURSOR FOR
        SELECT p.purchase_id, NVL(p.net,0)
          FROM purchase p
         WHERE p.biz_date = TO_DATE(:h_bizdate,'YYYYMMDD')
         ORDER BY p.purchase_id;
    EXEC SQL OPEN cur_reconcile;
    for (;;) {
        EXEC SQL WHENEVER NOTFOUND DO break;
        EXEC SQL FETCH cur_reconcile INTO :h_pid, :h_exp;
        EXEC SQL
            SELECT NVL(SUM(amount),0) INTO :h_act
              FROM ledger
             WHERE purchase_id = :h_pid
               AND entry_type IN ('RECON','SETTLE');
        adiff = h_exp - h_act;
        if (adiff < 0) adiff = -adiff;
        scan_rows++;
        if (adiff <= (tol > 0 ? tol : 0)) matched++;
        else { unmatched++; unmatched_amt += adiff; }
    }
    EXEC SQL WHENEVER NOTFOUND CONTINUE;
    EXEC SQL CLOSE cur_reconcile;
    userlog("RECONCILE 대사스캔: scan=%ld match=%ld unmatch=%ld",
            scan_rows, matched, unmatched);

    /* ---- 5) 조직/코드 트리 집계 (계층질의 CONNECT BY PRIOR) --------- */
    /*  가맹점이 속한 정산조직 코드트리를 루트→하위로 전개하여 소속 조직의  */
    /*  당일 정산 순액 합계를 취득한다(대사 대상 조직 범위 확정용).         */
    EXEC SQL
        SELECT NVL(org_code,'ROOT'), NVL(org_name,'-')
          INTO :h_orgcode, :h_orgname
          FROM rc_org_map
         WHERE merchant_id = :h_merch
           AND ROWNUM = 1;
    if (sqlca.sqlcode == 1403) { strcpy((char *)h_orgcode.arr, "ROOT");
        h_orgcode.len = 4; }
    EXEC SQL
        SELECT NVL(SUM(s.net),0), NVL(MAX(LEVEL),1)
          INTO :h_orgsum, :h_lvl
          FROM settlement s, rc_org o
         WHERE s.biz_date = TO_DATE(:h_bizdate,'YYYYMMDD')
           AND s.org_code = o.org_code
           AND o.org_code IN (
                 SELECT org_code
                   FROM rc_org
                  START WITH org_code = :h_orgcode
                CONNECT BY PRIOR org_code = parent_code);
    userlog("RECONCILE 코드트리: org=%.8s depth=%ld 정산합=%ld",
            h_orgcode.arr, h_lvl, h_orgsum);

    /* ---- 6) 업무규칙 분기 (매칭율 기반 상태판정, DECODE) ------------- */
    if (h_gross <= 0 && scan_rows == 0) {
        rc_code = 0; strcpy((char *)h_status.arr, "NODATA");
    } else if (unmatched == 0) {
        rc_code = 0; strcpy((char *)h_status.arr, "MATCHED");
    } else if (unmatched * 100 <= (scan_rows > 0 ? scan_rows : 1) * 5) {
        rc_code = 1; strcpy((char *)h_status.arr, "TOLERATED");
    } else {
        rc_code = 2; strcpy((char *)h_status.arr, "EXCEPTION");
    }
    h_status.len = strlen((char *)h_status.arr);
    /* 감사 심각도 라벨 (DECODE 코드분기) */
    EXEC SQL
        SELECT DECODE(:rc_code, 0,'I', 1,'W', 2,'E', 'X')
          INTO :h_pstat
          FROM DUAL;

    /* ---- 7) 본처리: RECON 원장 적재 (.NEXTVAL 채번) ---------------- */
    EXEC SQL SELECT ledger_seq.NEXTVAL INTO :h_led FROM DUAL;
    EXEC SQL
        INSERT INTO ledger
               (ledger_id, purchase_id, entry_type, amount, biz_date, posted_at)
        VALUES (:h_led, :h_pid, 'RECON', :h_net,
                TO_DATE(:h_bizdate,'YYYYMMDD'), SYSDATE);

    /* ---- 8) 불일치 예외 적재 + 감사로그 (PL/SQL 대사 패키지) -------- */
    if (rc_code >= 2) {
        EXEC SQL
            INSERT INTO rc_exception
                   (exc_id, biz_date, merchant_id, unmatch_cnt, unmatch_amt,
                    resolved, created_at)
            VALUES (rc_exception_seq.NEXTVAL, TO_DATE(:h_bizdate,'YYYYMMDD'),
                    :h_merch, :unmatched, :unmatched_amt, 'N', SYSDATE);
    }
    EXEC SQL EXECUTE
        BEGIN
            pkg_settle.recon_audit(:h_bizdate, :h_merch, :scan_rows,
                                   :matched, :unmatched, :rc_code);
        END;
    END-EXEC;

    EXEC SQL COMMIT WORK;

    /* ---- 9) 응답부 ------------------------------------------------- */
    Fchg(fb, RC_STATUS, 0, (char *)h_status.arr, 0L);
    Fchg(fb, RC_MATCHED, 0, (char *)&matched, 0L);
    Fchg(fb, RC_UNMATCHED, 0, (char *)&unmatched, 0L);
    Fchg(fb, RC_RC, 0, (char *)&rc_code, 0L);
    userlog("RECONCILE 완료: biz=%.8s status=%.10s rc=%ld orgsum=%ld",
            h_bizdate.arr, h_status.arr, rc_code, h_orgsum);
    tpreturn(TPSUCCESS, 0, (char *)fb, 0L, 0L);
    return;

sql_error:
    userlog("RECONCILE SQL 오류: %.70s (sqlcode=%ld)",
            sqlca.sqlerrm.sqlerrmc, sqlca.sqlcode);
    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL ROLLBACK WORK;
    Fchg(fb, RC_RC, 0, (char *)"E900", 0L);
    tpreturn(TPFAIL, 0, (char *)fb, 0L, 0L);
    return;
}
