규모확장 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:
parent
19acf4ed96
commit
ea90bef264
1292 changed files with 53968 additions and 20 deletions
54
app/src/py/batch/py_acctsync_batch.pgc
Normal file
54
app/src/py/batch/py_acctsync_batch.pgc
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* py_acctsync_batch.pgc - py 지급계좌 잔액 정합 점검 배치 (커서 + REVIEW 마킹, XA).
|
||||
*
|
||||
* 잔액이 음수로 뒤집힌 지급계좌를 커서로 찾아 REVIEW 상태로 마킹해
|
||||
* 지급 경로에서 격리하고, 전체 최저 잔액도 리포트한다.
|
||||
* host 변수는 전용 카피북 py_acctsync_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_acctsync_batch
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
long negatives = 0;
|
||||
|
||||
EXEC SQL INCLUDE py_acctsync_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(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
|
||||
|
||||
EXEC SQL DECLARE pas CURSOR FOR
|
||||
SELECT account_no, merchant_id, balance FROM py_account
|
||||
WHERE balance < 0 ORDER BY balance;
|
||||
EXEC SQL OPEN pas;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
for (;;) {
|
||||
EXEC SQL FETCH pas INTO :c_acct, :c_merch, :c_bal;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pas; tpabort(0); return 1; }
|
||||
negatives++;
|
||||
printf(" [음수잔액] %s (%s) balance=%ld -> REVIEW\n", c_acct, c_merch, c_bal);
|
||||
}
|
||||
EXEC SQL CLOSE pas;
|
||||
|
||||
EXEC SQL UPDATE py_account SET status = 'REVIEW', updated_at = now()
|
||||
WHERE balance < 0 AND status <> 'REVIEW';
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
c_flagged = sqlca.sqlerrd[2];
|
||||
|
||||
EXEC SQL SELECT coalesce(min(balance),0) INTO :c_minbal FROM py_account;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "SELECT FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_acctsync_batch COMMIT: 음수계좌=%ld REVIEW신규=%ld 최저잔액=%ld\n",
|
||||
negatives, c_flagged, c_minbal);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
6
app/src/py/batch/py_acctsync_cpy.h
Normal file
6
app/src/py/batch/py_acctsync_cpy.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* py_acctsync_cpy.h - py_acctsync_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char c_acct[32];
|
||||
char c_merch[64];
|
||||
long c_bal, c_flagged, c_minbal;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
63
app/src/py/batch/py_advrepay_batch.pgc
Normal file
63
app/src/py/batch/py_advrepay_batch.pgc
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* py_advrepay_batch.pgc - py 선지급 분할 상환 배치 (커서 + adjust 기록, XA).
|
||||
*
|
||||
* 잔여 net 이 남은 ADVANCE 지급건을 커서로 훑어 상환률(bps)만큼 회수:
|
||||
* py_adjust 음수 기록 + net 차감, 완납 시 마감(X) 전이까지 처리한다.
|
||||
* host 변수는 전용 카피북 py_advrepay_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_advrepay_batch [YYYY-MM-DD] [RATE_BPS]
|
||||
*/
|
||||
#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 repaid = 0, repaid_amt = 0, closed = 0;
|
||||
|
||||
EXEC SQL INCLUDE py_advrepay_cpy;
|
||||
|
||||
strncpy(c_bizdate, bizdate, sizeof(c_bizdate)-1); c_bizdate[sizeof(c_bizdate)-1] = 0;
|
||||
c_bps = (argc > 2) ? atol(argv[2]) : 1000; /* 기본 회당 10% 상환 */
|
||||
if (c_bps <= 0 || c_bps > 10000) c_bps = 1000;
|
||||
|
||||
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 par CURSOR FOR
|
||||
SELECT payout_id, net FROM py_payout
|
||||
WHERE channel = 'ADVANCE' AND net > 0 AND status NOT IN ('C','V','X')
|
||||
ORDER BY payout_id;
|
||||
EXEC SQL OPEN par;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
for (;;) {
|
||||
EXEC SQL FETCH par INTO :c_pid, :c_net;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE par; tpabort(0); return 1; }
|
||||
c_repay = c_net * c_bps / 10000;
|
||||
if (c_repay <= 0) c_repay = c_net; /* 소액 잔여는 완납 처리 */
|
||||
c_newnet = c_net - c_repay;
|
||||
c_adj = -c_repay;
|
||||
EXEC SQL INSERT INTO py_adjust (adj_id, payout_id, delta, reason, biz_date)
|
||||
VALUES (nextval('py_adjust_seq'), :c_pid, :c_adj, '선지급상환(배치)', :c_bizdate);
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE par; tpabort(0); return 1; }
|
||||
EXEC SQL UPDATE py_payout
|
||||
SET net = :c_newnet,
|
||||
status = CASE WHEN :c_newnet <= 0 THEN 'X' ELSE status END,
|
||||
updated_at = now()
|
||||
WHERE payout_id = :c_pid;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE par; tpabort(0); return 1; }
|
||||
repaid++; repaid_amt += c_repay;
|
||||
if (c_newnet <= 0) closed++;
|
||||
}
|
||||
EXEC SQL CLOSE par;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_advrepay_batch COMMIT: bps=%ld 상환=%ld건/%ld원 완납마감=%ld건\n",
|
||||
c_bps, repaid, repaid_amt, closed);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
6
app/src/py/batch/py_advrepay_cpy.h
Normal file
6
app/src/py/batch/py_advrepay_cpy.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* py_advrepay_cpy.h - py_advrepay_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char c_bizdate[16];
|
||||
long c_bps;
|
||||
long c_pid, c_net, c_repay, c_newnet, c_adj;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
66
app/src/py/batch/py_bankfile_batch.pgc
Normal file
66
app/src/py/batch/py_bankfile_batch.pgc
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* py_bankfile_batch.pgc - py 은행별 지급파일 일괄 생성 배치 (커서 + 파일편성, XA).
|
||||
*
|
||||
* 당일 승인(A) 지급건의 은행코드를 DISTINCT 커서로 훑어, 은행마다 py_file
|
||||
* 헤더를 만들고 해당 은행 건을 파일편성(F)+file_id 로 전이한다. ONE XA txn.
|
||||
* host 변수는 전용 카피북 py_bankfile_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_bankfile_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 files = 0, recs = 0;
|
||||
|
||||
EXEC SQL INCLUDE py_bankfile_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 pbf CURSOR FOR
|
||||
SELECT DISTINCT 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 = 'A'
|
||||
ORDER BY a.bank_code;
|
||||
EXEC SQL OPEN pbf;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
|
||||
for (;;) {
|
||||
EXEC SQL FETCH pbf INTO :c_bank;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE pbf; tpabort(0); return 1; }
|
||||
|
||||
EXEC SQL SELECT count(*), coalesce(sum(p.net),0) INTO :c_cnt, :c_sum
|
||||
FROM py_payout p JOIN py_account a ON p.account_no = a.account_no
|
||||
WHERE p.biz_date = :c_bizdate AND p.status = 'A' AND a.bank_code = :c_bank;
|
||||
if (sqlca.sqlcode < 0 || c_cnt == 0) continue;
|
||||
|
||||
snprintf(c_fname, sizeof(c_fname), "PAYOUT_%s_%s.dat", c_bank, c_bizdate);
|
||||
EXEC SQL SELECT nextval('py_file_seq') INTO :c_fid;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pbf; tpabort(0); return 1; }
|
||||
EXEC SQL INSERT INTO py_file (file_id, biz_date, file_name, record_count, total_amount, status)
|
||||
VALUES (:c_fid, :c_bizdate, :c_fname, :c_cnt, :c_sum, 'G');
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pbf; tpabort(0); return 1; }
|
||||
EXEC SQL UPDATE py_payout SET status = 'F', file_id = :c_fid, updated_at = now()
|
||||
FROM py_account a
|
||||
WHERE py_payout.account_no = a.account_no AND py_payout.biz_date = :c_bizdate
|
||||
AND py_payout.status = 'A' AND a.bank_code = :c_bank;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pbf; tpabort(0); return 1; }
|
||||
files++; recs += c_cnt;
|
||||
printf(" 은행 %s: 파일 %ld 생성 (%ld건 / %ld원)\n", c_bank, c_fid, c_cnt, c_sum);
|
||||
}
|
||||
EXEC SQL CLOSE pbf;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_bankfile_batch COMMIT: bizdate=%s files=%ld recs=%ld\n", bizdate, files, recs);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
7
app/src/py/batch/py_bankfile_cpy.h
Normal file
7
app/src/py/batch/py_bankfile_cpy.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/* py_bankfile_cpy.h - py_bankfile_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char c_bizdate[16];
|
||||
char c_bank[8];
|
||||
char c_fname[64];
|
||||
long c_fid, c_cnt, c_sum;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
59
app/src/py/batch/py_dormantstop_batch.pgc
Normal file
59
app/src/py/batch/py_dormantstop_batch.pgc
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* py_dormantstop_batch.pgc - py 휴면 가맹점 지급정지 배치 (NOT EXISTS 커서, XA).
|
||||
*
|
||||
* 최근 N일 지급 이력이 없는 정상 가맹점을 NOT EXISTS 커서로 뽑아, 가맹점별로
|
||||
* 지급계좌를 DORMANT 로 내리고 잔여 요청(R)건을 '휴면정지' 보류시킨다.
|
||||
* host 변수는 전용 카피북 py_dormantstop_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_dormantstop_batch [DAYS]
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
long stopped = 0, total_accts = 0, total_held = 0;
|
||||
|
||||
EXEC SQL INCLUDE py_dormantstop_cpy;
|
||||
|
||||
c_days = (argc > 1) ? atol(argv[1]) : 30;
|
||||
if (c_days <= 0) c_days = 30;
|
||||
|
||||
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 pds CURSOR FOR
|
||||
SELECT m.merchant_id FROM merchant m
|
||||
WHERE m.status = 'ACTIVE'
|
||||
AND NOT EXISTS (SELECT 1 FROM py_payout p
|
||||
WHERE p.merchant_id = m.merchant_id
|
||||
AND p.created_at > now() - :c_days * interval '1 day')
|
||||
ORDER BY m.merchant_id;
|
||||
EXEC SQL OPEN pds;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
for (;;) {
|
||||
EXEC SQL FETCH pds INTO :c_merch;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pds; tpabort(0); return 1; }
|
||||
EXEC SQL UPDATE py_account SET status = 'DORMANT', updated_at = now()
|
||||
WHERE merchant_id = :c_merch AND status = 'ACTIVE';
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pds; tpabort(0); return 1; }
|
||||
c_accts = sqlca.sqlerrd[2];
|
||||
EXEC SQL UPDATE py_payout SET status = 'H', reject_reason = '휴면정지', updated_at = now()
|
||||
WHERE merchant_id = :c_merch AND status = 'R';
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pds; tpabort(0); return 1; }
|
||||
c_held = sqlca.sqlerrd[2];
|
||||
stopped++; total_accts += c_accts; total_held += c_held;
|
||||
printf(" [휴면정지] %s: 계좌 %ld개 정지, 지급 %ld건 보류\n", c_merch, c_accts, c_held);
|
||||
}
|
||||
EXEC SQL CLOSE pds;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_dormantstop_batch COMMIT: %ld일 기준 휴면=%ld곳 계좌=%ld 보류=%ld\n",
|
||||
c_days, stopped, total_accts, total_held);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
6
app/src/py/batch/py_dormantstop_cpy.h
Normal file
6
app/src/py/batch/py_dormantstop_cpy.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* py_dormantstop_cpy.h - py_dormantstop_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long c_days;
|
||||
char c_merch[64];
|
||||
long c_accts, c_held;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
55
app/src/py/batch/py_failstat_batch.pgc
Normal file
55
app/src/py/batch/py_failstat_batch.pgc
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* py_failstat_batch.pgc - py 지급 실패사유 집계 리포트 배치 (GROUP BY 커서, XA).
|
||||
*
|
||||
* 당일 반려(J)건을 사유별 커서로 집계해 사유/건수/금액 표를 출력하고
|
||||
* 최다 사유를 강조 리포트한다 (read-only 분석 배치).
|
||||
* host 변수는 전용 카피북 py_failstat_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_failstat_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 groups = 0, fails = 0, amount = 0;
|
||||
char top_reason[128] = "";
|
||||
long top_cnt = 0;
|
||||
|
||||
EXEC SQL INCLUDE py_failstat_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 pfs CURSOR FOR
|
||||
SELECT coalesce(nullif(reject_reason,''),'(사유미기재)'), count(*), coalesce(sum(net),0)
|
||||
FROM py_payout
|
||||
WHERE biz_date = :c_bizdate AND status = 'J'
|
||||
GROUP BY 1 ORDER BY 2 DESC, 1;
|
||||
EXEC SQL OPEN pfs;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
for (;;) {
|
||||
EXEC SQL FETCH pfs INTO :c_reason, :c_cnt, :c_amt;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pfs; tpabort(0); return 1; }
|
||||
printf(" [실패사유] %-24s %6ld건 %12ld원\n", c_reason, c_cnt, c_amt);
|
||||
if (groups == 0) { strncpy(top_reason, c_reason, sizeof(top_reason)-1); top_cnt = c_cnt; }
|
||||
groups++; fails += c_cnt; amount += c_amt;
|
||||
}
|
||||
EXEC SQL CLOSE pfs;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
if (groups > 0)
|
||||
printf(">>> py_failstat_batch COMMIT: bizdate=%s 사유=%ld종 반려=%ld건/%ld원 최다='%s'(%ld건)\n",
|
||||
bizdate, groups, fails, amount, top_reason, top_cnt);
|
||||
else
|
||||
printf(">>> py_failstat_batch COMMIT: bizdate=%s 반려 없음\n", bizdate);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
6
app/src/py/batch/py_failstat_cpy.h
Normal file
6
app/src/py/batch/py_failstat_cpy.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* py_failstat_cpy.h - py_failstat_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char c_bizdate[16];
|
||||
char c_reason[128];
|
||||
long c_cnt, c_amt;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
50
app/src/py/batch/py_feewaive_batch.pgc
Normal file
50
app/src/py/batch/py_feewaive_batch.pgc
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* py_feewaive_batch.pgc - py 소액 지급 수수료 일괄 면제 배치 (INSERT..SELECT + UPDATE, XA).
|
||||
*
|
||||
* 당일 요청(R)건 중 지급액이 임계 이하인 소액건의 수수료를 전액 면제:
|
||||
* 먼저 INSERT..SELECT 로 py_adjust 증적을 뜨고, 이어서 fee=0/net=amount 로
|
||||
* 일괄 갱신한다 (증적 건수와 갱신 건수 일치 검증 출력).
|
||||
* host 변수는 전용 카피북 py_feewaive_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_feewaive_batch [YYYY-MM-DD] [THRESHOLD]
|
||||
*/
|
||||
#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_feewaive_cpy;
|
||||
|
||||
strncpy(c_bizdate, bizdate, sizeof(c_bizdate)-1); c_bizdate[sizeof(c_bizdate)-1] = 0;
|
||||
c_thr = (argc > 2) ? atol(argv[2]) : 30000; /* 기본 소액 기준 3만원 */
|
||||
|
||||
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 INSERT INTO py_adjust (adj_id, payout_id, delta, reason, biz_date)
|
||||
SELECT nextval('py_adjust_seq'), payout_id, fee, '소액수수료면제', biz_date
|
||||
FROM py_payout
|
||||
WHERE biz_date = :c_bizdate AND status = 'R' AND fee > 0 AND amount <= :c_thr;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "INSERT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
c_logged = sqlca.sqlerrd[2];
|
||||
|
||||
EXEC SQL UPDATE py_payout SET fee = 0, net = amount, updated_at = now()
|
||||
WHERE biz_date = :c_bizdate AND status = 'R' AND fee > 0 AND amount <= :c_thr;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
c_waived = sqlca.sqlerrd[2];
|
||||
|
||||
if (c_logged != c_waived) {
|
||||
fprintf(stderr, "py_feewaive_batch 증적/갱신 불일치 %ld != %ld -> ROLLBACK\n", c_logged, c_waived);
|
||||
tpabort(0); return 1;
|
||||
}
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_feewaive_batch COMMIT: bizdate=%s 기준=%ld원 면제=%ld건(증적 %ld)\n",
|
||||
bizdate, c_thr, c_waived, c_logged);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
5
app/src/py/batch/py_feewaive_cpy.h
Normal file
5
app/src/py/batch/py_feewaive_cpy.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/* py_feewaive_cpy.h - py_feewaive_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char c_bizdate[16];
|
||||
long c_thr, c_logged, c_waived;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
48
app/src/py/batch/py_holdaging_batch.pgc
Normal file
48
app/src/py/batch/py_holdaging_batch.pgc
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* py_holdaging_batch.pgc - py 장기 보류 리포트 배치 (가맹점별 커서, XA).
|
||||
*
|
||||
* 보류(H) 상태로 N일 이상 경과한 건을 가맹점별 커서로 집계해 리포트를
|
||||
* 출력한다 (조치 없는 read-only 점검 배치).
|
||||
* host 변수는 전용 카피북 py_holdaging_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_holdaging_batch [DAYS]
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
long merchants = 0, aged = 0, amount = 0;
|
||||
|
||||
EXEC SQL INCLUDE py_holdaging_cpy;
|
||||
|
||||
c_days = (argc > 1) ? atol(argv[1]) : 3;
|
||||
if (c_days <= 0) c_days = 3;
|
||||
|
||||
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 pha CURSOR FOR
|
||||
SELECT merchant_id, count(*), coalesce(sum(net),0)
|
||||
FROM py_payout
|
||||
WHERE status = 'H' AND updated_at < now() - :c_days * interval '1 day'
|
||||
GROUP BY merchant_id ORDER BY 2 DESC;
|
||||
EXEC SQL OPEN pha;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
for (;;) {
|
||||
EXEC SQL FETCH pha INTO :c_merch, :c_cnt, :c_amt;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pha; tpabort(0); return 1; }
|
||||
printf(" [장기보류] %s: %ld건 %ld원\n", c_merch, c_cnt, c_amt);
|
||||
merchants++; aged += c_cnt; amount += c_amt;
|
||||
}
|
||||
EXEC SQL CLOSE pha;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_holdaging_batch COMMIT: %ld일+ 보류 가맹점=%ld 건수=%ld 금액=%ld\n", c_days, merchants, aged, amount);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
6
app/src/py/batch/py_holdaging_cpy.h
Normal file
6
app/src/py/batch/py_holdaging_cpy.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* py_holdaging_cpy.h - py_holdaging_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long c_days;
|
||||
char c_merch[64];
|
||||
long c_cnt, c_amt;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
53
app/src/py/batch/py_limitguard_batch.pgc
Normal file
53
app/src/py/batch/py_limitguard_batch.pgc
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* py_limitguard_batch.pgc - py 지급한도 초과 자동 보류 배치 (HAVING 커서 + UPDATE, XA).
|
||||
*
|
||||
* 당일 확정성 지급(A/F/P) 합계가 일한도(merchant.daily_limit)를 넘긴 가맹점을
|
||||
* GROUP BY ... HAVING 커서로 뽑아, 초과액과 함께 잔여 요청(R)건을 보류시킨다.
|
||||
* host 변수는 전용 카피북 py_limitguard_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_limitguard_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 over_merchants = 0, held = 0;
|
||||
|
||||
EXEC SQL INCLUDE py_limitguard_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(90, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
|
||||
|
||||
EXEC SQL DECLARE plg CURSOR FOR
|
||||
SELECT m.merchant_id, coalesce(sum(p.net),0) - m.daily_limit
|
||||
FROM py_payout p JOIN merchant m ON p.merchant_id = m.merchant_id
|
||||
WHERE p.biz_date = :c_bizdate AND p.status IN ('A','F','P')
|
||||
GROUP BY m.merchant_id, m.daily_limit
|
||||
HAVING coalesce(sum(p.net),0) > m.daily_limit;
|
||||
EXEC SQL OPEN plg;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
for (;;) {
|
||||
EXEC SQL FETCH plg INTO :c_merch, :c_over;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE plg; tpabort(0); return 1; }
|
||||
over_merchants++;
|
||||
EXEC SQL UPDATE py_payout SET status = 'H', reject_reason = '한도초과', updated_at = now()
|
||||
WHERE merchant_id = :c_merch AND biz_date = :c_bizdate AND status = 'R';
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE plg; tpabort(0); return 1; }
|
||||
held += sqlca.sqlerrd[2];
|
||||
printf(" [한도초과] %s: 초과액=%ld 보류=%ld건\n", c_merch, c_over, sqlca.sqlerrd[2]);
|
||||
}
|
||||
EXEC SQL CLOSE plg;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_limitguard_batch COMMIT: bizdate=%s 초과가맹점=%ld 보류=%ld건\n", bizdate, over_merchants, held);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
6
app/src/py/batch/py_limitguard_cpy.h
Normal file
6
app/src/py/batch/py_limitguard_cpy.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* py_limitguard_cpy.h - py_limitguard_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char c_bizdate[16];
|
||||
char c_merch[64];
|
||||
long c_over, c_limit;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
60
app/src/py/batch/py_mergesmall_batch.pgc
Normal file
60
app/src/py/batch/py_mergesmall_batch.pgc
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* py_mergesmall_batch.pgc - py 분할건 합산 정리 배치 (HAVING 커서 + 재생성, XA).
|
||||
*
|
||||
* 당일 요청(R) SPLIT 건이 2건 이상인 가맹점을 커서로 뽑아 가맹점마다
|
||||
* 원건들을 취소(C) 표시하고 합산 1건(BULK)으로 재생성한다 (이체 건수 절감).
|
||||
* host 변수는 전용 카피북 py_mergesmall_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_mergesmall_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 merchants = 0, merged = 0;
|
||||
|
||||
EXEC SQL INCLUDE py_mergesmall_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 pms CURSOR FOR
|
||||
SELECT merchant_id, count(*), coalesce(sum(amount),0), coalesce(sum(fee),0),
|
||||
coalesce(sum(net),0), coalesce(min(account_no),'')
|
||||
FROM py_payout
|
||||
WHERE biz_date = :c_bizdate AND status = 'R' AND channel = 'SPLIT'
|
||||
GROUP BY merchant_id HAVING count(*) >= 2
|
||||
ORDER BY merchant_id;
|
||||
EXEC SQL OPEN pms;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
for (;;) {
|
||||
EXEC SQL FETCH pms INTO :c_merch, :c_cnt, :c_amt, :c_fee, :c_net, :c_acct;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pms; tpabort(0); return 1; }
|
||||
EXEC SQL UPDATE py_payout SET status = 'C', reject_reason = '분할합산', updated_at = now()
|
||||
WHERE merchant_id = :c_merch AND biz_date = :c_bizdate
|
||||
AND status = 'R' AND channel = 'SPLIT';
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pms; tpabort(0); return 1; }
|
||||
EXEC SQL SELECT nextval('py_payout_seq') INTO :c_newid;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pms; tpabort(0); return 1; }
|
||||
EXEC SQL INSERT INTO py_payout (payout_id, merchant_id, account_no, amount, fee, net,
|
||||
status, channel, biz_date)
|
||||
VALUES (:c_newid, :c_merch, :c_acct, :c_amt, :c_fee, :c_net, 'R', 'BULK', :c_bizdate);
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pms; tpabort(0); return 1; }
|
||||
merchants++; merged += c_cnt;
|
||||
printf(" [분할합산] %s: %ld건 -> BULK id=%ld (net=%ld)\n", c_merch, c_cnt, c_newid, c_net);
|
||||
}
|
||||
EXEC SQL CLOSE pms;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_mergesmall_batch COMMIT: bizdate=%s 가맹점=%ld 통합원건=%ld\n", bizdate, merchants, merged);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
7
app/src/py/batch/py_mergesmall_cpy.h
Normal file
7
app/src/py/batch/py_mergesmall_cpy.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/* py_mergesmall_cpy.h - py_mergesmall_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char c_bizdate[16];
|
||||
char c_merch[64];
|
||||
char c_acct[32];
|
||||
long c_cnt, c_amt, c_fee, c_net, c_newid;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
47
app/src/py/batch/py_notify_batch.pgc
Normal file
47
app/src/py/batch/py_notify_batch.pgc
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* py_notify_batch.pgc - py 지급완료 통지 발송 시뮬 배치 (읽기 커서, XA).
|
||||
*
|
||||
* 당일 지급완료(P) 건을 커서로 훑어 가맹점별 SMS 통지 발송을 stdout 으로
|
||||
* 시뮬레이션한다 (실발송 없음). 발송 건수/총액 리포트.
|
||||
* host 변수는 전용 카피북 py_notify_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_notify_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 sent = 0, total = 0;
|
||||
|
||||
EXEC SQL INCLUDE py_notify_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 pnt CURSOR FOR
|
||||
SELECT payout_id, merchant_id, net FROM py_payout
|
||||
WHERE biz_date = :c_bizdate AND status = 'P'
|
||||
ORDER BY merchant_id, payout_id;
|
||||
EXEC SQL OPEN pnt;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
for (;;) {
|
||||
EXEC SQL FETCH pnt INTO :c_pid, :c_merch, :c_net;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pnt; tpabort(0); return 1; }
|
||||
printf(" [SMS시뮬] %s님 지급 %ld원 완료 (payout=%ld)\n", c_merch, c_net, c_pid);
|
||||
sent++; total += c_net;
|
||||
}
|
||||
EXEC SQL CLOSE pnt;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_notify_batch COMMIT: bizdate=%s 통지=%ld건 총액=%ld\n", bizdate, sent, total);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
6
app/src/py/batch/py_notify_cpy.h
Normal file
6
app/src/py/batch/py_notify_cpy.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* py_notify_cpy.h - py_notify_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char c_bizdate[16];
|
||||
char c_merch[64];
|
||||
long c_pid, c_net;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
39
app/src/py/batch/py_schedexpire_batch.pgc
Normal file
39
app/src/py/batch/py_schedexpire_batch.pgc
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* py_schedexpire_batch.pgc - py 기한경과 지급예약 만료 배치 (DELETE + 잔여 점검, XA).
|
||||
*
|
||||
* 기준일 이전(run_date < 기준일)인데 아직 미실행(S)인 예약을 DELETE 로
|
||||
* 만료시키고, 남은 유효 예약 건수를 함께 리포트한다.
|
||||
* host 변수는 전용 카피북 py_schedexpire_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_schedexpire_batch [YYYY-MM-DD(기준일)]
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *today = (argc > 1) ? argv[1] : "2026-07-19";
|
||||
|
||||
EXEC SQL INCLUDE py_schedexpire_cpy;
|
||||
|
||||
strncpy(c_today, today, sizeof(c_today)-1); c_today[sizeof(c_today)-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 DELETE FROM py_schedule
|
||||
WHERE status = 'S' AND run_date < CAST(:c_today AS date);
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "DELETE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
c_expired = sqlca.sqlerrd[2];
|
||||
|
||||
EXEC SQL SELECT count(*) INTO :c_remain FROM py_schedule WHERE status = 'S';
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "SELECT FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_schedexpire_batch COMMIT: 기준=%s 만료삭제=%ld건 잔여예약=%ld건\n", today, c_expired, c_remain);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
5
app/src/py/batch/py_schedexpire_cpy.h
Normal file
5
app/src/py/batch/py_schedexpire_cpy.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/* py_schedexpire_cpy.h - py_schedexpire_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char c_today[16];
|
||||
long c_expired, c_remain;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
43
app/src/py/batch/py_seizescan_batch.pgc
Normal file
43
app/src/py/batch/py_seizescan_batch.pgc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* py_seizescan_batch.pgc - py 압류계좌 지급 차단 스캔 배치 (UPDATE FROM, XA).
|
||||
*
|
||||
* 압류(SEIZED) 계좌로 향하는 당일 미지급(R/A) 건을 UPDATE ... FROM 조인
|
||||
* 한 방으로 '압류차단' 보류(H) 전이하고, 압류 계좌 수도 함께 리포트.
|
||||
* host 변수는 전용 카피북 py_seizescan_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_seizescan_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 INCLUDE py_seizescan_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 SELECT count(*) INTO :c_accts FROM py_account WHERE status = 'SEIZED';
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "SELECT FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
|
||||
|
||||
EXEC SQL UPDATE py_payout SET status = 'H', reject_reason = '압류차단', updated_at = now()
|
||||
FROM py_account a
|
||||
WHERE py_payout.account_no = a.account_no
|
||||
AND py_payout.biz_date = :c_bizdate
|
||||
AND py_payout.status IN ('R','A')
|
||||
AND a.status = 'SEIZED';
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
c_blocked = sqlca.sqlerrd[2];
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_seizescan_batch COMMIT: bizdate=%s 압류계좌=%ld 차단보류=%ld건\n", bizdate, c_accts, c_blocked);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
5
app/src/py/batch/py_seizescan_cpy.h
Normal file
5
app/src/py/batch/py_seizescan_cpy.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/* py_seizescan_cpy.h - py_seizescan_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char c_bizdate[16];
|
||||
long c_blocked, c_accts;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
52
app/src/py/batch/py_xferrecon_batch.pgc
Normal file
52
app/src/py/batch/py_xferrecon_batch.pgc
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* py_xferrecon_batch.pgc - py 이체결과 대사 보정 배치 (안티조인 커서 + INSERT, XA).
|
||||
*
|
||||
* 당일 지급완료(P)인데 XFER 기표가 없는 건을 NOT EXISTS 커서로 찾아
|
||||
* py_ledger 에 XFERFIX 보정 기표를 채워 원장을 맞춘다.
|
||||
* host 변수는 전용 카피북 py_xferrecon_cpy.h 를 EXEC SQL INCLUDE.
|
||||
* Usage: py_xferrecon_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 fixed = 0, fixed_amt = 0;
|
||||
|
||||
EXEC SQL INCLUDE py_xferrecon_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(90, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
|
||||
|
||||
EXEC SQL DECLARE pxr CURSOR FOR
|
||||
SELECT p.payout_id, p.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 p.payout_id;
|
||||
EXEC SQL OPEN pxr;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
for (;;) {
|
||||
EXEC SQL FETCH pxr INTO :c_pid, :c_net;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pxr; 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, 'XFERFIX', :c_net, :c_bizdate);
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pxr; tpabort(0); return 1; }
|
||||
fixed++; fixed_amt += c_net;
|
||||
printf(" [대사보정] payout=%ld XFERFIX %ld원 기표\n", c_pid, c_net);
|
||||
}
|
||||
EXEC SQL CLOSE pxr;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_xferrecon_batch COMMIT: bizdate=%s 보정기표=%ld건 %ld원\n", bizdate, fixed, fixed_amt);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
5
app/src/py/batch/py_xferrecon_cpy.h
Normal file
5
app/src/py/batch/py_xferrecon_cpy.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/* py_xferrecon_cpy.h - py_xferrecon_batch 전용 카피북 (host 변수 선언). */
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char c_bizdate[16];
|
||||
long c_pid, c_net;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
Loading…
Add table
Add a link
Reference in a new issue