규모확장 Wave A: 전 11모듈 svc 30→75·batch 22→35 + 전 모듈 운영데이터 시드

- svc 330→825 (모듈당 +45 고유 실업무: chargeback·재매입·심사워크플로·전문유형별·
  대사세분·정산시뮬·지급재처리·복식결산·마감워크플로·마스터심사·검증룰·전문라우팅 등)
- batch 242→385 (모듈당 +13), 카피북 헤더·디스패처 advertise 전 75 반영
- db/schema.d/95-module-seed.sql: 전 모듈 테이블 운영데이터(au 8.4k·st 15.9k·mg 16.9k·
  lg 14.6k·rc 7.7k·py 2.9k·vl 8.5k·cm 10.4k 등, 총 ~171k행) — base 거래와 정합
- 화면 매니페스트 825 재생성, 큐 메모리 튜닝(NDRX_MSGMAX 50→30)
- 검증: build DONE modules=11 batches=385, 12서버 runok, xadmin psc 828 AVAIL,
  매입체인 XA 커밋(status=S), 새서비스 시드데이터 스모크(ACQ_MERCHSUM/RC_STATS 실값),
  전 코퍼스 .pgc 클론 0, 헤드리스 스크린샷 확인

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hyeongwoo-choi 2026-07-20 22:09:55 +00:00
parent 19acf4ed96
commit ea90bef264
1292 changed files with 53968 additions and 20 deletions

View file

@ -0,0 +1,62 @@
/*
* cl_apprflow_batch.pgc - cl 마감 승인 워크플로 일괄 배치 (검증부 승인 + XA).
*
* 승인요청(APPR_REQ)된 완료(C) 마감을 커서로 순회, 저장 건수와 실매입 건수가
* 일치하는 건만 승인(A)으로 전환하고 불일치 건은 HOLD 표시 후 건너뛴다.
* 승인 게이트가 검증을 겸하는 워크플로. 전 구간 ONE global XA.
* host 변수는 전용 카피북 cl_apprflow_cpy.h 를 EXEC SQL INCLUDE 로 가져온다.
* Usage: cl_apprflow_batch [YYYY-MM]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *prefix = (argc > 1) ? argv[1] : "2026-07";
long ok = 0, held = 0;
EXEC SQL INCLUDE cl_apprflow_cpy;
strncpy(af_prefix, prefix, sizeof(af_prefix)-1); af_prefix[sizeof(af_prefix)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(90, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE bapf CURSOR FOR
SELECT close_id, to_char(biz_date,'YYYY-MM-DD'), txn_count FROM cl_close_log
WHERE status = 'C' AND memo LIKE '%APPR_REQ%'
AND period_key LIKE :af_prefix || '%'
ORDER BY close_id;
EXEC SQL OPEN bapf;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bapf INTO :af_cid, :af_day, :af_stored;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bapf; tpabort(0); return 1; }
EXEC SQL SELECT count(*) INTO :af_actual FROM purchase WHERE biz_date = :af_day;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bapf; tpabort(0); return 1; }
if (af_stored == af_actual) {
EXEC SQL UPDATE cl_close_log
SET status = 'A', updated_at = now() WHERE close_id = :af_cid;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bapf; tpabort(0); return 1; }
ok++;
} else {
EXEC SQL UPDATE cl_close_log
SET memo = memo || '|HOLD', updated_at = now() WHERE close_id = :af_cid;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bapf; tpabort(0); return 1; }
printf(" 승인보류 cid=%ld (%ld != %ld)\n", af_cid, af_stored, af_actual);
held++;
}
}
EXEC SQL CLOSE bapf;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_apprflow_batch COMMIT: prefix=%s 승인=%ld 보류=%ld\n", prefix, ok, held);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,8 @@
/* cl_apprflow_cpy.h - cl_apprflow_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char af_prefix[16];
char af_day[16];
long af_cid;
long af_stored;
long af_actual;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,64 @@
/*
* cl_audit_batch.pgc - cl 마감 이력 감사 배치 (4개 감사항목 집계 + XA).
*
* 기간 프리픽스에 대해 (1)재개시 흔적(REOPEN memo) (2)승인/잠금인데 잠금행
* 없음 (3)잠금시각 이후 수정 흔적 (4)사유 없는 취소(X) 4개 감사항목을
* 각각 단건 집계 SELECT 로 산출해 감사 리포트를 출력한다 (조회 전용).
* host 변수는 전용 카피북 cl_audit_cpy.h 를 EXEC SQL INCLUDE 로 가져온다.
* Usage: cl_audit_batch [YYYY-MM]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *prefix = (argc > 1) ? argv[1] : "2026-07";
long findings = 0;
EXEC SQL INCLUDE cl_audit_cpy;
strncpy(au_prefix, prefix, sizeof(au_prefix)-1); au_prefix[sizeof(au_prefix)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
printf(">>> cl_audit_batch %s 마감 이력 감사\n", prefix);
EXEC SQL SELECT count(*) INTO :au_reopened FROM cl_close_log
WHERE period_key LIKE :au_prefix || '%' AND memo LIKE 'REOPEN:%';
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
printf(" [감사1] 재개시 이력 : %ld건\n", au_reopened);
EXEC SQL SELECT count(*) INTO :au_nolockrow FROM cl_close_log c
WHERE c.period_key LIKE :au_prefix || '%' AND c.status IN ('A','L')
AND NOT EXISTS (SELECT 1 FROM cl_period_lock l
WHERE l.period_key = c.period_key AND l.locked = true);
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
printf(" [감사2] 승인/잠금인데 잠금행 없음: %ld건\n", au_nolockrow);
EXEC SQL SELECT count(*) INTO :au_tampered
FROM cl_close_log c, cl_period_lock l
WHERE c.period_key LIKE :au_prefix || '%'
AND l.period_key = c.period_key AND l.locked = true
AND c.updated_at > l.locked_at;
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
printf(" [감사3] 잠금 후 수정 흔적 : %ld건\n", au_tampered);
EXEC SQL SELECT count(*) INTO :au_emptycxl FROM cl_close_log
WHERE period_key LIKE :au_prefix || '%' AND status = 'X'
AND (memo = '' OR memo IS NULL);
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
printf(" [감사4] 사유 없는 취소 : %ld건\n", au_emptycxl);
findings = au_nolockrow + au_tampered + au_emptycxl;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_audit_batch 결과: 이상항목 합계 %ld건 -> %s\n",
findings, findings ? "감사 소명 필요" : "적합");
tpclose(); tpterm();
return findings ? 2 : 0;
}

View file

@ -0,0 +1,8 @@
/* cl_audit_cpy.h - cl_audit_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char au_prefix[16];
long au_reopened;
long au_nolockrow;
long au_tampered;
long au_emptycxl;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,54 @@
/*
* cl_chnclose_batch.pgc - cl 채널별 일괄 마감 배치 (그룹 커서 + XA).
*
* 영업일의 매입을 채널(POS/EDC/EDI/FOREIGN) 그룹 커서로 순회하며 채널마다
* CHN 마감이력을 한 건씩 생성한다. 전 구간 ONE global XA (tpcommit = 2PC).
* host 변수는 전용 카피북 cl_chnclose_cpy.h 를 EXEC SQL INCLUDE 로 가져온다.
* Usage: cl_chnclose_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
long made = 0;
EXEC SQL INCLUDE cl_chnclose_cpy;
strncpy(cc_bizdate, bizdate, sizeof(cc_bizdate)-1); cc_bizdate[sizeof(cc_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE bchn CURSOR FOR
SELECT channel, count(*), coalesce(sum(amount),0) FROM purchase
WHERE biz_date = :cc_bizdate GROUP BY channel ORDER BY channel;
EXEC SQL OPEN bchn;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bchn INTO :cc_chn, :cc_cnt, :cc_amt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bchn; tpabort(0); return 1; }
EXEC SQL SELECT nextval('cl_close_seq') INTO :cc_cid;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bchn; tpabort(0); return 1; }
snprintf(cc_memo, sizeof(cc_memo), "채널마감:%s", cc_chn);
EXEC SQL INSERT INTO cl_close_log
(close_id, close_type, period_key, biz_date, status, txn_count, total_amount, memo)
VALUES (:cc_cid, 'CHN', :cc_bizdate, :cc_bizdate, 'C', :cc_cnt, :cc_amt, :cc_memo);
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bchn; tpabort(0); return 1; }
printf(" 채널 %-8s cid=%ld cnt=%ld amt=%ld\n", cc_chn, cc_cid, cc_cnt, cc_amt);
made++;
}
EXEC SQL CLOSE bchn;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_chnclose_batch COMMIT: bizdate=%s 채널마감 %ld건 생성\n", bizdate, made);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,9 @@
/* cl_chnclose_cpy.h - cl_chnclose_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char cc_bizdate[16];
char cc_chn[16];
char cc_memo[48];
long cc_cid;
long cc_cnt;
long cc_amt;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,57 @@
/*
* cl_dupchk_batch.pgc - cl 중복 마감 정리 배치 (HAVING 그룹 커서 + 정리 + XA).
*
* 취소(X) 제외 동일 (close_type, period_key) 마감이 2건 이상인 그룹을 찾아
* 최소 close_id 한 건만 남기고 나머지를 취소(X, memo 'DUP') 처리한다.
* 그룹별 잔존/정리 건수를 리포트. 전 구간 ONE global XA.
* host 변수는 전용 카피북 cl_dupchk_cpy.h 를 EXEC SQL INCLUDE 로 가져온다.
* Usage: cl_dupchk_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
long groups = 0, cleaned = 0;
EXEC SQL INCLUDE cl_dupchk_cpy;
(void)argc; (void)argv;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(90, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE bdup CURSOR FOR
SELECT close_type, period_key, count(*), min(close_id) FROM cl_close_log
WHERE status <> 'X'
GROUP BY close_type, period_key HAVING count(*) > 1
ORDER BY close_type, period_key;
EXEC SQL OPEN bdup;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bdup INTO :dp_type, :dp_period, :dp_n, :dp_keep;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bdup; tpabort(0); return 1; }
EXEC SQL UPDATE cl_close_log
SET status = 'X', memo = 'DUP', updated_at = now()
WHERE close_type = :dp_type AND period_key = :dp_period
AND status <> 'X' AND close_id <> :dp_keep;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bdup; tpabort(0); return 1; }
dp_gone = sqlca.sqlerrd[2];
printf(" 중복그룹 %s/%s %ld건 -> keep cid=%ld, 취소 %ld건\n",
dp_type, dp_period, dp_n, dp_keep, dp_gone);
cleaned += dp_gone;
groups++;
}
EXEC SQL CLOSE bdup;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_dupchk_batch COMMIT: 중복그룹=%ld 정리=%ld건\n", groups, cleaned);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,8 @@
/* cl_dupchk_cpy.h - cl_dupchk_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char dp_type[16];
char dp_period[24];
long dp_n;
long dp_keep;
long dp_gone;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,60 @@
/*
* cl_finalize_batch.pgc - cl 가마감 -> 본마감 일괄 확정 배치 (커서 + 재집계 + XA).
*
* 월 내 가마감(PROV, status O)을 커서로 순회하며 각 일자의 매입을 재집계해
* 본마감(C, memo FINAL)으로 확정한다. 가마감 대비 건수 변동은 리포트로 출력.
* 전 구간 ONE global XA. host 변수는 전용 카피북 cl_finalize_cpy.h 사용.
* Usage: cl_finalize_batch [YYYY-MM]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *month = (argc > 1) ? argv[1] : "2026-07";
long fixed = 0, drift = 0;
EXEC SQL INCLUDE cl_finalize_cpy;
strncpy(fz_month, month, sizeof(fz_month)-1); fz_month[sizeof(fz_month)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(90, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE bfin CURSOR FOR
SELECT close_id, period_key, txn_count FROM cl_close_log
WHERE close_type = 'DAILY' AND status = 'O' AND memo = 'PROV'
AND period_key LIKE :fz_month || '%'
ORDER BY period_key;
EXEC SQL OPEN bfin;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bfin INTO :fz_cid, :fz_day, :fz_oldcnt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bfin; tpabort(0); return 1; }
EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :fz_cnt, :fz_amt
FROM purchase WHERE biz_date = :fz_day AND status IN ('M','S');
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bfin; tpabort(0); return 1; }
EXEC SQL UPDATE cl_close_log
SET status = 'C', txn_count = :fz_cnt, total_amount = :fz_amt,
memo = 'FINAL', updated_at = now()
WHERE close_id = :fz_cid AND status = 'O';
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bfin; tpabort(0); return 1; }
if (fz_cnt != fz_oldcnt) {
printf(" 본마감 %s cid=%ld 건수변동 %ld -> %ld\n", fz_day, fz_cid, fz_oldcnt, fz_cnt);
drift++;
}
fixed++;
}
EXEC SQL CLOSE bfin;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_finalize_batch COMMIT: %s 본마감 확정=%ld건 (변동 %ld건)\n", month, fixed, drift);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,9 @@
/* cl_finalize_cpy.h - cl_finalize_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char fz_month[16];
char fz_day[16];
long fz_cid;
long fz_oldcnt;
long fz_cnt;
long fz_amt;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,58 @@
/*
* cl_gapdays_batch.pgc - cl 마감 누락일 탐지/보정 배치 (NOT EXISTS 커서 + XA).
*
* 월 내 매입은 있으나 일마감이 전혀 없는 누락일을 커서로 찾아 리포트하고,
* 각 누락일에 개시(O) placeholder 마감(memo 'GAP')을 만들어 마감 담당자의
* 후속 처리 대기열에 올린다. 전 구간 ONE global XA.
* host 변수는 전용 카피북 cl_gapdays_cpy.h 를 EXEC SQL INCLUDE 로 가져온다.
* Usage: cl_gapdays_batch [YYYY-MM]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *month = (argc > 1) ? argv[1] : "2026-07";
long gaps = 0;
EXEC SQL INCLUDE cl_gapdays_cpy;
strncpy(gd_month, month, sizeof(gd_month)-1); gd_month[sizeof(gd_month)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE bgapd CURSOR FOR
SELECT to_char(p.biz_date,'YYYY-MM-DD'), count(*) FROM purchase p
WHERE to_char(p.biz_date,'YYYY-MM') = :gd_month
AND NOT EXISTS (SELECT 1 FROM cl_close_log c
WHERE c.close_type = 'DAILY'
AND c.period_key = to_char(p.biz_date,'YYYY-MM-DD'))
GROUP BY p.biz_date ORDER BY p.biz_date;
EXEC SQL OPEN bgapd;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bgapd INTO :gd_day, :gd_txn;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bgapd; tpabort(0); return 1; }
EXEC SQL SELECT nextval('cl_close_seq') INTO :gd_cid;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bgapd; tpabort(0); return 1; }
EXEC SQL INSERT INTO cl_close_log
(close_id, close_type, period_key, biz_date, status, txn_count, total_amount, memo)
VALUES (:gd_cid, 'DAILY', :gd_day, :gd_day, 'O', 0, 0, 'GAP');
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bgapd; tpabort(0); return 1; }
printf(" 누락일 %s (매입 %ld건) -> placeholder cid=%ld 개시\n", gd_day, gd_txn, gd_cid);
gaps++;
}
EXEC SQL CLOSE bgapd;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_gapdays_batch COMMIT: %s 누락일 %ld건 등록\n", month, gaps);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,7 @@
/* cl_gapdays_cpy.h - cl_gapdays_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char gd_month[16];
char gd_day[16];
long gd_cid;
long gd_txn;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,77 @@
/*
* cl_halfclose_batch.pgc - cl 반기 결산 마감 배치 (월별 소계 + 반기 마감 + XA).
*
* 연도/반기(1|2)를 받아 반기 구간의 월별 소계를 커서로 출력한 뒤 반기 전체를
* BETWEEN 집계해 HALF 마감이력(YYYY-Hn, status C)을 생성한다.
* 동일 반기 기존 마감이 있으면 거절. 전 구간 ONE global XA.
* host 변수는 전용 카피북 cl_halfclose_cpy.h 를 EXEC SQL INCLUDE 로 가져온다.
* Usage: cl_halfclose_batch [YYYY] [1|2]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *year = (argc > 1) ? argv[1] : "2026";
int half = (argc > 2) ? atoi(argv[2]) : 1;
long months = 0;
EXEC SQL INCLUDE cl_halfclose_cpy;
if (half != 1 && half != 2) { fprintf(stderr, "반기는 1|2 만 허용\n"); return 1; }
if (half == 1) {
snprintf(hf_from, sizeof(hf_from), "%s-01-01", year);
snprintf(hf_to, sizeof(hf_to), "%s-06-30", year);
} else {
snprintf(hf_from, sizeof(hf_from), "%s-07-01", year);
snprintf(hf_to, sizeof(hf_to), "%s-12-31", year);
}
snprintf(hf_period, sizeof(hf_period), "%s-H%d", year, half);
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(90, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL SELECT count(*) INTO :hf_cnt FROM cl_close_log
WHERE close_type = 'HALF' AND period_key = :hf_period AND status <> 'X';
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
if (hf_cnt > 0) {
fprintf(stderr, "cl_halfclose_batch: %s 반기마감 기존재 - 중복 불가\n", hf_period);
tpabort(0); tpclose(); tpterm();
return 2;
}
EXEC SQL DECLARE bhalf CURSOR FOR
SELECT to_char(biz_date,'YYYY-MM'), count(*), coalesce(sum(amount),0)
FROM purchase WHERE biz_date BETWEEN :hf_from AND :hf_to
GROUP BY 1 ORDER BY 1;
EXEC SQL OPEN bhalf;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bhalf INTO :hf_mon, :hf_mcnt, :hf_mamt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bhalf; tpabort(0); return 1; }
printf(" 월소계 %s cnt=%ld amt=%ld\n", hf_mon, hf_mcnt, hf_mamt);
months++;
}
EXEC SQL CLOSE bhalf;
EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :hf_cnt, :hf_amt
FROM purchase WHERE biz_date BETWEEN :hf_from AND :hf_to;
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
EXEC SQL SELECT nextval('cl_close_seq') INTO :hf_cid;
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
EXEC SQL INSERT INTO cl_close_log
(close_id, close_type, period_key, biz_date, status, txn_count, total_amount, memo)
VALUES (:hf_cid, 'HALF', :hf_period, :hf_to, 'C', :hf_cnt, :hf_amt, '반기결산');
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_halfclose_batch COMMIT: %s cid=%ld 월=%ld개 cnt=%ld amt=%ld\n",
hf_period, hf_cid, months, hf_cnt, hf_amt);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,12 @@
/* cl_halfclose_cpy.h - cl_halfclose_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char hf_from[16];
char hf_to[16];
char hf_period[16];
char hf_mon[16];
long hf_cid;
long hf_cnt;
long hf_amt;
long hf_mcnt;
long hf_mamt;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,60 @@
/*
* cl_holiday_batch.pgc - cl 휴일 마감 정책 배치 (주말 판정 + 태깅 + XA).
*
* 월 내 매입 발생 일자를 커서로 순회하며 요일을 판정, 주말(토/일) 거래일을
* 리포트하고 해당 일자의 일마감에는 memo '|HOL' 태그를 남긴다.
* 휴일 마감은 익영업일 이월 대상이라는 정책 표시. 전 구간 ONE global XA.
* host 변수는 전용 카피북 cl_holiday_cpy.h 를 EXEC SQL INCLUDE 로 가져온다.
* Usage: cl_holiday_batch [YYYY-MM]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *month = (argc > 1) ? argv[1] : "2026-07";
long weekend = 0, tagged = 0;
EXEC SQL INCLUDE cl_holiday_cpy;
strncpy(hd_month, month, sizeof(hd_month)-1); hd_month[sizeof(hd_month)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE bhol CURSOR FOR
SELECT to_char(biz_date,'YYYY-MM-DD'), to_char(biz_date,'D'), count(*)
FROM purchase
WHERE to_char(biz_date,'YYYY-MM') = :hd_month
GROUP BY biz_date ORDER BY biz_date;
EXEC SQL OPEN bhol;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bhol INTO :hd_day, :hd_dow, :hd_txn;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bhol; tpabort(0); return 1; }
if (hd_dow[0] != '1' && hd_dow[0] != '7') continue; /* 평일은 정책 대상 아님 */
weekend++;
printf(" 휴일거래 %s (요일코드 %s) %ld건 -> 익영업일 이월 대상\n", hd_day, hd_dow, hd_txn);
EXEC SQL UPDATE cl_close_log
SET memo = memo || '|HOL', updated_at = now()
WHERE close_type = 'DAILY' AND period_key = :hd_day
AND status <> 'X' AND memo NOT LIKE '%|HOL%';
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bhol; tpabort(0); return 1; }
EXEC SQL SELECT count(*) INTO :hd_marked FROM cl_close_log
WHERE close_type = 'DAILY' AND period_key = :hd_day AND memo LIKE '%|HOL%';
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bhol; tpabort(0); return 1; }
tagged += hd_marked;
}
EXEC SQL CLOSE bhol;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_holiday_batch COMMIT: %s 휴일거래일=%ld HOL태그 마감=%ld\n", month, weekend, tagged);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,8 @@
/* cl_holiday_cpy.h - cl_holiday_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char hd_month[16];
char hd_day[16];
char hd_dow[4];
long hd_txn;
long hd_marked;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,57 @@
/*
* cl_posttrig_batch.pgc - cl 마감 후속작업 트리거 배치 (INSERT..SELECT + XA).
*
* 마감 완료(C/A/L)된 영업일의 정산건 중 아직 ledger 에 XFER 전기가 없는
* 건을 NOT EXISTS 조건 INSERT..SELECT 한 방으로 일괄 전기하고, 전기 전/후
* 건수를 리포트한다. 마감 없는 일자는 거절. 전 구간 ONE global XA.
* host 변수는 전용 카피북 cl_posttrig_cpy.h 를 EXEC SQL INCLUDE 로 가져온다.
* Usage: cl_posttrig_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
long closed = 0;
EXEC SQL INCLUDE cl_posttrig_cpy;
strncpy(pt_bizdate, bizdate, sizeof(pt_bizdate)-1); pt_bizdate[sizeof(pt_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL SELECT count(*) INTO :pt_ready FROM cl_close_log
WHERE close_type = 'DAILY' AND period_key = :pt_bizdate AND status IN ('C','A','L');
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
if (pt_ready == 0) {
fprintf(stderr, "cl_posttrig_batch: %s 완료된 일마감 없음 - 후속작업 불가\n", bizdate);
tpabort(0); tpclose(); tpterm();
return 2;
}
closed = pt_ready;
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
SELECT nextval('ledger_seq'), s.purchase_id, 'XFER', s.net, s.biz_date
FROM settlement s
WHERE s.biz_date = :pt_bizdate
AND NOT EXISTS (SELECT 1 FROM ledger l
WHERE l.purchase_id = s.purchase_id AND l.entry_type = 'XFER');
if (sqlca.sqlcode < 0) { fprintf(stderr, "INSERT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
pt_ins = sqlca.sqlerrd[2];
EXEC SQL SELECT count(*) INTO :pt_total FROM ledger
WHERE biz_date = :pt_bizdate AND entry_type = 'XFER';
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_posttrig_batch COMMIT: %s (마감 %ld건 확인) 신규 XFER=%ld 누적=%ld\n",
bizdate, closed, pt_ins, pt_total);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,7 @@
/* cl_posttrig_cpy.h - cl_posttrig_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char pt_bizdate[16];
long pt_ready;
long pt_ins;
long pt_total;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,64 @@
/*
* cl_precheck_batch.pgc - cl 마감 전 체크리스트 일괄 검증 배치 (5항목 + XA).
*
* 일마감 직전에 돌려 (1)미마감 M 매입 (2)미매칭 (3)기간잠금 (4)기존 마감
* (5)보류(H) 매입 5개 항목을 점검하고 체크리스트 리포트를 출력한다.
* 하나라도 실패면 rc=2 로 종료해 후속 마감 잡을 차단한다 (조회 전용, XA 조회 브랜치).
* host 변수는 전용 카피북 cl_precheck_cpy.h 를 EXEC SQL INCLUDE 로 가져온다.
* Usage: cl_precheck_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
int fails = 0;
EXEC SQL INCLUDE cl_precheck_cpy;
strncpy(pc_bizdate, bizdate, sizeof(pc_bizdate)-1); pc_bizdate[sizeof(pc_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(30, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
printf(">>> cl_precheck_batch %s 마감 전 체크리스트\n", bizdate);
EXEC SQL SELECT count(*) INTO :pc_unclosed FROM purchase
WHERE biz_date = :pc_bizdate AND status = 'M';
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
printf(" [1] 미마감(M) 매입 : %ld건 %s\n", pc_unclosed, pc_unclosed ? "FAIL" : "ok");
if (pc_unclosed) fails++;
EXEC SQL SELECT count(*) INTO :pc_unmatch FROM ac_unmatch WHERE biz_date = :pc_bizdate;
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
printf(" [2] 미매칭 건 : %ld건 %s\n", pc_unmatch, pc_unmatch ? "FAIL" : "ok");
if (pc_unmatch) fails++;
EXEC SQL SELECT count(*) INTO :pc_locked FROM cl_period_lock
WHERE period_key = :pc_bizdate AND locked = true;
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
printf(" [3] 기간 잠금 : %s\n", pc_locked ? "잠금됨 FAIL" : "ok");
if (pc_locked) fails++;
EXEC SQL SELECT count(*) INTO :pc_already FROM cl_close_log
WHERE close_type = 'DAILY' AND period_key = :pc_bizdate AND status <> 'X';
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
printf(" [4] 기존 일마감 : %ld건 %s\n", pc_already, pc_already ? "FAIL" : "ok");
if (pc_already) fails++;
EXEC SQL SELECT count(*) INTO :pc_hold FROM purchase
WHERE biz_date = :pc_bizdate AND status = 'H';
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
printf(" [5] 보류(H) 매입 : %ld건 %s\n", pc_hold, pc_hold ? "FAIL" : "ok");
if (pc_hold) fails++;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_precheck_batch 결과: %d항목 실패 -> %s\n", fails, fails ? "마감 차단" : "마감 진행 가능");
tpclose(); tpterm();
return fails ? 2 : 0;
}

View file

@ -0,0 +1,9 @@
/* cl_precheck_cpy.h - cl_precheck_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char pc_bizdate[16];
long pc_unclosed;
long pc_unmatch;
long pc_locked;
long pc_already;
long pc_hold;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,62 @@
/*
* cl_provclose_batch.pgc - cl 가마감 일괄 생성 배치 (누락일 커서 + XA).
*
* 월(YYYY-MM) 내 매입은 있으나 어떤 일마감도 없는 일자를 커서로 찾아
* 일자마다 status O + memo 'PROV' 가마감을 집계와 함께 생성한다.
* 본마감 전 임시 스냅샷 성격. 전 구간 ONE global XA.
* host 변수는 전용 카피북 cl_provclose_cpy.h 를 EXEC SQL INCLUDE 로 가져온다.
* Usage: cl_provclose_batch [YYYY-MM]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *month = (argc > 1) ? argv[1] : "2026-07";
long made = 0;
EXEC SQL INCLUDE cl_provclose_cpy;
strncpy(pv_month, month, sizeof(pv_month)-1); pv_month[sizeof(pv_month)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(90, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE bprov CURSOR FOR
SELECT DISTINCT to_char(p.biz_date,'YYYY-MM-DD') FROM purchase p
WHERE to_char(p.biz_date,'YYYY-MM') = :pv_month
AND NOT EXISTS (SELECT 1 FROM cl_close_log c
WHERE c.close_type = 'DAILY'
AND c.period_key = to_char(p.biz_date,'YYYY-MM-DD')
AND c.status <> 'X')
ORDER BY 1;
EXEC SQL OPEN bprov;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bprov INTO :pv_day;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bprov; tpabort(0); return 1; }
EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :pv_cnt, :pv_amt
FROM purchase WHERE biz_date = :pv_day AND status IN ('M','S');
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bprov; tpabort(0); return 1; }
EXEC SQL SELECT nextval('cl_close_seq') INTO :pv_cid;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bprov; tpabort(0); return 1; }
EXEC SQL INSERT INTO cl_close_log
(close_id, close_type, period_key, biz_date, status, txn_count, total_amount, memo)
VALUES (:pv_cid, 'DAILY', :pv_day, :pv_day, 'O', :pv_cnt, :pv_amt, 'PROV');
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bprov; tpabort(0); return 1; }
printf(" 가마감 %s cid=%ld cnt=%ld\n", pv_day, pv_cid, pv_cnt);
made++;
}
EXEC SQL CLOSE bprov;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_provclose_batch COMMIT: %s 가마감 %ld건 생성\n", month, made);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,8 @@
/* cl_provclose_cpy.h - cl_provclose_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char pv_month[16];
char pv_day[16];
long pv_cid;
long pv_cnt;
long pv_amt;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,62 @@
/*
* cl_slamon_batch.pgc - cl 마감 SLA 모니터 배치 (지연 마감 태깅 + XA).
*
* 기간 내 일마감 중 생성시각이 기준시각(HHMI, 기본 1000)을 넘긴 지연 마감을
* 커서로 찾아 memo 에 '|SLA' 태그를 남기고 지연 목록을 출력한다.
* 이미 태깅된 건은 제외. 전 구간 ONE global XA.
* host 변수는 전용 카피북 cl_slamon_cpy.h 를 EXEC SQL INCLUDE 로 가져온다.
* Usage: cl_slamon_batch [YYYY-MM] [HHMI]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *prefix = (argc > 1) ? argv[1] : "2026-07";
const char *cutoff = (argc > 2) ? argv[2] : "1000";
long tagged = 0;
EXEC SQL INCLUDE cl_slamon_cpy;
strncpy(sl_prefix, prefix, sizeof(sl_prefix)-1); sl_prefix[sizeof(sl_prefix)-1] = 0;
strncpy(sl_cut, cutoff, sizeof(sl_cut)-1); sl_cut[sizeof(sl_cut)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE bsla CURSOR FOR
SELECT close_id, period_key, to_char(created_at,'HH24MI') FROM cl_close_log
WHERE close_type = 'DAILY' AND status <> 'X'
AND period_key LIKE :sl_prefix || '%'
AND to_char(created_at,'HH24MI') > :sl_cut
AND memo NOT LIKE '%|SLA%'
ORDER BY created_at;
EXEC SQL OPEN bsla;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bsla INTO :sl_cid, :sl_period, :sl_hhmi;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bsla; tpabort(0); return 1; }
EXEC SQL UPDATE cl_close_log
SET memo = memo || '|SLA', updated_at = now() WHERE close_id = :sl_cid;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bsla; tpabort(0); return 1; }
printf(" SLA위반 %s cid=%ld 생성 %s (기준 %s)\n", sl_period, sl_cid, sl_hhmi, sl_cut);
tagged++;
}
EXEC SQL CLOSE bsla;
EXEC SQL SELECT count(*) INTO :sl_late FROM cl_close_log
WHERE close_type = 'DAILY' AND period_key LIKE :sl_prefix || '%'
AND memo LIKE '%|SLA%';
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_slamon_batch COMMIT: prefix=%s 신규태깅=%ld 누적 SLA위반=%ld\n", prefix, tagged, sl_late);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,9 @@
/* cl_slamon_cpy.h - cl_slamon_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char sl_prefix[16];
char sl_cut[8];
char sl_hhmi[8];
char sl_period[24];
long sl_cid;
long sl_late;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,62 @@
/*
* cl_xcheck_batch.pgc - cl 마감 데이터 건수/금액 크로스체크 배치 (이중 대사 + XA).
*
* 기간 내 일마감을 커서로 순회하며 마감이력의 (txn_count, total_amount) 와
* 실매입의 (count, sum) 을 동시에 대사해 건수/금액 불일치를 각각 집계,
* 불일치 상세를 표로 출력한다 (조회 전용). host 변수는 cl_xcheck_cpy.h 사용.
* Usage: cl_xcheck_batch [YYYY-MM]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *prefix = (argc > 1) ? argv[1] : "2026-07";
long seen = 0, cntbad = 0, amtbad = 0;
EXEC SQL INCLUDE cl_xcheck_cpy;
strncpy(xc_prefix, prefix, sizeof(xc_prefix)-1); xc_prefix[sizeof(xc_prefix)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE bxch CURSOR FOR
SELECT close_id, to_char(biz_date,'YYYY-MM-DD'), txn_count, total_amount
FROM cl_close_log
WHERE close_type = 'DAILY' AND status IN ('C','A','L')
AND period_key LIKE :xc_prefix || '%'
ORDER BY biz_date;
EXEC SQL OPEN bxch;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bxch INTO :xc_cid, :xc_day, :xc_logcnt, :xc_logamt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bxch; tpabort(0); return 1; }
EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :xc_realcnt, :xc_realamt
FROM purchase WHERE biz_date = :xc_day;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bxch; tpabort(0); return 1; }
seen++;
if (xc_logcnt != xc_realcnt) {
printf(" [건수] %s cid=%ld 이력=%ld 실제=%ld\n", xc_day, xc_cid, xc_logcnt, xc_realcnt);
cntbad++;
}
if (xc_logamt != xc_realamt) {
printf(" [금액] %s cid=%ld 이력=%ld 실제=%ld (편차 %+ld)\n",
xc_day, xc_cid, xc_logamt, xc_realamt, xc_logamt - xc_realamt);
amtbad++;
}
}
EXEC SQL CLOSE bxch;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> cl_xcheck_batch COMMIT: prefix=%s 대사=%ld 건수불일치=%ld 금액불일치=%ld\n",
prefix, seen, cntbad, amtbad);
tpclose(); tpterm();
return (cntbad + amtbad) ? 2 : 0;
}

View file

@ -0,0 +1,10 @@
/* cl_xcheck_cpy.h - cl_xcheck_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */
EXEC SQL BEGIN DECLARE SECTION;
char xc_prefix[16];
char xc_day[16];
long xc_cid;
long xc_logcnt;
long xc_logamt;
long xc_realcnt;
long xc_realamt;
EXEC SQL END DECLARE SECTION;