/*==========================================================================*/ /* PAY_FILEGEN.pc - 지급파일 생성 배치 (As-Is 원본, Oracle Pro*C) */ /*--------------------------------------------------------------------------*/ /* 대상 : BC 매입시스템 / 지급(펌뱅킹) 파일 생성 배치 PAYFGEN */ /* DBMS : Oracle 19c 전처리 : Pro*C/C++ (proc) */ /* 실행 : payfgen (인자 생략시 SYSDATE) */ /* 기능 : 당일 승인(A) 지급건을 커서로 스윕하여 py_file 헤더 채번 + */ /* 평문 지급레코드를 UTL_FILE 로 디스크 파일에 기록(펌뱅킹 규격). */ /* ※ 이 파일은 **As-Is 역사 원본**이다. 현행 오픈스택 이식본은 */ /* app/src/py/svc/PAY_FILEGEN.pgc (ECPG/PostgreSQL, Tuxedo 서비스화; */ /* 디스크 I/O 는 별도 xfer 로 분리됨). 컴파일 대상 아님(빌드 제외). */ /*--------------------------------------------------------------------------*/ /* 변경이력 */ /* 2000.07.03 KIM K.H 최초작성 (전은행 공통 지급파일 EBCDIC->ASCII) */ /* 2006.12.20 PARK J.S 정책 파라미터(cm_param) 기반 최대건수 분할 */ /* 2013.04.09 LEE S.W UTL_FILE 로 헤더/명세/트레일러 3레코드 규격화 */ /* 2019.09.01 CHOI H.W Oracle 11g -> 19c, CHAR->VARCHAR2 정비 */ /*==========================================================================*/ #include #include #include EXEC SQL INCLUDE sqlca; EXEC SQL INCLUDE oraca; EXEC ORACLE OPTION (ORACA=YES); EXEC SQL BEGIN DECLARE SECTION; VARCHAR h_user[32]; VARCHAR h_pass[32]; VARCHAR h_dbstr[32]; VARCHAR h_bizdate[9]; /* YYYYMMDD */ VARCHAR h_fname[64]; VARCHAR h_dir[32]; VARCHAR h_bankcd[4]; VARCHAR h_acctno[20]; VARCHAR h_merch[16]; VARCHAR h_line[256]; VARCHAR h_pval[16]; long h_fileid; long h_payid; long h_amount; long h_fee; long h_net; long h_reccnt; long h_totamt; long h_thr; long h_seq; EXEC SQL END DECLARE SECTION; /* 당일 승인(A) 지급 대상 커서 — 계좌 마스터 외부조인으로 은행/계좌 획득 */ EXEC SQL DECLARE cur_pay CURSOR FOR SELECT p.payout_id, p.merchant_id, NVL(a.bank_code, '000'), NVL(a.account_no, 'X'), NVL(p.amount, 0), NVL(p.fee, 0), NVL(p.net, 0) FROM py_payout p, py_account a WHERE a.merchant_id (+) = p.merchant_id AND p.biz_date = TO_DATE(:h_bizdate,'YYYYMMDD') AND p.status = 'A' ORDER BY p.merchant_id, p.payout_id; static void sql_error(const char *stage); int main(int argc, char **argv) { int done = 0; strcpy((char *)h_user.arr, "pybatch"); h_user.len = strlen((char *)h_user.arr); strcpy((char *)h_pass.arr, "pybatch"); h_pass.len = strlen((char *)h_pass.arr); strcpy((char *)h_dbstr.arr, "ACQPRD"); h_dbstr.len = strlen((char *)h_dbstr.arr); EXEC SQL WHENEVER SQLERROR GOTO connect_err; EXEC SQL CONNECT :h_user IDENTIFIED BY :h_pass USING :h_dbstr; EXEC SQL WHENEVER SQLERROR GOTO sqlfail; /* ---- 1) 영업일 결정 -------------------------------------------- */ if (argc >= 2) { strncpy((char *)h_bizdate.arr, argv[1], 8); h_bizdate.arr[8] = '\0'; h_bizdate.len = strlen((char *)h_bizdate.arr); } else { EXEC SQL SELECT TO_CHAR(SYSDATE,'YYYYMMDD') INTO :h_bizdate FROM DUAL; } printf("[PAY_FILEGEN] 지급파일 생성 개시 bizdate=%.8s\n", h_bizdate.arr); /* ---- 2) 편성 대상 사전집계 (건수/총액) ------------------------- */ EXEC SQL SELECT COUNT(*), NVL(SUM(net), 0) INTO :h_reccnt, :h_totamt FROM py_payout WHERE biz_date = TO_DATE(:h_bizdate,'YYYYMMDD') AND status = 'A'; if (h_reccnt == 0) { printf("[PAY_FILEGEN] 편성 대상 승인건 없음 - 파일생성 생략\n"); EXEC SQL ROLLBACK WORK RELEASE; return 0; } if (h_totamt <= 0) { fprintf(stderr, "[PAY_FILEGEN] 편성 총액 비정상 amt=%ld\n", h_totamt); EXEC SQL ROLLBACK WORK RELEASE; return 4; } /* ---- 3) 정책 파라미터 조회 (최대 레코드수) — NVL 널가드 --------- */ EXEC SQL SELECT NVL(MAX(param_val), '100000') INTO :h_pval FROM cm_param WHERE param_key = 'pay_filegen_max_rec'; h_thr = atol((char *)h_pval.arr); if (h_thr <= 0) h_thr = 100000; if (h_reccnt > h_thr) { fprintf(stderr, "[PAY_FILEGEN] 최대 레코드수 초과 %ld > %ld - 분할 필요\n", h_reccnt, h_thr); EXEC SQL ROLLBACK WORK RELEASE; return 5; } /* ---- 4) 파일 채번 및 헤더행 적재 (시퀀스 .NEXTVAL) ------------- */ EXEC SQL SELECT py_file_seq.NEXTVAL INTO :h_fileid FROM DUAL; sprintf((char *)h_fname.arr, "PAYOUT_%.8s_%ld.dat", h_bizdate.arr, h_fileid); h_fname.len = strlen((char *)h_fname.arr); strcpy((char *)h_dir.arr, "PAYOUT_OUT"); /* DIRECTORY 객체명 */ h_dir.len = strlen((char *)h_dir.arr); EXEC SQL INSERT INTO py_file (file_id, biz_date, file_name, record_count, total_amount, status, created_at) VALUES (:h_fileid, TO_DATE(:h_bizdate,'YYYYMMDD'), :h_fname, :h_reccnt, :h_totamt, 'G', SYSDATE); /* UTL_FILE 로 파일 오픈 후 헤더레코드('H') 기록 (PL/SQL 익명블록) */ EXEC SQL EXECUTE BEGIN pkg_payfile.open_file(:h_dir, :h_fname); pkg_payfile.put_header(:h_bizdate, :h_reccnt, :h_totamt); END; END-EXEC; /* ---- 5) 본처리: 지급건 커서 루프 + 명세레코드('D') 기록 -------- */ h_seq = 0; EXEC SQL OPEN cur_pay; for (;;) { EXEC SQL WHENEVER NOTFOUND DO done = 1; EXEC SQL FETCH cur_pay INTO :h_payid, :h_merch, :h_bankcd, :h_acctno, :h_amount, :h_fee, :h_net; if (done) break; h_seq += 1; /* 무계좌(외부조인 미스) 건은 파일에서 제외하고 보류 전이 */ if (strncmp((char *)h_bankcd.arr, "000", 3) == 0) { printf("[PAY_FILEGEN] 계좌미등록 payout=%ld merch=%.*s - 보류전이\n", h_payid, h_merch.len, h_merch.arr); EXEC SQL UPDATE py_payout SET status = 'H', updated_at = SYSDATE WHERE payout_id = :h_payid; h_seq -= 1; continue; } /* 펌뱅킹 명세레코드 조립 (고정폭: 구분1 은행3 계좌20 금액13) */ sprintf((char *)h_line.arr, "D%3.3s%-20.20s%013ld", h_bankcd.arr, h_acctno.arr, h_net); h_line.len = strlen((char *)h_line.arr); EXEC SQL EXECUTE BEGIN pkg_payfile.put_line(:h_line); END; END-EXEC; /* 지급건을 이 파일로 편성 ('A'->'F') */ EXEC SQL UPDATE py_payout SET file_id = :h_fileid, status = DECODE(status, 'A', 'F', status), updated_at = SYSDATE WHERE payout_id = :h_payid; if (h_seq % 1000 == 0) { printf("[PAY_FILEGEN] 진행 %ld건 편성 - 중간커밋\n", h_seq); EXEC SQL COMMIT WORK; } } EXEC SQL CLOSE cur_pay; /* ---- 6) 트레일러('T') 기록 및 파일 클로즈 --------------------- */ EXEC SQL EXECUTE BEGIN pkg_payfile.put_trailer(:h_seq, :h_totamt); pkg_payfile.close_file; END; END-EXEC; /* ---- 7) 파일헤더 확정 (실제 기록건수 반영, 'G'->'C') ----------- */ EXEC SQL UPDATE py_file SET record_count = :h_seq, status = 'C', closed_at = SYSDATE WHERE file_id = :h_fileid; EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) VALUES (cm_log_seq.NEXTVAL, 'PAY_FILEGEN', 'I', 'PAY_FILEGEN file='||:h_fileid||' name='||:h_fname ||' rec='||:h_seq||' amt='||:h_totamt, TO_DATE(:h_bizdate,'YYYYMMDD')); /* ---- 8) 최종 커밋 --------------------------------------------- */ EXEC SQL COMMIT WORK; printf("[PAY_FILEGEN] 완료 file=%ld name=%.*s rec=%ld amt=%ld\n", h_fileid, h_fname.len, h_fname.arr, h_seq, h_totamt); EXEC SQL COMMIT WORK RELEASE; return 0; connect_err: fprintf(stderr, "[PAY_FILEGEN] DB 접속 실패: %.70s\n", sqlca.sqlerrm.sqlerrmc); return 8; sqlfail: sql_error("지급파일 생성"); /* UTL_FILE 핸들 정리 (best-effort) */ EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL EXECUTE BEGIN pkg_payfile.close_file; END; END-EXEC; EXEC SQL ROLLBACK WORK RELEASE; return 12; } /*--------------------------------------------------------------------------*/ /* sql_error - 오류 로깅 (sqlca/oraca) */ /*--------------------------------------------------------------------------*/ static void sql_error(const char *stage) { fprintf(stderr, "[PAY_FILEGEN] SQL 오류 (%s): %.70s\n", stage, sqlca.sqlerrm.sqlerrmc); fprintf(stderr, " sqlcode=%ld rows=%ld\n", sqlca.sqlcode, sqlca.sqlerrd[2]); }