규모확장 Wave B: 전 11모듈 svc 75→133·batch 35→46 (BAIS 온라인 1:1)

- svc 825→1,463 (모듈당 133), batch 385→506 (모듈당 46) — 전부 새 고유 실업무 로직
  + 카피북 헤더, 디스패처 133 advertise, 한글 기능설명(매니페스트 추출)
- BAIS As-Is 대비: 온라인 1,463 vs 1,466(99.8%), 배치 506 vs 503(초과)
- 화면 매니페스트 1,463 재생성
- 검증: build DONE modules=11 batches=506, 12서버 runok, xadmin psc 1,466 AVAIL,
  매입체인 XA 커밋, 신규서비스 실데이터 스모크, 전 코퍼스 .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:55:47 +00:00
parent ea90bef264
commit c62d91b7a7
1526 changed files with 59213 additions and 12 deletions

View file

@ -0,0 +1,45 @@
/*
* py_apprpend_batch.pgc - py 승인대기 리포트 배치 (커서 GROUP BY, XA).
*
* 아직 승인 전인 요청(R) 지급건을 가맹점별로 묶어 대기 건수/요청금액 합을
* 건수 내림차순 커서로 훑어 stdout 리포트로 출력한다 (승인 워크로드 파악).
* Usage: py_apprpend_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
long groups = 0, waiting = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64];
long h_cnt, h_amt;
EXEC SQL END DECLARE SECTION;
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 pap CURSOR FOR
SELECT merchant_id, count(*), coalesce(sum(amount),0)
FROM py_payout WHERE status = 'R'
GROUP BY merchant_id ORDER BY 2 DESC, 1;
EXEC SQL OPEN pap;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH pap INTO :h_merch, :h_cnt, :h_amt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pap; tpabort(0); return 1; }
groups++; waiting += h_cnt;
printf(" %s: 대기 %ld건 요청 %ld원\n", h_merch, h_cnt, h_amt);
}
EXEC SQL CLOSE pap;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> py_apprpend_batch COMMIT: merchants=%ld waiting=%ld\n", groups, waiting);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,47 @@
/*
* py_bankverify_batch.pgc - py 지급파일 전건 대사 배치 (커서 검증, XA).
*
* 생성(G) 상태 파일을 커서로 훑어 파일 헤더의 건수/금액과 실제 편성 지급건의
* 건수/net 합을 비교해 불일치 파일을 리포트한다. 갱신 없이 검증만 수행한다.
* host 변수는 전용 카피북 py_bankverify_cpy.h 를 EXEC SQL INCLUDE.
* Usage: py_bankverify_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
long files = 0, mism = 0;
EXEC SQL INCLUDE py_bankverify_cpy;
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(120, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE pbv CURSOR FOR
SELECT file_id, record_count, total_amount FROM py_file WHERE status = 'G' ORDER BY file_id;
EXEC SQL OPEN pbv;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH pbv INTO :c_fid, :c_hcnt, :c_hsum;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pbv; tpabort(0); return 1; }
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :c_acnt, :c_asum
FROM py_payout WHERE file_id = :c_fid;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pbv; tpabort(0); return 1; }
files++;
if (c_hcnt != c_acnt || c_hsum != c_asum) {
mism++;
printf(" 파일 %ld 불일치: hdr(%ld/%ld) act(%ld/%ld)\n", c_fid, c_hcnt, c_hsum, c_acnt, c_asum);
}
}
EXEC SQL CLOSE pbv;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> py_bankverify_batch COMMIT: files=%ld mismatch=%ld\n", files, mism);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,4 @@
/* py_bankverify_cpy.h - py_bankverify_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
long c_fid, c_hcnt, c_hsum, c_acnt, c_asum;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,44 @@
/*
* py_carryover_batch.pgc - py 미지급 이월 배치 (SELECT 카운트 + UPDATE, XA).
*
* 지정 기준일 이전의 미지급 요청(R) 건 수를 먼저 집계한 뒤, 그 건들의
* biz_date 를 차기 영업일로 일괄 이월한다. ONE XA transaction.
* Usage: py_carryover_batch [CUTOFF-YYYY-MM-DD] [NEXT-YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *cut = (argc > 1) ? argv[1] : "2026-07-19";
const char *next = (argc > 2) ? argv[2] : "2026-07-20";
long moved;
EXEC SQL BEGIN DECLARE SECTION;
char h_cut[16], h_next[16];
long h_cnt;
EXEC SQL END DECLARE SECTION;
strncpy(h_cut, cut, sizeof(h_cut)-1); h_cut[sizeof(h_cut)-1] = 0;
strncpy(h_next, next, sizeof(h_next)-1); h_next[sizeof(h_next)-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 :h_cnt
FROM py_payout WHERE status = 'R' AND biz_date < :h_cut;
if (sqlca.sqlcode < 0) { fprintf(stderr, "CNT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
EXEC SQL UPDATE py_payout SET biz_date = :h_next, updated_at = now()
WHERE status = 'R' AND biz_date < :h_cut;
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPD FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
moved = sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> py_carryover_batch COMMIT: cutoff=%s next=%s candidates=%ld moved=%ld\n", cut, next, h_cnt, moved);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,39 @@
/*
* py_dormscan_batch.pgc - py 휴면 계좌 스캔 배치 (NOT EXISTS UPDATE, XA).
*
* 지정 기준일 이후 지급 이력이 전혀 없는 활성(ACTIVE) 계좌를 NOT EXISTS
* 서브쿼리로 골라 휴면(DORMANT)으로 일괄 전환한다. ONE XA transaction.
* Usage: py_dormscan_batch [SINCE-YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *since = (argc > 1) ? argv[1] : "2026-04-01";
long dormant;
EXEC SQL BEGIN DECLARE SECTION;
char h_since[16];
EXEC SQL END DECLARE SECTION;
strncpy(h_since, since, sizeof(h_since)-1); h_since[sizeof(h_since)-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 UPDATE py_account SET status = 'DORMANT', updated_at = now()
WHERE status = 'ACTIVE'
AND NOT EXISTS (SELECT 1 FROM py_payout p
WHERE p.account_no = py_account.account_no AND p.biz_date >= :h_since);
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPD FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
dormant = sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> py_dormscan_batch COMMIT: since=%s dormant=%ld\n", since, dormant);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,41 @@
/*
* py_feecap_batch.pgc - py 수수료 상한 일괄 적용 배치 (INSERT..SELECT + UPDATE, XA).
*
* 미지급(R/A) 건 중 수수료가 상한(인자)을 넘는 건을 대상으로, 초과분을
* py_adjust 에 양수 조정으로 적재한 뒤 fee 를 상한까지 낮추고 net 을 보정한다.
* host 변수는 전용 카피북 py_feecap_cpy.h 를 EXEC SQL INCLUDE.
* Usage: py_feecap_batch [YYYY-MM-DD] [CAP]
*/
#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";
EXEC SQL INCLUDE py_feecap_cpy;
strncpy(c_bizdate, bizdate, sizeof(c_bizdate)-1); c_bizdate[sizeof(c_bizdate)-1] = 0;
c_cap = (argc > 2) ? atol(argv[2]) : 5000;
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 INSERT INTO py_adjust (adj_id, payout_id, delta, reason, biz_date)
SELECT nextval('py_adjust_seq'), payout_id, fee - :c_cap, '수수료상한배치', :c_bizdate
FROM py_payout WHERE status IN ('R','A') AND fee > :c_cap;
if (sqlca.sqlcode < 0) { fprintf(stderr, "ADJ FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
EXEC SQL UPDATE py_payout SET net = net + (fee - :c_cap), fee = :c_cap, updated_at = now()
WHERE status IN ('R','A') AND fee > :c_cap;
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPD FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
c_capped = sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> py_feecap_batch COMMIT: bizdate=%s cap=%ld capped=%ld\n", bizdate, c_cap, c_capped);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,5 @@
/* py_feecap_cpy.h - py_feecap_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char c_bizdate[16];
long c_cap, c_capped;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,45 @@
/*
* py_garnish_batch.pgc - py 압류/가압류 계좌 지급보류 배치 (커서 + UPDATE, XA).
*
* 압류(SEIZED)/가압류(GARNISH) 상태 계좌를 커서로 훑어 각 계좌의 미지급(R/A)
* 건을 '압류보류'로 보류(H) 전이한다. 계좌별 보류 건수를 리포트한다.
* host 변수는 전용 카피북 py_garnish_cpy.h 를 EXEC SQL INCLUDE.
* Usage: py_garnish_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
long accts = 0, held = 0;
EXEC SQL INCLUDE py_garnish_cpy;
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(120, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE pgr CURSOR FOR
SELECT account_no FROM py_account WHERE status IN ('SEIZED','GARNISH') ORDER BY account_no;
EXEC SQL OPEN pgr;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH pgr INTO :c_acct;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pgr; tpabort(0); return 1; }
EXEC SQL UPDATE py_payout SET status = 'H', reject_reason = '압류보류', updated_at = now()
WHERE account_no = :c_acct AND status IN ('R','A');
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pgr; tpabort(0); return 1; }
c_held = sqlca.sqlerrd[2];
accts++; held += c_held;
if (c_held > 0) printf(" 계좌 %s: %ld건 압류보류\n", c_acct, c_held);
}
EXEC SQL CLOSE pgr;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> py_garnish_batch COMMIT: accounts=%ld held=%ld\n", accts, held);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,5 @@
/* py_garnish_cpy.h - py_garnish_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char c_acct[32];
long c_held;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,47 @@
/*
* py_notifypush_batch.pgc - py 지급완료 푸시 통지 시뮬 배치 (조인 커서, XA).
*
* 당일 지급완료(P) 건을 py_account 조인 커서로 훑어 예금주/은행/금액으로
* 푸시 통지 발송을 stdout 으로 시뮬레이션한다 (실발송 없음). 은행별 집계 없이
* 건별 발송. host 변수는 전용 카피북 py_notifypush_cpy.h 를 EXEC SQL INCLUDE.
* Usage: py_notifypush_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 pushed = 0, total = 0;
EXEC SQL INCLUDE py_notifypush_cpy;
strncpy(c_bizdate, bizdate, sizeof(c_bizdate)-1); c_bizdate[sizeof(c_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 pnp CURSOR FOR
SELECT p.payout_id, p.net, a.holder, a.bank_code
FROM py_payout p JOIN py_account a ON p.account_no = a.account_no
WHERE p.biz_date = :c_bizdate AND p.status = 'P'
ORDER BY a.bank_code, p.payout_id;
EXEC SQL OPEN pnp;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH pnp INTO :c_pid, :c_net, :c_holder, :c_bank;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pnp; tpabort(0); return 1; }
printf(" [PUSH시뮬] %s(%s) %ld원 지급알림 (payout=%ld)\n", c_holder, c_bank, c_net, c_pid);
pushed++; total += c_net;
}
EXEC SQL CLOSE pnp;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> py_notifypush_batch COMMIT: bizdate=%s pushed=%ld total=%ld\n", bizdate, pushed, total);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,7 @@
/* py_notifypush_cpy.h - py_notifypush_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char c_bizdate[16];
char c_holder[64];
char c_bank[8];
long c_pid, c_net;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,41 @@
/*
* py_pendaging_batch.pgc - py 미지급 경과일 집계 배치 (CASE 단일 집계, XA).
*
* 미지급(R/A/H) 건을 기준일 대비 경과일수로 당일/3일내/장기(초과) 3구간으로
* CASE 분류해 구간별 건수를 한 번의 집계 쿼리로 산출해 리포트한다.
* Usage: py_pendaging_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";
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16];
long h_fresh, h_mid, h_old;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_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
coalesce(sum(CASE WHEN :h_bizdate::date - biz_date <= 0 THEN 1 ELSE 0 END),0),
coalesce(sum(CASE WHEN :h_bizdate::date - biz_date BETWEEN 1 AND 3 THEN 1 ELSE 0 END),0),
coalesce(sum(CASE WHEN :h_bizdate::date - biz_date > 3 THEN 1 ELSE 0 END),0)
INTO :h_fresh, :h_mid, :h_old
FROM py_payout WHERE status IN ('R','A','H');
if (sqlca.sqlcode < 0) { fprintf(stderr, "AGG FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> py_pendaging_batch COMMIT: base=%s 당일=%ld 3일내=%ld 장기=%ld\n",
bizdate, h_fresh, h_mid, h_old);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,38 @@
/*
* py_requeue_batch.pgc - py 실패 재처리 일괄 적재 배치 (단일 UPDATE, XA).
*
* 당일 반려(J) 건 중 재시도 여력(retry_count<3)이 있는 건을 요청(R)으로
* 되돌리며 retry_count 를 +1 해 재처리 큐에 일괄 적재한다. ONE XA txn.
* Usage: py_requeue_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 requeued;
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_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 UPDATE py_payout
SET status = 'R', retry_count = retry_count + 1, reject_reason = '', updated_at = now()
WHERE biz_date = :h_bizdate AND status = 'J' AND retry_count < 3;
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPD FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
requeued = sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> py_requeue_batch COMMIT: bizdate=%s requeued=%ld\n", bizdate, requeued);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,50 @@
/*
* py_statmonth_batch.pgc - py 월별 지급 집계 리포트 배치 (커서 GROUP BY, XA).
*
* 지정 기간의 지급건을 'YYYY-MM' 월 단위로 묶어 월별 건수/net 합을 커서로
* 훑어 stdout 리포트로 출력한다 (갱신 없음, 읽기 전용 리포트).
* Usage: py_statmonth_batch [FROM-YYYY-MM-DD] [TO-YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *from = (argc > 1) ? argv[1] : "2026-01-01";
const char *to = (argc > 2) ? argv[2] : "2026-12-31";
long months = 0, grand = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_from[16], h_to[16], h_mon[16];
long h_cnt, h_amt;
EXEC SQL END DECLARE SECTION;
strncpy(h_from, from, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0;
strncpy(h_to, to, sizeof(h_to)-1); h_to[sizeof(h_to)-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 psm CURSOR FOR
SELECT to_char(biz_date, 'YYYY-MM'), count(*), coalesce(sum(net),0)
FROM py_payout WHERE biz_date BETWEEN :h_from AND :h_to
GROUP BY 1 ORDER BY 1;
EXEC SQL OPEN psm;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH psm INTO :h_mon, :h_cnt, :h_amt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE psm; tpabort(0); return 1; }
months++; grand += h_amt;
printf(" %s: %ld건 %ld원\n", h_mon, h_cnt, h_amt);
}
EXEC SQL CLOSE psm;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> py_statmonth_batch COMMIT: months=%ld grand=%ld\n", months, grand);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,50 @@
/*
* py_xferconf_batch.pgc - py 이체 기표 일괄 확정 배치 (커서 + INSERT, XA).
*
* 당일 지급완료(P) 이면서 XFER 기표가 아직 없는 건을 NOT EXISTS 커서로 뽑아
* py_ledger 에 net 만큼 XFER 기표를 일괄 생성한다. ONE XA txn.
* host 변수는 전용 카피북 py_xferconf_cpy.h 를 EXEC SQL INCLUDE.
* Usage: py_xferconf_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 posted = 0;
EXEC SQL INCLUDE py_xferconf_cpy;
strncpy(c_bizdate, bizdate, sizeof(c_bizdate)-1); c_bizdate[sizeof(c_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(120, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE pxc CURSOR FOR
SELECT payout_id, net FROM py_payout p
WHERE p.biz_date = :c_bizdate AND p.status = 'P'
AND NOT EXISTS (SELECT 1 FROM py_ledger l
WHERE l.payout_id = p.payout_id AND l.entry_type = 'XFER')
ORDER BY payout_id;
EXEC SQL OPEN pxc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH pxc INTO :c_pid, :c_net;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pxc; tpabort(0); return 1; }
EXEC SQL INSERT INTO py_ledger (ledger_id, payout_id, entry_type, amount, biz_date)
VALUES (nextval('py_ledger_seq'), :c_pid, 'XFER', :c_net, :c_bizdate);
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pxc; tpabort(0); return 1; }
posted++;
}
EXEC SQL CLOSE pxc;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> py_xferconf_batch COMMIT: bizdate=%s posted=%ld\n", bizdate, posted);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,5 @@
/* py_xferconf_cpy.h - py_xferconf_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char c_bizdate[16];
long c_pid, c_net;
EXEC SQL END DECLARE SECTION;