규모확장 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;
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* py_svr.pgc - py (지급) MODULE SERVER (thin dispatcher).
|
||||
*
|
||||
* The legacy "one module server, many services" pattern: this binary owns NO
|
||||
* business logic. Each of py's 30 online services now lives in its own file
|
||||
* business logic. Each of py's 75 online services now lives in its own file
|
||||
* (app/src/py/svc/<SVCNAME>.pgc) with its own copybook header
|
||||
* (app/src/py/svc/<SVCNAME>.h); those objects are archived into libpysvc.a and
|
||||
* linked into this server. Here we only:
|
||||
|
|
@ -49,6 +49,51 @@
|
|||
#include "PAY_REMIT.h"
|
||||
#include "PAY_CLOSE.h"
|
||||
#include "PAY_SUMMARY.h"
|
||||
#include "PAY_BANKFILE.h"
|
||||
#include "PAY_FILEVERIFY.h"
|
||||
#include "PAY_FILEREBUILD.h"
|
||||
#include "PAY_FILESTAT.h"
|
||||
#include "PAY_NOTIFYSMS.h"
|
||||
#include "PAY_NOTIFYEMAIL.h"
|
||||
#include "PAY_NOTIFYPLAN.h"
|
||||
#include "PAY_ACCTCHANGE.h"
|
||||
#include "PAY_REPAYNEW.h"
|
||||
#include "PAY_HOLDREASON.h"
|
||||
#include "PAY_HOLDLIST.h"
|
||||
#include "PAY_HOLDAGING.h"
|
||||
#include "PAY_SEIZE.h"
|
||||
#include "PAY_SEIZEREL.h"
|
||||
#include "PAY_SEIZECHK.h"
|
||||
#include "PAY_URGENT.h"
|
||||
#include "PAY_ADVCALC.h"
|
||||
#include "PAY_ADVREPAY.h"
|
||||
#include "PAY_MERGE.h"
|
||||
#include "PAY_SPLITRATIO.h"
|
||||
#include "PAY_FEERATE.h"
|
||||
#include "PAY_FEEWAIVE.h"
|
||||
#include "PAY_FEEMIN.h"
|
||||
#include "PAY_FAILSTAT.h"
|
||||
#include "PAY_FAILTOP.h"
|
||||
#include "PAY_FAILRATE.h"
|
||||
#include "PAY_XFERMATCH.h"
|
||||
#include "PAY_XFERMISS.h"
|
||||
#include "PAY_REFUND.h"
|
||||
#include "PAY_CANCELHIST.h"
|
||||
#include "PAY_LIMITCHK.h"
|
||||
#include "PAY_LIMITHOLD.h"
|
||||
#include "PAY_DORMANT.h"
|
||||
#include "PAY_DORMSTOP.h"
|
||||
#include "PAY_APPRREQ.h"
|
||||
#include "PAY_APPRBULK.h"
|
||||
#include "PAY_APPRLIMIT.h"
|
||||
#include "PAY_STATCHAN.h"
|
||||
#include "PAY_STATBANK.h"
|
||||
#include "PAY_STATDAILY.h"
|
||||
#include "PAY_STATRANGE.h"
|
||||
#include "PAY_SCHEDCANCEL.h"
|
||||
#include "PAY_LEDGERSUM.h"
|
||||
#include "PAY_ACCTLIST.h"
|
||||
#include "PAY_NETRECALC.h"
|
||||
|
||||
/* ----- advertise table: {service name, function} (fn is extern from its file) */
|
||||
static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = {
|
||||
|
|
@ -67,6 +112,29 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = {
|
|||
{"PAY_HISTORY", PAY_HISTORY}, {"PAY_REVERSE", PAY_REVERSE},
|
||||
{"PAY_CONFIRM", PAY_CONFIRM}, {"PAY_REMIT", PAY_REMIT},
|
||||
{"PAY_CLOSE", PAY_CLOSE}, {"PAY_SUMMARY", PAY_SUMMARY},
|
||||
{"PAY_BANKFILE", PAY_BANKFILE}, {"PAY_FILEVERIFY", PAY_FILEVERIFY},
|
||||
{"PAY_FILEREBUILD", PAY_FILEREBUILD}, {"PAY_FILESTAT", PAY_FILESTAT},
|
||||
{"PAY_NOTIFYSMS", PAY_NOTIFYSMS}, {"PAY_NOTIFYEMAIL", PAY_NOTIFYEMAIL},
|
||||
{"PAY_NOTIFYPLAN", PAY_NOTIFYPLAN}, {"PAY_ACCTCHANGE", PAY_ACCTCHANGE},
|
||||
{"PAY_REPAYNEW", PAY_REPAYNEW}, {"PAY_HOLDREASON", PAY_HOLDREASON},
|
||||
{"PAY_HOLDLIST", PAY_HOLDLIST}, {"PAY_HOLDAGING", PAY_HOLDAGING},
|
||||
{"PAY_SEIZE", PAY_SEIZE}, {"PAY_SEIZEREL", PAY_SEIZEREL},
|
||||
{"PAY_SEIZECHK", PAY_SEIZECHK}, {"PAY_URGENT", PAY_URGENT},
|
||||
{"PAY_ADVCALC", PAY_ADVCALC}, {"PAY_ADVREPAY", PAY_ADVREPAY},
|
||||
{"PAY_MERGE", PAY_MERGE}, {"PAY_SPLITRATIO", PAY_SPLITRATIO},
|
||||
{"PAY_FEERATE", PAY_FEERATE}, {"PAY_FEEWAIVE", PAY_FEEWAIVE},
|
||||
{"PAY_FEEMIN", PAY_FEEMIN}, {"PAY_FAILSTAT", PAY_FAILSTAT},
|
||||
{"PAY_FAILTOP", PAY_FAILTOP}, {"PAY_FAILRATE", PAY_FAILRATE},
|
||||
{"PAY_XFERMATCH", PAY_XFERMATCH}, {"PAY_XFERMISS", PAY_XFERMISS},
|
||||
{"PAY_REFUND", PAY_REFUND}, {"PAY_CANCELHIST", PAY_CANCELHIST},
|
||||
{"PAY_LIMITCHK", PAY_LIMITCHK}, {"PAY_LIMITHOLD", PAY_LIMITHOLD},
|
||||
{"PAY_DORMANT", PAY_DORMANT}, {"PAY_DORMSTOP", PAY_DORMSTOP},
|
||||
{"PAY_APPRREQ", PAY_APPRREQ}, {"PAY_APPRBULK", PAY_APPRBULK},
|
||||
{"PAY_APPRLIMIT", PAY_APPRLIMIT}, {"PAY_STATCHAN", PAY_STATCHAN},
|
||||
{"PAY_STATBANK", PAY_STATBANK}, {"PAY_STATDAILY", PAY_STATDAILY},
|
||||
{"PAY_STATRANGE", PAY_STATRANGE}, {"PAY_SCHEDCANCEL", PAY_SCHEDCANCEL},
|
||||
{"PAY_LEDGERSUM", PAY_LEDGERSUM}, {"PAY_ACCTLIST", PAY_ACCTLIST},
|
||||
{"PAY_NETRECALC", PAY_NETRECALC},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
|
|||
19
app/src/py/svc/PAY_ACCTCHANGE.h
Normal file
19
app/src/py/svc/PAY_ACCTCHANGE.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_ACCTCHANGE.h - py 서비스 PAY_ACCTCHANGE 카피북 (copybook / record header).
|
||||
* Request fields: T_ID1(payout_id), T_KEY1(새 계좌번호)
|
||||
* Response fields: T_RC(0 변경 / 1 상태불가 / 2 소유아님 / 3 비정상계좌), T_MSG
|
||||
*/
|
||||
#ifndef PAY_ACCTCHANGE_H
|
||||
#define PAY_ACCTCHANGE_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long payout_id;
|
||||
char new_account[32];
|
||||
char acct_status[16];
|
||||
} py_acctchange_rec_t;
|
||||
|
||||
void PAY_ACCTCHANGE(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_ACCTCHANGE_H */
|
||||
42
app/src/py/svc/PAY_ACCTCHANGE.pgc
Normal file
42
app/src/py/svc/PAY_ACCTCHANGE.pgc
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* PAY_ACCTCHANGE.pgc - py 모듈 서비스 PAY_ACCTCHANGE (one service = one file).
|
||||
* 지급계좌 변경: 미지급(R/H/J) 건만 대상. 새 계좌가 동일 가맹점의
|
||||
* 정상(ACTIVE) 계좌인지 검증한 뒤 지급건의 입금계좌를 교체한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_ACCTCHANGE.h"
|
||||
|
||||
void PAY_ACCTCHANGE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pid;
|
||||
char h_acct[32], h_st[4], h_merch[64], h_ast[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_pid = getl(b, T_ID1);
|
||||
gets_(b, T_KEY1, h_acct, sizeof(h_acct));
|
||||
if (h_acct[0] == 0) { userlog("PAY_ACCTCHANGE: 새 계좌 누락"); FAIL(b); }
|
||||
|
||||
EXEC SQL SELECT status, merchant_id INTO :h_st, :h_merch
|
||||
FROM py_payout WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
if (h_st[0] != 'R' && h_st[0] != 'H' && h_st[0] != 'J') {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "미지급 상태에서만 계좌 변경 가능", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
EXEC SQL SELECT status INTO :h_ast FROM py_account
|
||||
WHERE account_no = :h_acct AND merchant_id = :h_merch;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (sqlca.sqlcode == 100) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "가맹점 소유 계좌 아님", 0L); FAIL(b); }
|
||||
if (strcmp(h_ast, "ACTIVE") != 0) { setl(b, T_RC, 3); Bchg(b, T_MSG, 0, "정상 상태 계좌 아님", 0L); FAIL(b); }
|
||||
|
||||
EXEC SQL UPDATE py_payout SET account_no = :h_acct, updated_at = now()
|
||||
WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_RC, 0);
|
||||
Bchg(b, T_KEY1, 0, h_acct, 0L);
|
||||
userlog("PAY_ACCTCHANGE payout=%ld new_acct=%s", h_pid, h_acct);
|
||||
OK(b);
|
||||
}
|
||||
21
app/src/py/svc/PAY_ACCTLIST.h
Normal file
21
app/src/py/svc/PAY_ACCTLIST.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* PAY_ACCTLIST.h - py 서비스 PAY_ACCTLIST 카피북 (copybook / record header).
|
||||
* Request fields: T_MERCHANT
|
||||
* Response fields: T_RC(0 있음 / 1 없음), T_COUNT(계좌수), T_ARG1(정상 계좌수),
|
||||
* T_GROSS(잔액 합), occurrence: T_KEY1/T_STR1/T_STR2/T_AMT1
|
||||
*/
|
||||
#ifndef PAY_ACCTLIST_H
|
||||
#define PAY_ACCTLIST_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char merchant_id[64];
|
||||
char account_no[32];
|
||||
char bank_code[8];
|
||||
long balance;
|
||||
} py_acctlist_rec_t;
|
||||
|
||||
void PAY_ACCTLIST(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_ACCTLIST_H */
|
||||
46
app/src/py/svc/PAY_ACCTLIST.pgc
Normal file
46
app/src/py/svc/PAY_ACCTLIST.pgc
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* PAY_ACCTLIST.pgc - py 모듈 서비스 PAY_ACCTLIST (one service = one file).
|
||||
* 가맹점 지급계좌 현황: 가맹점의 모든 지급계좌를 커서로 훑어 계좌/은행/
|
||||
* 상태/잔액을 occurrence 로 회신하고 정상 계좌수와 잔액 합계를 산출한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_ACCTLIST.h"
|
||||
|
||||
void PAY_ACCTLIST(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
int i = 0;
|
||||
long active = 0, bal_total = 0;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_acct[32], h_bank[8], h_ast[16];
|
||||
long h_bal;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
|
||||
EXEC SQL DECLARE cur_acctlist CURSOR FOR
|
||||
SELECT account_no, bank_code, status, balance
|
||||
FROM py_account WHERE merchant_id = :h_merch
|
||||
ORDER BY account_no;
|
||||
EXEC SQL OPEN cur_acctlist;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
for (;;) {
|
||||
EXEC SQL FETCH cur_acctlist INTO :h_acct, :h_bank, :h_ast, :h_bal;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_acctlist; FAIL(b); }
|
||||
if (strcmp(h_ast, "ACTIVE") == 0) active++;
|
||||
bal_total += h_bal;
|
||||
if (i < 10) {
|
||||
Bchg(b, T_KEY1, i, h_acct, 0L);
|
||||
Bchg(b, T_STR1, i, h_bank, 0L);
|
||||
Bchg(b, T_STR2, i, h_ast, 0L);
|
||||
Bchg(b, T_AMT1, i, (char *)&h_bal, 0L);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
EXEC SQL CLOSE cur_acctlist;
|
||||
setl(b, T_COUNT, (long)i);
|
||||
setl(b, T_ARG1, active);
|
||||
setl(b, T_GROSS, bal_total);
|
||||
setl(b, T_RC, i > 0 ? 0 : 1);
|
||||
OK(b);
|
||||
}
|
||||
20
app/src/py/svc/PAY_ADVCALC.h
Normal file
20
app/src/py/svc/PAY_ADVCALC.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* PAY_ADVCALC.h - py 서비스 PAY_ADVCALC 카피북 (copybook / record header).
|
||||
* Request fields: T_MERCHANT, T_ARG1(지급률 bps, 기본 8000)
|
||||
* Response fields: T_GROSS(미지급 잔액), T_AMT1(선지급 가능액), T_AMT2(선지급 잔액)
|
||||
*/
|
||||
#ifndef PAY_ADVCALC_H
|
||||
#define PAY_ADVCALC_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char merchant_id[64];
|
||||
long pending_net;
|
||||
long advance_out;
|
||||
long available;
|
||||
} py_advcalc_rec_t;
|
||||
|
||||
void PAY_ADVCALC(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_ADVCALC_H */
|
||||
39
app/src/py/svc/PAY_ADVCALC.pgc
Normal file
39
app/src/py/svc/PAY_ADVCALC.pgc
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* PAY_ADVCALC.pgc - py 모듈 서비스 PAY_ADVCALC (one service = one file).
|
||||
* 선지급 가능액 산정: 가맹점 미지급(R/A) 잔액에 지급률(bps)을 곱한 한도에서
|
||||
* 이미 나간 선지급(ADVANCE, 미상환) 잔액을 차감해 추가 가능액을 회신한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_ADVCALC.h"
|
||||
|
||||
void PAY_ADVCALC(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long bps, avail;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64];
|
||||
long h_pend, h_out;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
|
||||
bps = getl(b, T_ARG1);
|
||||
if (bps <= 0 || bps > 10000) bps = 8000; /* 기본 지급률 80% */
|
||||
|
||||
EXEC SQL SELECT coalesce(sum(net),0) INTO :h_pend
|
||||
FROM py_payout WHERE merchant_id = :h_merch AND status IN ('R','A');
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
EXEC SQL SELECT coalesce(sum(net),0) INTO :h_out
|
||||
FROM py_payout
|
||||
WHERE merchant_id = :h_merch AND channel = 'ADVANCE'
|
||||
AND status NOT IN ('C','V','X');
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
avail = h_pend * bps / 10000 - h_out;
|
||||
if (avail < 0) avail = 0;
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_GROSS, h_pend);
|
||||
setl(b, T_AMT1, avail);
|
||||
setl(b, T_AMT2, h_out);
|
||||
setl(b, T_ARG1, bps);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_ADVREPAY.h
Normal file
19
app/src/py/svc/PAY_ADVREPAY.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_ADVREPAY.h - py 서비스 PAY_ADVREPAY 카피북 (copybook / record header).
|
||||
* Request fields: T_ID1(ADVANCE payout_id), T_AMOUNT(상환액), T_BIZDATE
|
||||
* Response fields: T_RC(0 상환 / 1 선지급 아님), T_AMOUNT(실상환액), T_NET(잔여)
|
||||
*/
|
||||
#ifndef PAY_ADVREPAY_H
|
||||
#define PAY_ADVREPAY_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long payout_id;
|
||||
long repay_amount;
|
||||
long remain_net;
|
||||
} py_advrepay_rec_t;
|
||||
|
||||
void PAY_ADVREPAY(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_ADVREPAY_H */
|
||||
47
app/src/py/svc/PAY_ADVREPAY.pgc
Normal file
47
app/src/py/svc/PAY_ADVREPAY.pgc
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* PAY_ADVREPAY.pgc - py 모듈 서비스 PAY_ADVREPAY (one service = one file).
|
||||
* 선지급 상환: ADVANCE 채널 지급건의 잔여 net 에서 상환액을 차감하고
|
||||
* py_adjust 에 음수 조정으로 남긴다. 완납되면 마감(X) 전이.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_ADVREPAY.h"
|
||||
|
||||
void PAY_ADVREPAY(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char bizdate[16];
|
||||
long repay;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pid, h_net, h_new;
|
||||
char h_chan[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_pid = getl(b, T_ID1);
|
||||
repay = getl(b, T_AMOUNT);
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
|
||||
EXEC SQL SELECT channel, net INTO :h_chan, :h_net
|
||||
FROM py_payout WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
if (strcmp(h_chan, "ADVANCE") != 0) {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "선지급 건만 상환 가능", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
if (repay <= 0 || repay > h_net) repay = h_net; /* 초과 상환은 잔액 전액으로 절사 */
|
||||
if (pydb_insert_adjust(h_pid, -repay, "선지급상환", bizdate) < 0) FAIL(b);
|
||||
h_new = h_net - repay;
|
||||
EXEC SQL UPDATE py_payout
|
||||
SET net = :h_new,
|
||||
status = CASE WHEN :h_new <= 0 THEN 'X' ELSE status END,
|
||||
updated_at = now()
|
||||
WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_AMOUNT, repay);
|
||||
setl(b, T_NET, h_new);
|
||||
Bchg(b, T_STATUS, 0, h_new <= 0 ? "X" : "A", 0L);
|
||||
userlog("PAY_ADVREPAY id=%ld repay=%ld remain=%ld", h_pid, repay, h_new);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_APPRBULK.h
Normal file
19
app/src/py/svc/PAY_APPRBULK.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_APPRBULK.h - py 서비스 PAY_APPRBULK 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE, T_AMT1(자동승인 한도, 기본 500000)
|
||||
* Response fields: T_COUNT(승인 건수), T_ARG1(잔여 대기), T_AMT1(적용 한도)
|
||||
*/
|
||||
#ifndef PAY_APPRBULK_H
|
||||
#define PAY_APPRBULK_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char biz_date[16];
|
||||
long approve_cap;
|
||||
long approved_count;
|
||||
} py_apprbulk_rec_t;
|
||||
|
||||
void PAY_APPRBULK(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_APPRBULK_H */
|
||||
35
app/src/py/svc/PAY_APPRBULK.pgc
Normal file
35
app/src/py/svc/PAY_APPRBULK.pgc
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* PAY_APPRBULK.pgc - py 모듈 서비스 PAY_APPRBULK (one service = one file).
|
||||
* 소액 일괄 승인: 당일 요청(R)건 중 net 이 승인한도 이하인 건을 한 번의
|
||||
* UPDATE 로 승인(A)하고, 남은 고액 대기 건수를 함께 회신한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_APPRBULK.h"
|
||||
|
||||
void PAY_APPRBULK(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16];
|
||||
long h_cap, h_appr, h_remain;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
h_cap = getl(b, T_AMT1);
|
||||
if (h_cap <= 0) h_cap = 500000; /* 기본 자동승인 한도 50만원 */
|
||||
|
||||
EXEC SQL UPDATE py_payout SET status = 'A', updated_at = now()
|
||||
WHERE biz_date = :h_bizdate AND status = 'R' AND net <= :h_cap;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
h_appr = sqlca.sqlerrd[2];
|
||||
EXEC SQL SELECT count(*) INTO :h_remain
|
||||
FROM py_payout WHERE biz_date = :h_bizdate AND status = 'R';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_COUNT, h_appr);
|
||||
setl(b, T_ARG1, h_remain);
|
||||
setl(b, T_AMT1, h_cap);
|
||||
userlog("PAY_APPRBULK date=%s cap=%ld approved=%ld remain=%ld", h_bizdate, h_cap, h_appr, h_remain);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_APPRLIMIT.h
Normal file
19
app/src/py/svc/PAY_APPRLIMIT.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_APPRLIMIT.h - py 서비스 PAY_APPRLIMIT 카피북 (copybook / record header).
|
||||
* Request fields: T_ID1(payout_id), T_KEY2(승인자 ID), T_AMT1(고액 기준, 기본 1천만)
|
||||
* Response fields: T_RC(0 승인 / 1 상태불가 / 2 승인자 필요), T_NET, T_STATUS('A')
|
||||
*/
|
||||
#ifndef PAY_APPRLIMIT_H
|
||||
#define PAY_APPRLIMIT_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long payout_id;
|
||||
char approver[32];
|
||||
long high_cap;
|
||||
} py_apprlimit_rec_t;
|
||||
|
||||
void PAY_APPRLIMIT(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_APPRLIMIT_H */
|
||||
48
app/src/py/svc/PAY_APPRLIMIT.pgc
Normal file
48
app/src/py/svc/PAY_APPRLIMIT.pgc
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* PAY_APPRLIMIT.pgc - py 모듈 서비스 PAY_APPRLIMIT (one service = one file).
|
||||
* 고액 승인 통제: 한도 초과 건은 승인자 지정 없이는 거절(2). 승인 시
|
||||
* py_ledger 에 APPROVE 기표를 남겨 누가/얼마를 승인했는지 증적화한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_APPRLIMIT.h"
|
||||
|
||||
void PAY_APPRLIMIT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char approver[32];
|
||||
long cap;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pid, h_net;
|
||||
char h_st[4], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_pid = getl(b, T_ID1);
|
||||
gets_(b, T_KEY2, approver, sizeof(approver));
|
||||
cap = getl(b, T_AMT1);
|
||||
if (cap <= 0) cap = 10000000; /* 기본 고액 기준 1천만원 */
|
||||
|
||||
EXEC SQL SELECT net, status, CAST(biz_date AS text) INTO :h_net, :h_st, :h_bizdate
|
||||
FROM py_payout WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
if (h_st[0] != 'R') {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "요청 상태 건만 승인 가능", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
if (h_net > cap && approver[0] == 0) {
|
||||
setl(b, T_RC, 2);
|
||||
Bchg(b, T_MSG, 0, "고액 지급은 승인자 지정 필수", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
EXEC SQL UPDATE py_payout SET status = 'A', updated_at = now() WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
EXEC SQL INSERT INTO py_ledger (ledger_id, payout_id, entry_type, amount, biz_date)
|
||||
VALUES (nextval('py_ledger_seq'), :h_pid, 'APPROVE', :h_net, :h_bizdate);
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_NET, h_net);
|
||||
Bchg(b, T_STATUS, 0, "A", 0L);
|
||||
userlog("PAY_APPRLIMIT id=%ld net=%ld approver=%s", h_pid, h_net, approver[0] ? approver : "-");
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_APPRREQ.h
Normal file
19
app/src/py/svc/PAY_APPRREQ.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_APPRREQ.h - py 서비스 PAY_APPRREQ 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE, T_ARG1(조회 건수, 기본 5, 최대 10)
|
||||
* Response fields: T_COUNT, occurrence: T_ID2[i](payout_id), T_STR1[i](가맹점), T_AMT1[i](net)
|
||||
*/
|
||||
#ifndef PAY_APPRREQ_H
|
||||
#define PAY_APPRREQ_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char biz_date[16];
|
||||
long queue_limit;
|
||||
long queued_count;
|
||||
} py_apprreq_rec_t;
|
||||
|
||||
void PAY_APPRREQ(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_APPRREQ_H */
|
||||
42
app/src/py/svc/PAY_APPRREQ.pgc
Normal file
42
app/src/py/svc/PAY_APPRREQ.pgc
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* PAY_APPRREQ.pgc - py 모듈 서비스 PAY_APPRREQ (one service = one file).
|
||||
* 승인 대기 큐 조회: 당일 요청(R)건을 금액 내림차순 상위 N건 커서로 훑어
|
||||
* occurrence(T_ID2[i]/T_STR1[i]/T_AMT1[i]) 로 회신한다 (승인 워크플로 1단계).
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_APPRREQ.h"
|
||||
|
||||
void PAY_APPRREQ(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
int i = 0;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16], h_merch[64];
|
||||
long h_lim, h_pid, h_net;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
h_lim = getl(b, T_ARG1);
|
||||
if (h_lim <= 0 || h_lim > 10) h_lim = 5;
|
||||
|
||||
EXEC SQL DECLARE cur_apprreq CURSOR FOR
|
||||
SELECT payout_id, merchant_id, net FROM py_payout
|
||||
WHERE biz_date = :h_bizdate AND status = 'R'
|
||||
ORDER BY net DESC, payout_id
|
||||
LIMIT :h_lim;
|
||||
EXEC SQL OPEN cur_apprreq;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
for (;;) {
|
||||
EXEC SQL FETCH cur_apprreq INTO :h_pid, :h_merch, :h_net;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_apprreq; FAIL(b); }
|
||||
Bchg(b, T_ID2, i, (char *)&h_pid, 0L);
|
||||
Bchg(b, T_STR1, i, h_merch, 0L);
|
||||
Bchg(b, T_AMT1, i, (char *)&h_net, 0L);
|
||||
i++;
|
||||
}
|
||||
EXEC SQL CLOSE cur_apprreq;
|
||||
setl(b, T_COUNT, (long)i);
|
||||
setl(b, T_RC, 0);
|
||||
OK(b);
|
||||
}
|
||||
20
app/src/py/svc/PAY_BANKFILE.h
Normal file
20
app/src/py/svc/PAY_BANKFILE.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* PAY_BANKFILE.h - py 서비스 PAY_BANKFILE 카피북 (copybook / record header).
|
||||
* Request fields: T_KEY1(bank_code), T_BIZDATE
|
||||
* Response fields: T_RC(0 ok / 1 대상없음), T_ID2(file_id), T_COUNT, T_GROSS, T_STR1(파일명)
|
||||
*/
|
||||
#ifndef PAY_BANKFILE_H
|
||||
#define PAY_BANKFILE_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char bank_code[8];
|
||||
long file_id;
|
||||
long record_count;
|
||||
long total_amount;
|
||||
} py_bankfile_rec_t;
|
||||
|
||||
void PAY_BANKFILE(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_BANKFILE_H */
|
||||
48
app/src/py/svc/PAY_BANKFILE.pgc
Normal file
48
app/src/py/svc/PAY_BANKFILE.pgc
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* PAY_BANKFILE.pgc - py 모듈 서비스 PAY_BANKFILE (one service = one file).
|
||||
* 은행별 지급파일 생성: 지정 은행코드의 승인(A) 지급건만 py_account 조인으로
|
||||
* 골라 집계 -> py_file 생성 -> 대상 건을 파일편성(F) + file_id 로 일괄 전이.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_BANKFILE.h"
|
||||
|
||||
void PAY_BANKFILE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char fname[64];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_fid, h_cnt, h_sum;
|
||||
char h_bank[8], h_bizdate[16], h_fname[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_KEY1, h_bank, sizeof(h_bank));
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
if (h_bank[0] == 0) { userlog("PAY_BANKFILE: 은행코드 누락"); FAIL(b); }
|
||||
|
||||
EXEC SQL SELECT count(*), coalesce(sum(p.net),0) INTO :h_cnt, :h_sum
|
||||
FROM py_payout p JOIN py_account a ON p.account_no = a.account_no
|
||||
WHERE p.biz_date = :h_bizdate AND p.status = 'A' AND a.bank_code = :h_bank;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (h_cnt == 0) { setl(b, T_RC, 1); setl(b, T_COUNT, 0); OK(b); }
|
||||
|
||||
snprintf(fname, sizeof(fname), "PAYOUT_%s_%s.dat", h_bank, h_bizdate);
|
||||
strncpy(h_fname, fname, sizeof(h_fname)-1); h_fname[sizeof(h_fname)-1] = 0;
|
||||
EXEC SQL SELECT nextval('py_file_seq') INTO :h_fid;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
EXEC SQL INSERT INTO py_file (file_id, biz_date, file_name, record_count, total_amount, status)
|
||||
VALUES (:h_fid, :h_bizdate, :h_fname, :h_cnt, :h_sum, 'G');
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
EXEC SQL UPDATE py_payout SET status = 'F', file_id = :h_fid, updated_at = now()
|
||||
FROM py_account a
|
||||
WHERE py_payout.account_no = a.account_no AND py_payout.biz_date = :h_bizdate
|
||||
AND py_payout.status = 'A' AND a.bank_code = :h_bank;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_ID2, h_fid);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_sum);
|
||||
Bchg(b, T_STR1, 0, fname, 0L);
|
||||
userlog("PAY_BANKFILE bank=%s file_id=%ld recs=%ld amt=%ld", h_bank, h_fid, h_cnt, h_sum);
|
||||
OK(b);
|
||||
}
|
||||
21
app/src/py/svc/PAY_CANCELHIST.h
Normal file
21
app/src/py/svc/PAY_CANCELHIST.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* PAY_CANCELHIST.h - py 서비스 PAY_CANCELHIST 카피북 (copybook / record header).
|
||||
* Request fields: T_MERCHANT, T_BIZDATE
|
||||
* Response fields: T_ARG1/T_AMT1(취소 C 건수/금액), T_ARG2/T_AMT2(환입 V 건수/금액), T_COUNT
|
||||
*/
|
||||
#ifndef PAY_CANCELHIST_H
|
||||
#define PAY_CANCELHIST_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char merchant_id[64];
|
||||
long cancel_count;
|
||||
long cancel_amount;
|
||||
long refund_count;
|
||||
long refund_amount;
|
||||
} py_cancelhist_rec_t;
|
||||
|
||||
void PAY_CANCELHIST(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_CANCELHIST_H */
|
||||
35
app/src/py/svc/PAY_CANCELHIST.pgc
Normal file
35
app/src/py/svc/PAY_CANCELHIST.pgc
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* PAY_CANCELHIST.pgc - py 모듈 서비스 PAY_CANCELHIST (one service = one file).
|
||||
* 취소/환입 이력 집계: 가맹점 당일 취소(C)와 역분개/환입(V)을 각각
|
||||
* 건수/금액으로 나눠 한 번의 CASE 집계로 회신한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_CANCELHIST.h"
|
||||
|
||||
void PAY_CANCELHIST(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_bizdate[16];
|
||||
long h_ccnt, h_camt, h_vcnt, h_vamt;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL SELECT coalesce(sum(CASE WHEN status = 'C' THEN 1 ELSE 0 END),0),
|
||||
coalesce(sum(CASE WHEN status = 'C' THEN net ELSE 0 END),0),
|
||||
coalesce(sum(CASE WHEN status = 'V' THEN 1 ELSE 0 END),0),
|
||||
coalesce(sum(CASE WHEN status = 'V' THEN net ELSE 0 END),0)
|
||||
INTO :h_ccnt, :h_camt, :h_vcnt, :h_vamt
|
||||
FROM py_payout
|
||||
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_ARG1, h_ccnt);
|
||||
setl(b, T_AMT1, h_camt);
|
||||
setl(b, T_ARG2, h_vcnt);
|
||||
setl(b, T_AMT2, h_vamt);
|
||||
setl(b, T_COUNT, h_ccnt + h_vcnt);
|
||||
setl(b, T_RC, 0);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_DORMANT.h
Normal file
19
app/src/py/svc/PAY_DORMANT.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_DORMANT.h - py 서비스 PAY_DORMANT 카피북 (copybook / record header).
|
||||
* Request fields: T_ARG1(무거래 일수, 기본 30)
|
||||
* Response fields: T_RC(0 없음 / 1 휴면 존재), T_COUNT(휴면 수), T_MERCHANT(대표 1곳)
|
||||
*/
|
||||
#ifndef PAY_DORMANT_H
|
||||
#define PAY_DORMANT_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long dormant_days;
|
||||
long dormant_count;
|
||||
char first_merchant[64];
|
||||
} py_dormant_rec_t;
|
||||
|
||||
void PAY_DORMANT(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_DORMANT_H */
|
||||
33
app/src/py/svc/PAY_DORMANT.pgc
Normal file
33
app/src/py/svc/PAY_DORMANT.pgc
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* PAY_DORMANT.pgc - py 모듈 서비스 PAY_DORMANT (one service = one file).
|
||||
* 휴면 가맹점 검출: 최근 N일간 지급 이력이 전혀 없는 정상(ACTIVE) 가맹점
|
||||
* 수를 NOT EXISTS 로 세고 대표 1곳(가장 빠른 ID)을 함께 회신한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_DORMANT.h"
|
||||
|
||||
void PAY_DORMANT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_days, h_cnt;
|
||||
char h_first[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_days = getl(b, T_ARG1);
|
||||
if (h_days <= 0 || h_days > 730) h_days = 30;
|
||||
EXEC SQL SELECT count(*), coalesce(min(m.merchant_id),'')
|
||||
INTO :h_cnt, :h_first
|
||||
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() - :h_days * interval '1 day');
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, h_cnt > 0 ? 1 : 0);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_ARG1, h_days);
|
||||
Bchg(b, T_MERCHANT, 0, h_first, 0L);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_DORMSTOP.h
Normal file
19
app/src/py/svc/PAY_DORMSTOP.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_DORMSTOP.h - py 서비스 PAY_DORMSTOP 카피북 (copybook / record header).
|
||||
* Request fields: T_MERCHANT
|
||||
* Response fields: T_RC(0 정지 / 1 대상없음), T_ARG1(정지 계좌수), T_COUNT(보류 건수)
|
||||
*/
|
||||
#ifndef PAY_DORMSTOP_H
|
||||
#define PAY_DORMSTOP_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char merchant_id[64];
|
||||
long stopped_accounts;
|
||||
long held_payouts;
|
||||
} py_dormstop_rec_t;
|
||||
|
||||
void PAY_DORMSTOP(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_DORMSTOP_H */
|
||||
39
app/src/py/svc/PAY_DORMSTOP.pgc
Normal file
39
app/src/py/svc/PAY_DORMSTOP.pgc
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* PAY_DORMSTOP.pgc - py 모듈 서비스 PAY_DORMSTOP (one service = one file).
|
||||
* 휴면 가맹점 지급정지: 대상 가맹점의 정상 계좌를 DORMANT 로 내리고
|
||||
* 잔여 요청(R) 지급건을 '휴면정지' 사유로 보류(H)한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_DORMSTOP.h"
|
||||
|
||||
void PAY_DORMSTOP(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64];
|
||||
long h_accts, h_held;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
|
||||
if (h_merch[0] == 0) { userlog("PAY_DORMSTOP: 가맹점 누락"); FAIL(b); }
|
||||
|
||||
EXEC SQL UPDATE py_account SET status = 'DORMANT', updated_at = now()
|
||||
WHERE merchant_id = :h_merch AND status = 'ACTIVE';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
h_accts = sqlca.sqlerrd[2];
|
||||
EXEC SQL UPDATE py_payout SET status = 'H', reject_reason = '휴면정지', updated_at = now()
|
||||
WHERE merchant_id = :h_merch AND status = 'R';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
h_held = sqlca.sqlerrd[2];
|
||||
|
||||
if (h_accts == 0 && h_held == 0) {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "정지할 계좌/지급건 없음", 0L);
|
||||
OK(b);
|
||||
}
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_ARG1, h_accts);
|
||||
setl(b, T_COUNT, h_held);
|
||||
userlog("PAY_DORMSTOP merch=%s accts=%ld held=%ld", h_merch, h_accts, h_held);
|
||||
OK(b);
|
||||
}
|
||||
20
app/src/py/svc/PAY_FAILRATE.h
Normal file
20
app/src/py/svc/PAY_FAILRATE.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* PAY_FAILRATE.h - py 서비스 PAY_FAILRATE 카피북 (copybook / record header).
|
||||
* Request fields: T_MERCHANT, T_BIZDATE
|
||||
* Response fields: T_RC(0 산출 / 1 대상없음), T_COUNT(전체), T_ARG1(반려), T_ARG2(실패율 bps)
|
||||
*/
|
||||
#ifndef PAY_FAILRATE_H
|
||||
#define PAY_FAILRATE_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char merchant_id[64];
|
||||
long total_count;
|
||||
long fail_count;
|
||||
long fail_rate_bps;
|
||||
} py_failrate_rec_t;
|
||||
|
||||
void PAY_FAILRATE(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_FAILRATE_H */
|
||||
35
app/src/py/svc/PAY_FAILRATE.pgc
Normal file
35
app/src/py/svc/PAY_FAILRATE.pgc
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* PAY_FAILRATE.pgc - py 모듈 서비스 PAY_FAILRATE (one service = one file).
|
||||
* 지급 실패율 산출: 가맹점 당일 전체 지급건 대비 반려(J) 비중을 bps 로
|
||||
* 환산해 회신한다 (CASE 집계 1회). 대상 없으면 1.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_FAILRATE.h"
|
||||
|
||||
void PAY_FAILRATE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long rate;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_bizdate[16];
|
||||
long h_tot, h_fail;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL SELECT count(*), coalesce(sum(CASE WHEN status = 'J' THEN 1 ELSE 0 END),0)
|
||||
INTO :h_tot, :h_fail
|
||||
FROM py_payout WHERE merchant_id = :h_merch AND biz_date = :h_bizdate;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (h_tot == 0) {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "산출 대상 지급건 없음", 0L);
|
||||
OK(b);
|
||||
}
|
||||
rate = h_fail * 10000 / h_tot;
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_COUNT, h_tot);
|
||||
setl(b, T_ARG1, h_fail);
|
||||
setl(b, T_ARG2, rate);
|
||||
OK(b);
|
||||
}
|
||||
21
app/src/py/svc/PAY_FAILSTAT.h
Normal file
21
app/src/py/svc/PAY_FAILSTAT.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* PAY_FAILSTAT.h - py 서비스 PAY_FAILSTAT 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE
|
||||
* Response fields: T_COUNT(총 반려), T_GROSS(총액), T_ARG2(사유 그룹수),
|
||||
* occurrence: T_STR1[i](사유), T_ARG1[i](건수), T_AMT1[i](금액)
|
||||
*/
|
||||
#ifndef PAY_FAILSTAT_H
|
||||
#define PAY_FAILSTAT_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char biz_date[16];
|
||||
char fail_reason[128];
|
||||
long fail_count;
|
||||
long fail_amount;
|
||||
} py_failstat_rec_t;
|
||||
|
||||
void PAY_FAILSTAT(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_FAILSTAT_H */
|
||||
46
app/src/py/svc/PAY_FAILSTAT.pgc
Normal file
46
app/src/py/svc/PAY_FAILSTAT.pgc
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* PAY_FAILSTAT.pgc - py 모듈 서비스 PAY_FAILSTAT (one service = one file).
|
||||
* 실패사유별 통계: 당일 반려(J)건을 사유별 커서로 집계해 상위 8개 사유를
|
||||
* occurrence 로, 총 반려건/총액을 스칼라로 회신한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_FAILSTAT.h"
|
||||
|
||||
void PAY_FAILSTAT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
int i = 0;
|
||||
long tot_cnt = 0, tot_amt = 0;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16], h_reason[128];
|
||||
long h_cnt, h_amt;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL DECLARE cur_failstat CURSOR FOR
|
||||
SELECT coalesce(nullif(reject_reason,''),'(사유미기재)'), count(*), coalesce(sum(net),0)
|
||||
FROM py_payout
|
||||
WHERE biz_date = :h_bizdate AND status = 'J'
|
||||
GROUP BY 1 ORDER BY 2 DESC, 3 DESC;
|
||||
EXEC SQL OPEN cur_failstat;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
for (;;) {
|
||||
EXEC SQL FETCH cur_failstat INTO :h_reason, :h_cnt, :h_amt;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_failstat; FAIL(b); }
|
||||
tot_cnt += h_cnt;
|
||||
tot_amt += h_amt;
|
||||
if (i < 8) {
|
||||
Bchg(b, T_STR1, i, h_reason, 0L);
|
||||
Bchg(b, T_ARG1, i, (char *)&h_cnt, 0L);
|
||||
Bchg(b, T_AMT1, i, (char *)&h_amt, 0L);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
EXEC SQL CLOSE cur_failstat;
|
||||
setl(b, T_COUNT, tot_cnt);
|
||||
setl(b, T_GROSS, tot_amt);
|
||||
setl(b, T_ARG2, (long)i);
|
||||
setl(b, T_RC, 0);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_FAILTOP.h
Normal file
19
app/src/py/svc/PAY_FAILTOP.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_FAILTOP.h - py 서비스 PAY_FAILTOP 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE
|
||||
* Response fields: T_RC(0 있음 / 1 없음), T_MERCHANT(최다 실패 가맹점), T_COUNT, T_GROSS
|
||||
*/
|
||||
#ifndef PAY_FAILTOP_H
|
||||
#define PAY_FAILTOP_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char biz_date[16];
|
||||
char top_merchant[64];
|
||||
long fail_count;
|
||||
} py_failtop_rec_t;
|
||||
|
||||
void PAY_FAILTOP(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_FAILTOP_H */
|
||||
37
app/src/py/svc/PAY_FAILTOP.pgc
Normal file
37
app/src/py/svc/PAY_FAILTOP.pgc
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* PAY_FAILTOP.pgc - py 모듈 서비스 PAY_FAILTOP (one service = one file).
|
||||
* 최다 실패 가맹점: 당일 반려(J)건이 가장 많은 가맹점 1곳을 ORDER BY +
|
||||
* LIMIT 1 로 뽑아 가맹점/건수/금액을 회신한다. 반려 없으면 1.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_FAILTOP.h"
|
||||
|
||||
void PAY_FAILTOP(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16], h_merch[64];
|
||||
long h_cnt, h_amt;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL SELECT merchant_id, count(*), coalesce(sum(net),0)
|
||||
INTO :h_merch, :h_cnt, :h_amt
|
||||
FROM py_payout
|
||||
WHERE biz_date = :h_bizdate AND status = 'J'
|
||||
GROUP BY merchant_id
|
||||
ORDER BY count(*) DESC, merchant_id
|
||||
LIMIT 1;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (sqlca.sqlcode == 100) {
|
||||
setl(b, T_RC, 1);
|
||||
setl(b, T_COUNT, 0);
|
||||
Bchg(b, T_MSG, 0, "당일 반려 건 없음", 0L);
|
||||
OK(b);
|
||||
}
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_amt);
|
||||
Bchg(b, T_MERCHANT, 0, h_merch, 0L);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_FEEMIN.h
Normal file
19
app/src/py/svc/PAY_FEEMIN.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_FEEMIN.h - py 서비스 PAY_FEEMIN 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE, T_AMT1(최소수수료, 기본 100)
|
||||
* Response fields: T_RC(0), T_COUNT(보정 건수), T_AMT1(적용 최소수수료)
|
||||
*/
|
||||
#ifndef PAY_FEEMIN_H
|
||||
#define PAY_FEEMIN_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char biz_date[16];
|
||||
long min_fee;
|
||||
long fixed_count;
|
||||
} py_feemin_rec_t;
|
||||
|
||||
void PAY_FEEMIN(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_FEEMIN_H */
|
||||
33
app/src/py/svc/PAY_FEEMIN.pgc
Normal file
33
app/src/py/svc/PAY_FEEMIN.pgc
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* PAY_FEEMIN.pgc - py 모듈 서비스 PAY_FEEMIN (one service = one file).
|
||||
* 최소수수료 보정: 당일 요청(R) 건 중 수수료가 최소수수료 미만인 건을
|
||||
* 한 번의 가드된 UPDATE 로 최소수수료로 끌어올리고 net 을 재계산한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_FEEMIN.h"
|
||||
|
||||
void PAY_FEEMIN(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16];
|
||||
long h_min, h_aff;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
h_min = getl(b, T_AMT1);
|
||||
if (h_min <= 0) h_min = 100; /* 기본 최소수수료 100원 */
|
||||
|
||||
EXEC SQL UPDATE py_payout
|
||||
SET fee = :h_min, net = amount - :h_min, updated_at = now()
|
||||
WHERE biz_date = :h_bizdate AND status = 'R'
|
||||
AND fee < :h_min AND amount > :h_min;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
h_aff = sqlca.sqlerrd[2];
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_COUNT, h_aff);
|
||||
setl(b, T_AMT1, h_min);
|
||||
userlog("PAY_FEEMIN date=%s min=%ld fixed=%ld", h_bizdate, h_min, h_aff);
|
||||
OK(b);
|
||||
}
|
||||
20
app/src/py/svc/PAY_FEERATE.h
Normal file
20
app/src/py/svc/PAY_FEERATE.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* PAY_FEERATE.h - py 서비스 PAY_FEERATE 카피북 (copybook / record header).
|
||||
* Request fields: T_ID1(payout_id)
|
||||
* Response fields: T_RC(0 재적용 / 1 상태불가), T_FEE, T_NET, T_ARG1(적용 bps)
|
||||
*/
|
||||
#ifndef PAY_FEERATE_H
|
||||
#define PAY_FEERATE_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long payout_id;
|
||||
long mdr_bps;
|
||||
long new_fee;
|
||||
long new_net;
|
||||
} py_feerate_rec_t;
|
||||
|
||||
void PAY_FEERATE(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_FEERATE_H */
|
||||
38
app/src/py/svc/PAY_FEERATE.pgc
Normal file
38
app/src/py/svc/PAY_FEERATE.pgc
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* PAY_FEERATE.pgc - py 모듈 서비스 PAY_FEERATE (one service = one file).
|
||||
* 수수료율 재적용: 가맹점 계약 요율(merchant.mdr_bps)을 조인으로 가져와
|
||||
* 지급 수수료/순액을 재계산해 미지급(R/A) 건에 반영한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_FEERATE.h"
|
||||
|
||||
void PAY_FEERATE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pid, h_amt, h_bps, h_fee, h_net;
|
||||
char h_st[4];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_pid = getl(b, T_ID1);
|
||||
EXEC SQL SELECT p.amount, p.status, m.mdr_bps INTO :h_amt, :h_st, :h_bps
|
||||
FROM py_payout p JOIN merchant m ON p.merchant_id = m.merchant_id
|
||||
WHERE p.payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
if (h_st[0] != 'R' && h_st[0] != 'A') {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "미지급 건만 요율 재적용 가능", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
h_fee = acq_fee(h_amt, h_bps);
|
||||
h_net = h_amt - h_fee;
|
||||
EXEC SQL UPDATE py_payout SET fee = :h_fee, net = :h_net, updated_at = now()
|
||||
WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_FEE, h_fee);
|
||||
setl(b, T_NET, h_net);
|
||||
setl(b, T_ARG1, h_bps);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_FEEWAIVE.h
Normal file
19
app/src/py/svc/PAY_FEEWAIVE.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_FEEWAIVE.h - py 서비스 PAY_FEEWAIVE 카피북 (copybook / record header).
|
||||
* Request fields: T_ID1(payout_id), T_REASON, T_BIZDATE
|
||||
* Response fields: T_RC(0 면제 / 1 수수료없음 / 2 상태불가), T_FEE(면제액), T_NET
|
||||
*/
|
||||
#ifndef PAY_FEEWAIVE_H
|
||||
#define PAY_FEEWAIVE_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long payout_id;
|
||||
long waived_fee;
|
||||
char waive_reason[96];
|
||||
} py_feewaive_rec_t;
|
||||
|
||||
void PAY_FEEWAIVE(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_FEEWAIVE_H */
|
||||
39
app/src/py/svc/PAY_FEEWAIVE.pgc
Normal file
39
app/src/py/svc/PAY_FEEWAIVE.pgc
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* PAY_FEEWAIVE.pgc - py 모듈 서비스 PAY_FEEWAIVE (one service = one file).
|
||||
* 수수료 면제: 미지급(R/A) 건의 수수료를 0 으로 하고 net 을 amount 로 복원.
|
||||
* 면제분은 py_adjust 에 양수 조정으로 증적을 남긴다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_FEEWAIVE.h"
|
||||
|
||||
void PAY_FEEWAIVE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char reason[96], bizdate[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pid, h_fee, h_amt;
|
||||
char h_st[4];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_pid = getl(b, T_ID1);
|
||||
gets_(b, T_REASON, reason, sizeof(reason));
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
if (reason[0] == 0) strcpy(reason, "수수료면제");
|
||||
|
||||
EXEC SQL SELECT fee, amount, status INTO :h_fee, :h_amt, :h_st
|
||||
FROM py_payout WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
if (h_fee <= 0) { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "면제할 수수료 없음", 0L); FAIL(b); }
|
||||
if (h_st[0] != 'R' && h_st[0] != 'A') { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "미지급 건만 면제 가능", 0L); FAIL(b); }
|
||||
|
||||
if (pydb_insert_adjust(h_pid, h_fee, reason, bizdate) < 0) FAIL(b);
|
||||
EXEC SQL UPDATE py_payout SET fee = 0, net = amount, updated_at = now()
|
||||
WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_FEE, h_fee);
|
||||
setl(b, T_NET, h_amt);
|
||||
userlog("PAY_FEEWAIVE id=%ld waived=%ld", h_pid, h_fee);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_FILEREBUILD.h
Normal file
19
app/src/py/svc/PAY_FILEREBUILD.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_FILEREBUILD.h - py 서비스 PAY_FILEREBUILD 카피북 (copybook / record header).
|
||||
* Request fields: T_ID2(file_id)
|
||||
* Response fields: T_RC(0 ok / 1 확정파일 거절), T_COUNT, T_GROSS, T_MSG
|
||||
*/
|
||||
#ifndef PAY_FILEREBUILD_H
|
||||
#define PAY_FILEREBUILD_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long file_id;
|
||||
char file_status[4];
|
||||
long rebuilt_count;
|
||||
} py_filerebuild_rec_t;
|
||||
|
||||
void PAY_FILEREBUILD(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_FILEREBUILD_H */
|
||||
36
app/src/py/svc/PAY_FILEREBUILD.pgc
Normal file
36
app/src/py/svc/PAY_FILEREBUILD.pgc
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* PAY_FILEREBUILD.pgc - py 모듈 서비스 PAY_FILEREBUILD (one service = one file).
|
||||
* 지급파일 재생성: 아직 미확정(G)인 파일 헤더를 실제 편성 상세 기준으로
|
||||
* 재집계해 갱신한다. 확정(C)된 파일은 재생성 불가로 거절.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_FILEREBUILD.h"
|
||||
|
||||
void PAY_FILEREBUILD(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_fid, h_cnt, h_sum;
|
||||
char h_st[4];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_fid = getl(b, T_ID2);
|
||||
EXEC SQL SELECT status INTO :h_st FROM py_file WHERE file_id = :h_fid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
if (h_st[0] == 'C') {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "확정 파일은 재생성 불가", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_sum
|
||||
FROM py_payout WHERE file_id = :h_fid AND status = 'F';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
EXEC SQL UPDATE py_file SET record_count = :h_cnt, total_amount = :h_sum
|
||||
WHERE file_id = :h_fid;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_sum);
|
||||
userlog("PAY_FILEREBUILD file=%ld recs=%ld amt=%ld", h_fid, h_cnt, h_sum);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_FILESTAT.h
Normal file
19
app/src/py/svc/PAY_FILESTAT.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_FILESTAT.h - py 서비스 PAY_FILESTAT 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE
|
||||
* Response fields: T_ARG1(생성 G), T_ARG2(확정 C), T_COUNT(총레코드), T_GROSS(총액)
|
||||
*/
|
||||
#ifndef PAY_FILESTAT_H
|
||||
#define PAY_FILESTAT_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char biz_date[16];
|
||||
long gen_files;
|
||||
long confirmed_files;
|
||||
} py_filestat_rec_t;
|
||||
|
||||
void PAY_FILESTAT(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_FILESTAT_H */
|
||||
32
app/src/py/svc/PAY_FILESTAT.pgc
Normal file
32
app/src/py/svc/PAY_FILESTAT.pgc
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* PAY_FILESTAT.pgc - py 모듈 서비스 PAY_FILESTAT (one service = one file).
|
||||
* 지급파일 현황: 당일 파일을 생성(G)/확정(C) 상태별로 세고 총 레코드수와
|
||||
* 총액을 한 번의 CASE 집계로 회신한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_FILESTAT.h"
|
||||
|
||||
void PAY_FILESTAT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16];
|
||||
long h_gen, h_conf, h_recs, h_amt;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL SELECT coalesce(sum(CASE WHEN status = 'G' THEN 1 ELSE 0 END),0),
|
||||
coalesce(sum(CASE WHEN status = 'C' THEN 1 ELSE 0 END),0),
|
||||
coalesce(sum(record_count),0),
|
||||
coalesce(sum(total_amount),0)
|
||||
INTO :h_gen, :h_conf, :h_recs, :h_amt
|
||||
FROM py_file WHERE biz_date = :h_bizdate;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_ARG1, h_gen);
|
||||
setl(b, T_ARG2, h_conf);
|
||||
setl(b, T_COUNT, h_recs);
|
||||
setl(b, T_GROSS, h_amt);
|
||||
setl(b, T_RC, 0);
|
||||
OK(b);
|
||||
}
|
||||
21
app/src/py/svc/PAY_FILEVERIFY.h
Normal file
21
app/src/py/svc/PAY_FILEVERIFY.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* PAY_FILEVERIFY.h - py 서비스 PAY_FILEVERIFY 카피북 (copybook / record header).
|
||||
* Request fields: T_ID2(file_id)
|
||||
* Response fields: T_RC(0 일치 / 1 불일치), T_ARG1/T_AMT1(헤더), T_COUNT/T_GROSS(상세), T_MSG
|
||||
*/
|
||||
#ifndef PAY_FILEVERIFY_H
|
||||
#define PAY_FILEVERIFY_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long file_id;
|
||||
long hdr_count;
|
||||
long hdr_amount;
|
||||
long dtl_count;
|
||||
long dtl_amount;
|
||||
} py_fileverify_rec_t;
|
||||
|
||||
void PAY_FILEVERIFY(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_FILEVERIFY_H */
|
||||
38
app/src/py/svc/PAY_FILEVERIFY.pgc
Normal file
38
app/src/py/svc/PAY_FILEVERIFY.pgc
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* PAY_FILEVERIFY.pgc - py 모듈 서비스 PAY_FILEVERIFY (one service = one file).
|
||||
* 지급파일 검증: py_file 헤더의 건수/총액과 실제 편성된 py_payout 상세 집계를
|
||||
* 대사해 일치 여부를 회신한다 (헤더-상세 정합 점검).
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_FILEVERIFY.h"
|
||||
|
||||
void PAY_FILEVERIFY(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_fid, h_fcnt, h_fsum, h_acnt, h_asum;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_fid = getl(b, T_ID2);
|
||||
EXEC SQL SELECT record_count, total_amount INTO :h_fcnt, :h_fsum
|
||||
FROM py_file WHERE file_id = :h_fid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_acnt, :h_asum
|
||||
FROM py_payout WHERE file_id = :h_fid;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_ARG1, h_fcnt);
|
||||
setl(b, T_AMT1, h_fsum);
|
||||
setl(b, T_COUNT, h_acnt);
|
||||
setl(b, T_GROSS, h_asum);
|
||||
if (h_fcnt == h_acnt && h_fsum == h_asum) {
|
||||
setl(b, T_RC, 0);
|
||||
Bchg(b, T_MSG, 0, "지급파일 헤더-상세 일치", 0L);
|
||||
} else {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "지급파일 헤더-상세 불일치", 0L);
|
||||
userlog("PAY_FILEVERIFY 불일치 file=%ld hdr=%ld/%ld dtl=%ld/%ld",
|
||||
h_fid, h_fcnt, h_fsum, h_acnt, h_asum);
|
||||
}
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_HOLDAGING.h
Normal file
19
app/src/py/svc/PAY_HOLDAGING.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_HOLDAGING.h - py 서비스 PAY_HOLDAGING 카피북 (copybook / record header).
|
||||
* Request fields: T_ARG1(경과일수, 기본 3)
|
||||
* Response fields: T_RC(0 정상 / 1 장기보류 존재), T_COUNT, T_GROSS, T_STR1(최고령 시각)
|
||||
*/
|
||||
#ifndef PAY_HOLDAGING_H
|
||||
#define PAY_HOLDAGING_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long aging_days;
|
||||
long aged_count;
|
||||
char oldest_ts[40];
|
||||
} py_holdaging_rec_t;
|
||||
|
||||
void PAY_HOLDAGING(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_HOLDAGING_H */
|
||||
33
app/src/py/svc/PAY_HOLDAGING.pgc
Normal file
33
app/src/py/svc/PAY_HOLDAGING.pgc
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* PAY_HOLDAGING.pgc - py 모듈 서비스 PAY_HOLDAGING (one service = one file).
|
||||
* 장기 보류 점검: 보류(H) 상태로 N일 이상 경과한 건수/금액과 가장 오래된
|
||||
* 보류 시각을 회신한다. 장기 보류가 있으면 경보(1).
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_HOLDAGING.h"
|
||||
|
||||
void PAY_HOLDAGING(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_days, h_cnt, h_sum;
|
||||
char h_oldest[40];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_days = getl(b, T_ARG1);
|
||||
if (h_days <= 0 || h_days > 365) h_days = 3;
|
||||
EXEC SQL SELECT count(*), coalesce(sum(net),0), coalesce(min(CAST(updated_at AS text)),'')
|
||||
INTO :h_cnt, :h_sum, :h_oldest
|
||||
FROM py_payout
|
||||
WHERE status = 'H' AND updated_at < now() - :h_days * interval '1 day';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, h_cnt > 0 ? 1 : 0);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_sum);
|
||||
setl(b, T_ARG1, h_days);
|
||||
Bchg(b, T_STR1, 0, h_oldest, 0L);
|
||||
if (h_cnt > 0)
|
||||
userlog("PAY_HOLDAGING 장기보류 경보 %ld일+ cnt=%ld amt=%ld", h_days, h_cnt, h_sum);
|
||||
OK(b);
|
||||
}
|
||||
21
app/src/py/svc/PAY_HOLDLIST.h
Normal file
21
app/src/py/svc/PAY_HOLDLIST.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* PAY_HOLDLIST.h - py 서비스 PAY_HOLDLIST 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE
|
||||
* Response fields: T_COUNT(사유 그룹수), T_GROSS(보류 총건수),
|
||||
* occurrence: T_STR1[i](사유), T_ARG1[i](건수), T_AMT1[i](금액)
|
||||
*/
|
||||
#ifndef PAY_HOLDLIST_H
|
||||
#define PAY_HOLDLIST_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char biz_date[16];
|
||||
char reason[128];
|
||||
long reason_count;
|
||||
long reason_amount;
|
||||
} py_holdlist_rec_t;
|
||||
|
||||
void PAY_HOLDLIST(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_HOLDLIST_H */
|
||||
44
app/src/py/svc/PAY_HOLDLIST.pgc
Normal file
44
app/src/py/svc/PAY_HOLDLIST.pgc
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* PAY_HOLDLIST.pgc - py 모듈 서비스 PAY_HOLDLIST (one service = one file).
|
||||
* 보류 사유별 집계: 당일 보류(H)건을 사유별로 커서 집계해 상위 10개 사유를
|
||||
* UBF occurrence(T_STR1[i]/T_ARG1[i]/T_AMT1[i]) 로 회신한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_HOLDLIST.h"
|
||||
|
||||
void PAY_HOLDLIST(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
int i = 0;
|
||||
long total = 0;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16], h_reason[128];
|
||||
long h_cnt, h_sum;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL DECLARE cur_holdlist CURSOR FOR
|
||||
SELECT coalesce(nullif(reject_reason,''),'(사유없음)'), count(*), coalesce(sum(net),0)
|
||||
FROM py_payout
|
||||
WHERE biz_date = :h_bizdate AND status = 'H'
|
||||
GROUP BY 1 ORDER BY 2 DESC, 1;
|
||||
EXEC SQL OPEN cur_holdlist;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
for (;;) {
|
||||
EXEC SQL FETCH cur_holdlist INTO :h_reason, :h_cnt, :h_sum;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_holdlist; FAIL(b); }
|
||||
total += h_cnt;
|
||||
if (i < 10) {
|
||||
Bchg(b, T_STR1, i, h_reason, 0L);
|
||||
Bchg(b, T_ARG1, i, (char *)&h_cnt, 0L);
|
||||
Bchg(b, T_AMT1, i, (char *)&h_sum, 0L);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
EXEC SQL CLOSE cur_holdlist;
|
||||
setl(b, T_COUNT, (long)i);
|
||||
setl(b, T_GROSS, total);
|
||||
setl(b, T_RC, 0);
|
||||
OK(b);
|
||||
}
|
||||
18
app/src/py/svc/PAY_HOLDREASON.h
Normal file
18
app/src/py/svc/PAY_HOLDREASON.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* PAY_HOLDREASON.h - py 서비스 PAY_HOLDREASON 카피북 (copybook / record header).
|
||||
* Request fields: T_ID1(payout_id), T_REASON(보류 사유)
|
||||
* Response fields: T_RC(0 보류 / 1 상태불가 / 2 사유누락), T_STATUS, T_MSG
|
||||
*/
|
||||
#ifndef PAY_HOLDREASON_H
|
||||
#define PAY_HOLDREASON_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long payout_id;
|
||||
char hold_reason[128];
|
||||
} py_holdreason_rec_t;
|
||||
|
||||
void PAY_HOLDREASON(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_HOLDREASON_H */
|
||||
33
app/src/py/svc/PAY_HOLDREASON.pgc
Normal file
33
app/src/py/svc/PAY_HOLDREASON.pgc
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* PAY_HOLDREASON.pgc - py 모듈 서비스 PAY_HOLDREASON (one service = one file).
|
||||
* 사유부 지급보류: 요청(R)/승인(A) 건을 보류(H)로 전이하면서 보류 사유를
|
||||
* reject_reason 에 기록한다. 사유 없는 보류는 거절.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_HOLDREASON.h"
|
||||
|
||||
void PAY_HOLDREASON(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char st[4];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pid;
|
||||
char h_reason[128];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_pid = getl(b, T_ID1);
|
||||
gets_(b, T_REASON, h_reason, sizeof(h_reason));
|
||||
if (h_reason[0] == 0) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "보류 사유는 필수", 0L); FAIL(b); }
|
||||
if (pydb_get_status(h_pid, st) < 0) FAIL(b);
|
||||
if (st[0] != 'R' && st[0] != 'A') {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "요청/승인 상태만 보류 가능", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
EXEC SQL UPDATE py_payout SET status = 'H', reject_reason = :h_reason, updated_at = now()
|
||||
WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_RC, 0);
|
||||
Bchg(b, T_STATUS, 0, "H", 0L);
|
||||
OK(b);
|
||||
}
|
||||
21
app/src/py/svc/PAY_LEDGERSUM.h
Normal file
21
app/src/py/svc/PAY_LEDGERSUM.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* PAY_LEDGERSUM.h - py 서비스 PAY_LEDGERSUM 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE
|
||||
* Response fields: T_COUNT(유형수), T_GROSS(순합계), occurrence: T_STR1[i](유형),
|
||||
* T_ARG1[i](건수), T_AMT1[i](합계), T_AMT2[i](최소), T_AMT3[i](최대)
|
||||
*/
|
||||
#ifndef PAY_LEDGERSUM_H
|
||||
#define PAY_LEDGERSUM_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char biz_date[16];
|
||||
char entry_type[16];
|
||||
long type_count;
|
||||
long type_sum;
|
||||
} py_ledgersum_rec_t;
|
||||
|
||||
void PAY_LEDGERSUM(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_LEDGERSUM_H */
|
||||
46
app/src/py/svc/PAY_LEDGERSUM.pgc
Normal file
46
app/src/py/svc/PAY_LEDGERSUM.pgc
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* PAY_LEDGERSUM.pgc - py 모듈 서비스 PAY_LEDGERSUM (one service = one file).
|
||||
* 지급원장 유형별 합계: 당일 py_ledger 를 기표유형별 커서 집계해
|
||||
* 유형/건수/합계/최소/최대를 occurrence 로 회신한다 (원장 마감 검산용).
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_LEDGERSUM.h"
|
||||
|
||||
void PAY_LEDGERSUM(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
int i = 0;
|
||||
long net_total = 0;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16], h_type[16];
|
||||
long h_cnt, h_sum, h_min, h_max;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL DECLARE cur_ledsum CURSOR FOR
|
||||
SELECT entry_type, count(*), coalesce(sum(amount),0),
|
||||
coalesce(min(amount),0), coalesce(max(amount),0)
|
||||
FROM py_ledger WHERE biz_date = :h_bizdate
|
||||
GROUP BY entry_type ORDER BY entry_type;
|
||||
EXEC SQL OPEN cur_ledsum;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
for (;;) {
|
||||
EXEC SQL FETCH cur_ledsum INTO :h_type, :h_cnt, :h_sum, :h_min, :h_max;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_ledsum; FAIL(b); }
|
||||
net_total += h_sum;
|
||||
if (i < 10) {
|
||||
Bchg(b, T_STR1, i, h_type, 0L);
|
||||
Bchg(b, T_ARG1, i, (char *)&h_cnt, 0L);
|
||||
Bchg(b, T_AMT1, i, (char *)&h_sum, 0L);
|
||||
Bchg(b, T_AMT2, i, (char *)&h_min, 0L);
|
||||
Bchg(b, T_AMT3, i, (char *)&h_max, 0L);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
EXEC SQL CLOSE cur_ledsum;
|
||||
setl(b, T_COUNT, (long)i);
|
||||
setl(b, T_GROSS, net_total);
|
||||
setl(b, T_RC, 0);
|
||||
OK(b);
|
||||
}
|
||||
20
app/src/py/svc/PAY_LIMITCHK.h
Normal file
20
app/src/py/svc/PAY_LIMITCHK.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* PAY_LIMITCHK.h - py 서비스 PAY_LIMITCHK 카피북 (copybook / record header).
|
||||
* Request fields: T_MERCHANT, T_BIZDATE
|
||||
* Response fields: T_RC(0 이내 / 1 초과), T_AMT1(한도), T_AMT2(사용), T_AMT3(잔여)
|
||||
*/
|
||||
#ifndef PAY_LIMITCHK_H
|
||||
#define PAY_LIMITCHK_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char merchant_id[64];
|
||||
long daily_limit;
|
||||
long used_amount;
|
||||
long remain_amount;
|
||||
} py_limitchk_rec_t;
|
||||
|
||||
void PAY_LIMITCHK(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_LIMITCHK_H */
|
||||
39
app/src/py/svc/PAY_LIMITCHK.pgc
Normal file
39
app/src/py/svc/PAY_LIMITCHK.pgc
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* PAY_LIMITCHK.pgc - py 모듈 서비스 PAY_LIMITCHK (one service = one file).
|
||||
* 지급한도 점검: 가맹점 일한도(merchant.daily_limit) 대비 당일 확정성
|
||||
* 지급(A/F/P) 사용액과 잔여 한도를 회신, 초과 시 경보(1).
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_LIMITCHK.h"
|
||||
|
||||
void PAY_LIMITCHK(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_bizdate[16];
|
||||
long h_lim, h_used;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL SELECT daily_limit INTO :h_lim FROM merchant WHERE merchant_id = :h_merch;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
EXEC SQL SELECT coalesce(sum(net),0) INTO :h_used
|
||||
FROM py_payout
|
||||
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate
|
||||
AND status IN ('A','F','P');
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_AMT1, h_lim);
|
||||
setl(b, T_AMT2, h_used);
|
||||
setl(b, T_AMT3, h_lim - h_used);
|
||||
if (h_used > h_lim) {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "일 지급한도 초과", 0L);
|
||||
userlog("PAY_LIMITCHK 한도초과 merch=%s used=%ld limit=%ld", h_merch, h_used, h_lim);
|
||||
} else {
|
||||
setl(b, T_RC, 0);
|
||||
Bchg(b, T_MSG, 0, "한도 이내", 0L);
|
||||
}
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_LIMITHOLD.h
Normal file
19
app/src/py/svc/PAY_LIMITHOLD.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_LIMITHOLD.h - py 서비스 PAY_LIMITHOLD 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE
|
||||
* Response fields: T_RC(0 초과없음 / 1 보류 수행), T_ARG1(초과 가맹점 수), T_COUNT(보류 건수)
|
||||
*/
|
||||
#ifndef PAY_LIMITHOLD_H
|
||||
#define PAY_LIMITHOLD_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char biz_date[16];
|
||||
long over_merchants;
|
||||
long held_count;
|
||||
} py_limithold_rec_t;
|
||||
|
||||
void PAY_LIMITHOLD(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_LIMITHOLD_H */
|
||||
43
app/src/py/svc/PAY_LIMITHOLD.pgc
Normal file
43
app/src/py/svc/PAY_LIMITHOLD.pgc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* PAY_LIMITHOLD.pgc - py 모듈 서비스 PAY_LIMITHOLD (one service = one file).
|
||||
* 한도 초과 자동 보류: 당일 사용액이 일한도를 넘은 가맹점을 HAVING 커서로
|
||||
* 뽑아 각 가맹점의 잔여 요청(R)건을 '한도초과' 사유로 보류(H)시킨다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_LIMITHOLD.h"
|
||||
|
||||
void PAY_LIMITHOLD(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long merchants = 0, held = 0;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16], h_merch[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL DECLARE cur_limhold CURSOR FOR
|
||||
SELECT m.merchant_id
|
||||
FROM py_payout p JOIN merchant m ON p.merchant_id = m.merchant_id
|
||||
WHERE p.biz_date = :h_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 cur_limhold;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
for (;;) {
|
||||
EXEC SQL FETCH cur_limhold INTO :h_merch;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_limhold; FAIL(b); }
|
||||
merchants++;
|
||||
EXEC SQL UPDATE py_payout SET status = 'H', reject_reason = '한도초과', updated_at = now()
|
||||
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'R';
|
||||
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_limhold; FAIL(b); }
|
||||
held += sqlca.sqlerrd[2];
|
||||
}
|
||||
EXEC SQL CLOSE cur_limhold;
|
||||
|
||||
setl(b, T_RC, merchants > 0 ? 1 : 0);
|
||||
setl(b, T_ARG1, merchants);
|
||||
setl(b, T_COUNT, held);
|
||||
userlog("PAY_LIMITHOLD date=%s over=%ld held=%ld", h_bizdate, merchants, held);
|
||||
OK(b);
|
||||
}
|
||||
20
app/src/py/svc/PAY_MERGE.h
Normal file
20
app/src/py/svc/PAY_MERGE.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* PAY_MERGE.h - py 서비스 PAY_MERGE 카피북 (copybook / record header).
|
||||
* Request fields: T_MERCHANT, T_BIZDATE
|
||||
* Response fields: T_RC(0 합산 / 1 대상부족), T_ID2(신규 BULK id), T_COUNT(통합 건수), T_NET
|
||||
*/
|
||||
#ifndef PAY_MERGE_H
|
||||
#define PAY_MERGE_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char merchant_id[64];
|
||||
long merged_count;
|
||||
long merged_net;
|
||||
long new_payout_id;
|
||||
} py_merge_rec_t;
|
||||
|
||||
void PAY_MERGE(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_MERGE_H */
|
||||
47
app/src/py/svc/PAY_MERGE.pgc
Normal file
47
app/src/py/svc/PAY_MERGE.pgc
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* PAY_MERGE.pgc - py 모듈 서비스 PAY_MERGE (one service = one file).
|
||||
* 지급 합산: 가맹점 당일 요청(R) STD 건이 2건 이상이면 전부 취소(C) 표시 후
|
||||
* 합산 1건(BULK)으로 재생성한다. 이체 수수료 절감용 통합.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_MERGE.h"
|
||||
|
||||
void PAY_MERGE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long new_id;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_merch[64], h_bizdate[16], h_acct[32];
|
||||
long h_cnt, h_amt, h_fee, h_net;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(sum(fee),0),
|
||||
coalesce(sum(net),0), coalesce(min(account_no),'')
|
||||
INTO :h_cnt, :h_amt, :h_fee, :h_net, :h_acct
|
||||
FROM py_payout
|
||||
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate
|
||||
AND status = 'R' AND channel = 'STD';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (h_cnt < 2) {
|
||||
setl(b, T_RC, 1);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
Bchg(b, T_MSG, 0, "합산 대상 2건 미만", 0L);
|
||||
OK(b);
|
||||
}
|
||||
EXEC SQL UPDATE py_payout SET status = 'C', reject_reason = '합산통합', updated_at = now()
|
||||
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate
|
||||
AND status = 'R' AND channel = 'STD';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
new_id = pydb_next_payout_id();
|
||||
if (new_id < 0) FAIL(b);
|
||||
if (pydb_insert_payout(new_id, h_merch, h_acct, h_amt, h_fee, h_net, "R", "BULK", h_bizdate) < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_ID2, new_id);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_NET, h_net);
|
||||
userlog("PAY_MERGE merch=%s merged=%ld -> id=%ld net=%ld", h_merch, h_cnt, new_id, h_net);
|
||||
OK(b);
|
||||
}
|
||||
20
app/src/py/svc/PAY_NETRECALC.h
Normal file
20
app/src/py/svc/PAY_NETRECALC.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* PAY_NETRECALC.h - py 서비스 PAY_NETRECALC 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE, T_ARG1(1=자동보정)
|
||||
* Response fields: T_RC(0 정합 / 1 불일치), T_COUNT(불일치 건수), T_DELTA(누적 오차), T_ARG2(보정 건수)
|
||||
*/
|
||||
#ifndef PAY_NETRECALC_H
|
||||
#define PAY_NETRECALC_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char biz_date[16];
|
||||
long bad_count;
|
||||
long drift_sum;
|
||||
long fixed_count;
|
||||
} py_netrecalc_rec_t;
|
||||
|
||||
void PAY_NETRECALC(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_NETRECALC_H */
|
||||
40
app/src/py/svc/PAY_NETRECALC.pgc
Normal file
40
app/src/py/svc/PAY_NETRECALC.pgc
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* PAY_NETRECALC.pgc - py 모듈 서비스 PAY_NETRECALC (one service = one file).
|
||||
* 순지급액 정합 검증: 당일 건 중 amount - fee != net 인 불일치 건수와
|
||||
* 누적 오차를 산출하고, 보정 플래그(T_ARG1=1) 시 net 을 재계산해 고친다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_NETRECALC.h"
|
||||
|
||||
void PAY_NETRECALC(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long fix;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16];
|
||||
long h_bad, h_drift, h_fixed;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
fix = getl(b, T_ARG1);
|
||||
h_fixed = 0;
|
||||
|
||||
EXEC SQL SELECT count(*), coalesce(sum(amount - fee - net),0)
|
||||
INTO :h_bad, :h_drift
|
||||
FROM py_payout WHERE biz_date = :h_bizdate AND amount - fee <> net;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (fix == 1 && h_bad > 0) {
|
||||
EXEC SQL UPDATE py_payout SET net = amount - fee, updated_at = now()
|
||||
WHERE biz_date = :h_bizdate AND amount - fee <> net;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
h_fixed = sqlca.sqlerrd[2];
|
||||
}
|
||||
|
||||
setl(b, T_RC, h_bad > 0 ? 1 : 0);
|
||||
setl(b, T_COUNT, h_bad);
|
||||
setl(b, T_DELTA, h_drift);
|
||||
setl(b, T_ARG2, h_fixed);
|
||||
if (h_bad > 0)
|
||||
userlog("PAY_NETRECALC date=%s bad=%ld drift=%ld fixed=%ld", h_bizdate, h_bad, h_drift, h_fixed);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_NOTIFYEMAIL.h
Normal file
19
app/src/py/svc/PAY_NOTIFYEMAIL.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_NOTIFYEMAIL.h - py 서비스 PAY_NOTIFYEMAIL 카피북 (copybook / record header).
|
||||
* Request fields: T_MERCHANT, T_BIZDATE
|
||||
* Response fields: T_RC(0 발송 / 1 대상없음), T_COUNT, T_GROSS, T_STR1(제목), T_MSG(본문)
|
||||
*/
|
||||
#ifndef PAY_NOTIFYEMAIL_H
|
||||
#define PAY_NOTIFYEMAIL_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char merchant_id[64];
|
||||
char subject[128];
|
||||
char body[192];
|
||||
} py_notifyemail_rec_t;
|
||||
|
||||
void PAY_NOTIFYEMAIL(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_NOTIFYEMAIL_H */
|
||||
38
app/src/py/svc/PAY_NOTIFYEMAIL.pgc
Normal file
38
app/src/py/svc/PAY_NOTIFYEMAIL.pgc
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* PAY_NOTIFYEMAIL.pgc - py 모듈 서비스 PAY_NOTIFYEMAIL (one service = one file).
|
||||
* 지급내역 이메일 통지(시뮬): 가맹점의 당일 지급완료(P) 건수/합계를 집계해
|
||||
* 메일 제목과 본문 요약을 조립한다. 대상이 없으면 미발송(1) 회신.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_NOTIFYEMAIL.h"
|
||||
|
||||
void PAY_NOTIFYEMAIL(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char subject[128], body[192];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_cnt, h_sum;
|
||||
char h_merch[64], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_sum
|
||||
FROM py_payout
|
||||
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'P';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (h_cnt == 0) {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "이메일 통지 대상 없음", 0L);
|
||||
OK(b);
|
||||
}
|
||||
snprintf(subject, sizeof(subject), "[Forge] %s 지급내역 안내 (%s)", h_merch, h_bizdate);
|
||||
snprintf(body, sizeof(body), "당일 %ld건, 합계 %ld원이 지급 완료되었습니다.", h_cnt, h_sum);
|
||||
userlog("PAY_NOTIFYEMAIL 발송시뮬 merch=%s cnt=%ld amt=%ld", h_merch, h_cnt, h_sum);
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_sum);
|
||||
Bchg(b, T_STR1, 0, subject, 0L);
|
||||
Bchg(b, T_MSG, 0, body, 0L);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_NOTIFYPLAN.h
Normal file
19
app/src/py/svc/PAY_NOTIFYPLAN.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_NOTIFYPLAN.h - py 서비스 PAY_NOTIFYPLAN 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE(예약 실행일)
|
||||
* Response fields: T_RC(0 예정있음 / 1 없음), T_COUNT, T_GROSS, T_MSG(통보 문안)
|
||||
*/
|
||||
#ifndef PAY_NOTIFYPLAN_H
|
||||
#define PAY_NOTIFYPLAN_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char run_date[16];
|
||||
long plan_count;
|
||||
long plan_amount;
|
||||
} py_notifyplan_rec_t;
|
||||
|
||||
void PAY_NOTIFYPLAN(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_NOTIFYPLAN_H */
|
||||
29
app/src/py/svc/PAY_NOTIFYPLAN.pgc
Normal file
29
app/src/py/svc/PAY_NOTIFYPLAN.pgc
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* PAY_NOTIFYPLAN.pgc - py 모듈 서비스 PAY_NOTIFYPLAN (one service = one file).
|
||||
* 지급 예정 통보: 지정 실행일의 예약(S) 건을 py_payout 과 조인 집계해
|
||||
* 지급 예정 건수/총액과 통보 문안을 회신한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_NOTIFYPLAN.h"
|
||||
|
||||
void PAY_NOTIFYPLAN(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char msg[160];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_rundate[16];
|
||||
long h_cnt, h_sum;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_rundate, sizeof(h_rundate));
|
||||
EXEC SQL SELECT count(*), coalesce(sum(p.net),0) INTO :h_cnt, :h_sum
|
||||
FROM py_schedule s JOIN py_payout p ON s.payout_id = p.payout_id
|
||||
WHERE s.run_date = :h_rundate AND s.status = 'S';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
snprintf(msg, sizeof(msg), "%s 지급 예정 %ld건 / 총 %ld원", h_rundate, h_cnt, h_sum);
|
||||
setl(b, T_RC, h_cnt > 0 ? 0 : 1);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_sum);
|
||||
Bchg(b, T_MSG, 0, msg, 0L);
|
||||
OK(b);
|
||||
}
|
||||
18
app/src/py/svc/PAY_NOTIFYSMS.h
Normal file
18
app/src/py/svc/PAY_NOTIFYSMS.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* PAY_NOTIFYSMS.h - py 서비스 PAY_NOTIFYSMS 카피북 (copybook / record header).
|
||||
* Request fields: T_ID1(payout_id)
|
||||
* Response fields: T_RC(0 발송 / 1 대상아님), T_NET, T_MSG(문자 본문)
|
||||
*/
|
||||
#ifndef PAY_NOTIFYSMS_H
|
||||
#define PAY_NOTIFYSMS_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long payout_id;
|
||||
char sms_body[192];
|
||||
} py_notifysms_rec_t;
|
||||
|
||||
void PAY_NOTIFYSMS(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_NOTIFYSMS_H */
|
||||
33
app/src/py/svc/PAY_NOTIFYSMS.pgc
Normal file
33
app/src/py/svc/PAY_NOTIFYSMS.pgc
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* PAY_NOTIFYSMS.pgc - py 모듈 서비스 PAY_NOTIFYSMS (one service = one file).
|
||||
* 지급완료 SMS 통지(시뮬): 지급완료(P)건만 대상. 가맹점/금액을 조회해
|
||||
* 문자 본문을 조립하고 발송은 userlog 로 시뮬레이션한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_NOTIFYSMS.h"
|
||||
|
||||
void PAY_NOTIFYSMS(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char msg[192];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pid, h_net;
|
||||
char h_merch[64], h_st[4];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_pid = getl(b, T_ID1);
|
||||
EXEC SQL SELECT merchant_id, net, status INTO :h_merch, :h_net, :h_st
|
||||
FROM py_payout WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
if (h_st[0] != 'P') {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "지급완료 건만 SMS 통지 가능", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
snprintf(msg, sizeof(msg), "[Forge지급] %s님, %ld원 지급이 완료되었습니다.", h_merch, h_net);
|
||||
userlog("PAY_NOTIFYSMS 발송시뮬 payout=%ld to=%s", h_pid, h_merch);
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_NET, h_net);
|
||||
Bchg(b, T_MSG, 0, msg, 0L);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_REFUND.h
Normal file
19
app/src/py/svc/PAY_REFUND.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_REFUND.h - py 서비스 PAY_REFUND 카피북 (copybook / record header).
|
||||
* Request fields: T_ID1(payout_id), T_REASON(환입 사유)
|
||||
* Response fields: T_RC(0 환입 / 1 상태불가), T_NET(환입액), T_STATUS('V')
|
||||
*/
|
||||
#ifndef PAY_REFUND_H
|
||||
#define PAY_REFUND_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long payout_id;
|
||||
long refund_net;
|
||||
char refund_reason[96];
|
||||
} py_refund_rec_t;
|
||||
|
||||
void PAY_REFUND(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_REFUND_H */
|
||||
46
app/src/py/svc/PAY_REFUND.pgc
Normal file
46
app/src/py/svc/PAY_REFUND.pgc
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* PAY_REFUND.pgc - py 모듈 서비스 PAY_REFUND (one service = one file).
|
||||
* 지급 환입: 지급완료(P) 건을 역분개(V)하며 지급계좌에 net 을 복원하고
|
||||
* py_ledger 에 음수 REFUND 기표를 남긴다 (취소/환입 원장 일관성).
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_REFUND.h"
|
||||
|
||||
void PAY_REFUND(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char reason[96];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pid, h_net, h_neg;
|
||||
char h_acct[32], h_st[4], h_bizdate[16], h_rsn[96];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_pid = getl(b, T_ID1);
|
||||
gets_(b, T_REASON, reason, sizeof(reason));
|
||||
if (reason[0] == 0) strcpy(reason, "지급환입");
|
||||
strncpy(h_rsn, reason, sizeof(h_rsn)-1); h_rsn[sizeof(h_rsn)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT account_no, net, status, CAST(biz_date AS text)
|
||||
INTO :h_acct, :h_net, :h_st, :h_bizdate
|
||||
FROM py_payout WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
if (h_st[0] != 'P') {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "지급완료 건만 환입 가능", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
if (pydb_account_add(h_acct, h_net) < 0) FAIL(b);
|
||||
EXEC SQL UPDATE py_payout SET status = 'V', reject_reason = :h_rsn, updated_at = now()
|
||||
WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
h_neg = -h_net;
|
||||
EXEC SQL INSERT INTO py_ledger (ledger_id, payout_id, entry_type, amount, biz_date)
|
||||
VALUES (nextval('py_ledger_seq'), :h_pid, 'REFUND', :h_neg, :h_bizdate);
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_NET, h_net);
|
||||
Bchg(b, T_STATUS, 0, "V", 0L);
|
||||
userlog("PAY_REFUND id=%ld net=%ld acct=%s", h_pid, h_net, h_acct);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_REPAYNEW.h
Normal file
19
app/src/py/svc/PAY_REPAYNEW.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_REPAYNEW.h - py 서비스 PAY_REPAYNEW 카피북 (copybook / record header).
|
||||
* Request fields: T_ID1(반려 payout_id)
|
||||
* Response fields: T_RC(0 재지급 / 1 반려아님 / 2 계좌없음), T_ID2(신규 id), T_KEY1(새 계좌)
|
||||
*/
|
||||
#ifndef PAY_REPAYNEW_H
|
||||
#define PAY_REPAYNEW_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long old_payout_id;
|
||||
long new_payout_id;
|
||||
char new_account[32];
|
||||
} py_repaynew_rec_t;
|
||||
|
||||
void PAY_REPAYNEW(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_REPAYNEW_H */
|
||||
46
app/src/py/svc/PAY_REPAYNEW.pgc
Normal file
46
app/src/py/svc/PAY_REPAYNEW.pgc
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* PAY_REPAYNEW.pgc - py 모듈 서비스 PAY_REPAYNEW (one service = one file).
|
||||
* 계좌 변경 재지급: 반려(J)된 지급건을 가맹점의 최신 정상(ACTIVE) 계좌로
|
||||
* 재지급 신규건(R) 생성 후 원건은 역분개(V) 처리한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_REPAYNEW.h"
|
||||
|
||||
void PAY_REPAYNEW(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long new_id;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pid, h_amt, h_fee, h_net;
|
||||
char h_merch[64], h_st[4], h_bizdate[16], h_newacct[32];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_pid = getl(b, T_ID1);
|
||||
EXEC SQL SELECT merchant_id, amount, fee, net, status, CAST(biz_date AS text)
|
||||
INTO :h_merch, :h_amt, :h_fee, :h_net, :h_st, :h_bizdate
|
||||
FROM py_payout WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
if (h_st[0] != 'J') {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "반려 건만 재지급 가능", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
EXEC SQL SELECT account_no INTO :h_newacct FROM py_account
|
||||
WHERE merchant_id = :h_merch AND status = 'ACTIVE'
|
||||
ORDER BY updated_at DESC LIMIT 1;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (sqlca.sqlcode == 100) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "재지급 가능한 정상 계좌 없음", 0L); FAIL(b); }
|
||||
|
||||
new_id = pydb_next_payout_id();
|
||||
if (new_id < 0) FAIL(b);
|
||||
if (pydb_insert_payout(new_id, h_merch, h_newacct, h_amt, h_fee, h_net, "R", "STD", h_bizdate) < 0) FAIL(b);
|
||||
EXEC SQL UPDATE py_payout SET status = 'V', reject_reason = '재지급 전환', updated_at = now()
|
||||
WHERE payout_id = :h_pid;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_ID2, new_id);
|
||||
Bchg(b, T_KEY1, 0, h_newacct, 0L);
|
||||
userlog("PAY_REPAYNEW old=%ld new=%ld acct=%s", h_pid, new_id, h_newacct);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_SCHEDCANCEL.h
Normal file
19
app/src/py/svc/PAY_SCHEDCANCEL.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_SCHEDCANCEL.h - py 서비스 PAY_SCHEDCANCEL 카피북 (copybook / record header).
|
||||
* Request fields: T_ID1(schedule_id)
|
||||
* Response fields: T_RC(0 취소 / 1 실행됨), T_ID2(연결 payout_id), T_MSG
|
||||
*/
|
||||
#ifndef PAY_SCHEDCANCEL_H
|
||||
#define PAY_SCHEDCANCEL_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
long schedule_id;
|
||||
long payout_id;
|
||||
char sched_status[4];
|
||||
} py_schedcancel_rec_t;
|
||||
|
||||
void PAY_SCHEDCANCEL(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_SCHEDCANCEL_H */
|
||||
34
app/src/py/svc/PAY_SCHEDCANCEL.pgc
Normal file
34
app/src/py/svc/PAY_SCHEDCANCEL.pgc
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* PAY_SCHEDCANCEL.pgc - py 모듈 서비스 PAY_SCHEDCANCEL (one service = one file).
|
||||
* 지급 예약 취소: 아직 미실행(S)인 예약만 DELETE 로 제거한다. 이미
|
||||
* 실행(D)된 예약은 취소 불가로 거절하고 연결 지급건 id 를 회신.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_SCHEDCANCEL.h"
|
||||
|
||||
void PAY_SCHEDCANCEL(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_sid, h_pid;
|
||||
char h_st[4];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
h_sid = getl(b, T_ID1);
|
||||
EXEC SQL SELECT payout_id, status INTO :h_pid, :h_st
|
||||
FROM py_schedule WHERE schedule_id = :h_sid;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
if (h_st[0] != 'S') {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "이미 실행된 예약은 취소 불가", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
EXEC SQL DELETE FROM py_schedule WHERE schedule_id = :h_sid AND status = 'S';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_ID2, h_pid);
|
||||
Bchg(b, T_MSG, 0, "지급 예약 취소 완료", 0L);
|
||||
userlog("PAY_SCHEDCANCEL sched=%ld payout=%ld", h_sid, h_pid);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_SEIZE.h
Normal file
19
app/src/py/svc/PAY_SEIZE.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_SEIZE.h - py 서비스 PAY_SEIZE 카피북 (copybook / record header).
|
||||
* Request fields: T_KEY1(계좌번호), T_REASON(압류 사유, 기본 '법원명령')
|
||||
* Response fields: T_RC(0 압류 / 1 불가), T_COUNT(보류 전환 건수), T_STR1(기록 사유)
|
||||
*/
|
||||
#ifndef PAY_SEIZE_H
|
||||
#define PAY_SEIZE_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char account_no[32];
|
||||
char seize_reason[96];
|
||||
long held_count;
|
||||
} py_seize_rec_t;
|
||||
|
||||
void PAY_SEIZE(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_SEIZE_H */
|
||||
41
app/src/py/svc/PAY_SEIZE.pgc
Normal file
41
app/src/py/svc/PAY_SEIZE.pgc
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* PAY_SEIZE.pgc - py 모듈 서비스 PAY_SEIZE (one service = one file).
|
||||
* 압류/가압류 등록: 대상 계좌를 SEIZED 로 전환하고 그 계좌의 미지급(R/A)
|
||||
* 건을 '압류:<사유>' 로 일괄 보류(H)한다. 이미 압류면 거절.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_SEIZE.h"
|
||||
|
||||
void PAY_SEIZE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char reason[96];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_acct[32], h_rsn[128];
|
||||
long h_held;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_KEY1, h_acct, sizeof(h_acct));
|
||||
gets_(b, T_REASON, reason, sizeof(reason));
|
||||
if (reason[0] == 0) strcpy(reason, "법원명령");
|
||||
|
||||
EXEC SQL UPDATE py_account SET status = 'SEIZED', updated_at = now()
|
||||
WHERE account_no = :h_acct AND status <> 'SEIZED';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (sqlca.sqlerrd[2] == 0) {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "이미 압류되었거나 없는 계좌", 0L);
|
||||
FAIL(b);
|
||||
}
|
||||
snprintf(h_rsn, sizeof(h_rsn), "압류:%s", reason);
|
||||
EXEC SQL UPDATE py_payout SET status = 'H', reject_reason = :h_rsn, updated_at = now()
|
||||
WHERE account_no = :h_acct AND status IN ('R','A');
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
h_held = sqlca.sqlerrd[2];
|
||||
|
||||
setl(b, T_RC, 0);
|
||||
setl(b, T_COUNT, h_held);
|
||||
Bchg(b, T_STR1, 0, h_rsn, 0L);
|
||||
userlog("PAY_SEIZE acct=%s held=%ld reason=%s", h_acct, h_held, h_rsn);
|
||||
OK(b);
|
||||
}
|
||||
19
app/src/py/svc/PAY_SEIZECHK.h
Normal file
19
app/src/py/svc/PAY_SEIZECHK.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* PAY_SEIZECHK.h - py 서비스 PAY_SEIZECHK 카피북 (copybook / record header).
|
||||
* Request fields: T_BIZDATE
|
||||
* Response fields: T_RC(0 없음 / 1 차단대상 존재), T_COUNT, T_GROSS, T_MSG
|
||||
*/
|
||||
#ifndef PAY_SEIZECHK_H
|
||||
#define PAY_SEIZECHK_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char biz_date[16];
|
||||
long blocked_count;
|
||||
long blocked_amount;
|
||||
} py_seizechk_rec_t;
|
||||
|
||||
void PAY_SEIZECHK(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_SEIZECHK_H */
|
||||
34
app/src/py/svc/PAY_SEIZECHK.pgc
Normal file
34
app/src/py/svc/PAY_SEIZECHK.pgc
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* PAY_SEIZECHK.pgc - py 모듈 서비스 PAY_SEIZECHK (one service = one file).
|
||||
* 압류계좌 지급 차단 점검: 당일 미지급(R/A)건 중 압류(SEIZED) 계좌로
|
||||
* 나가려는 건수/금액을 조인 집계해 차단 필요 여부를 경보한다.
|
||||
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr.
|
||||
*/
|
||||
#include "PAY_SEIZECHK.h"
|
||||
|
||||
void PAY_SEIZECHK(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16];
|
||||
long h_cnt, h_sum;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
|
||||
EXEC SQL SELECT count(*), coalesce(sum(p.net),0) INTO :h_cnt, :h_sum
|
||||
FROM py_payout p JOIN py_account a ON p.account_no = a.account_no
|
||||
WHERE p.biz_date = :h_bizdate AND p.status IN ('R','A') AND a.status = 'SEIZED';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_sum);
|
||||
if (h_cnt > 0) {
|
||||
setl(b, T_RC, 1);
|
||||
Bchg(b, T_MSG, 0, "압류계좌 대상 지급건 존재 - 차단 필요", 0L);
|
||||
userlog("PAY_SEIZECHK 차단대상 cnt=%ld amt=%ld", h_cnt, h_sum);
|
||||
} else {
|
||||
setl(b, T_RC, 0);
|
||||
Bchg(b, T_MSG, 0, "압류계좌 지급 시도 없음", 0L);
|
||||
}
|
||||
OK(b);
|
||||
}
|
||||
18
app/src/py/svc/PAY_SEIZEREL.h
Normal file
18
app/src/py/svc/PAY_SEIZEREL.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* PAY_SEIZEREL.h - py 서비스 PAY_SEIZEREL 카피북 (copybook / record header).
|
||||
* Request fields: T_KEY1(계좌번호)
|
||||
* Response fields: T_RC(0 해제 / 1 압류상태 아님), T_COUNT(복원 건수), T_STATUS
|
||||
*/
|
||||
#ifndef PAY_SEIZEREL_H
|
||||
#define PAY_SEIZEREL_H
|
||||
|
||||
#include "py_svc.h"
|
||||
|
||||
typedef struct {
|
||||
char account_no[32];
|
||||
long released_count;
|
||||
} py_seizerel_rec_t;
|
||||
|
||||
void PAY_SEIZEREL(TPSVCINFO *p);
|
||||
|
||||
#endif /* PAY_SEIZEREL_H */
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue